@clypra/engine 1.1.0 → 1.1.2

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/index.js CHANGED
@@ -115,7 +115,7 @@ function layoutWithFontSize(ctx, cfg, fontSize, lines) {
115
115
  ctx.font = fontStr;
116
116
  const lineWidths = lines.map((line) => measureLine(ctx, line, letterSpacing));
117
117
  const maxLineWidth = Math.max(...lineWidths, 1);
118
- const textBlockHeight = fontSize + (lines.length - 1) * fontSize * lineHeight;
118
+ const textBlockHeight = lines.length === 1 ? fontSize : (lines.length - 1) * fontSize * lineHeight + fontSize;
119
119
  let align = "center";
120
120
  let startX = cWidth / 2;
121
121
  if (cfg.textPosX === "left") {
@@ -376,9 +376,8 @@ function createCanvas(width, height) {
376
376
  }
377
377
  function releaseCanvas(canvas) {
378
378
  if (canvas instanceof OffscreenCanvas) return;
379
- if (typeof document !== "undefined" && canvas.parentNode) {
380
- canvas.parentNode.removeChild(canvas);
381
- }
379
+ canvas.width = 0;
380
+ canvas.height = 0;
382
381
  }
383
382
  function _resetPlatformCache() {
384
383
  _ctxFilter = null;
@@ -387,6 +386,46 @@ function _resetPlatformCache() {
387
386
  _offscreenCanvas = null;
388
387
  _webgl2 = null;
389
388
  }
389
+ var CanvasDevice = class {
390
+ static canvases = [];
391
+ static maxPoolSize = 10;
392
+ /**
393
+ * Acquire a Canvas context from the pool or create a new one.
394
+ * If a canvas is pulled from the pool, it is resized to the target dimensions.
395
+ */
396
+ static acquire(width, height) {
397
+ let canvas;
398
+ if (this.canvases.length > 0) {
399
+ canvas = this.canvases.pop();
400
+ if (canvas.width !== width || canvas.height !== height) {
401
+ canvas.width = width;
402
+ canvas.height = height;
403
+ }
404
+ } else {
405
+ canvas = createCanvas(width, height);
406
+ }
407
+ return canvas;
408
+ }
409
+ /**
410
+ * Release a canvas back to the pool, or free its resources immediately if pool is full.
411
+ */
412
+ static release(canvas) {
413
+ if (this.canvases.length < this.maxPoolSize) {
414
+ this.canvases.push(canvas);
415
+ } else {
416
+ releaseCanvas(canvas);
417
+ }
418
+ }
419
+ /**
420
+ * Disposes all pooled canvases to release GPU/memory backing stores.
421
+ */
422
+ static clearPool() {
423
+ while (this.canvases.length > 0) {
424
+ const c = this.canvases.pop();
425
+ releaseCanvas(c);
426
+ }
427
+ }
428
+ };
390
429
 
391
430
  // src/engine/procedural/utils.ts
392
431
  function getCanvas2DContext(canvas) {
@@ -1905,7 +1944,7 @@ function renderTextEffectCore(ctx, cfg) {
1905
1944
  ctx.globalCompositeOperation = "source-atop";
1906
1945
  const renderCount = Math.max(1, Math.min(20, layer2.strength ?? 1));
1907
1946
  for (let i = 0; i < renderCount; i++) {
1908
- renderWithShadowTrick("fill", layer2.color, layer2.blur, 0, 0, layer2.opacity, "transparent", layer2.spread ?? 0);
1947
+ renderWithShadowTrick("fill", layer2.color, layer2.blur, 0, 0, layer2.opacity, "#000000", layer2.spread ?? 0);
1909
1948
  }
1910
1949
  ctx.restore();
1911
1950
  }
@@ -1913,7 +1952,7 @@ function renderTextEffectCore(ctx, cfg) {
1913
1952
  if (shadowEnabled && shadowType === "inner" && shadowOpacity > 0) {
1914
1953
  ctx.save();
1915
1954
  ctx.globalCompositeOperation = "source-atop";
1916
- renderWithShadowTrick("fill", shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY, shadowOpacity, "transparent");
1955
+ renderWithShadowTrick("fill", shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY, shadowOpacity, "#000000");
1917
1956
  ctx.restore();
1918
1957
  }
1919
1958
  if (isGlitch) {
@@ -2346,12 +2385,148 @@ async function initializeFontSystem() {
2346
2385
  }
2347
2386
  }
2348
2387
  function checkFontVariant(variantName) {
2349
- const canvas = document.createElement("canvas");
2350
- const ctx = canvas.getContext("2d");
2351
- if (!ctx) return false;
2352
- ctx.font = `16px "${variantName}"`;
2353
- const metrics = ctx.measureText("Test");
2354
- return metrics.width > 0;
2388
+ if (typeof document === "undefined" || !document.fonts) return false;
2389
+ return document.fonts.check(`16px "${variantName}"`);
2390
+ }
2391
+ var FontLoader = class {
2392
+ state = {
2393
+ loading: /* @__PURE__ */ new Set(),
2394
+ loaded: /* @__PURE__ */ new Set(),
2395
+ failed: /* @__PURE__ */ new Map(),
2396
+ promises: /* @__PURE__ */ new Map()
2397
+ };
2398
+ async ensureFont(descriptor) {
2399
+ const key = this.getFontKey(descriptor);
2400
+ if (this.state.loaded.has(key)) {
2401
+ return {
2402
+ font: descriptor,
2403
+ loaded: true,
2404
+ loadTimeMs: 0
2405
+ };
2406
+ }
2407
+ if (this.state.failed.has(key)) {
2408
+ return {
2409
+ font: descriptor,
2410
+ loaded: false,
2411
+ error: this.state.failed.get(key),
2412
+ loadTimeMs: 0
2413
+ };
2414
+ }
2415
+ if (this.state.promises.has(key)) {
2416
+ return this.state.promises.get(key);
2417
+ }
2418
+ const promise = this.loadFont(descriptor);
2419
+ this.state.promises.set(key, promise);
2420
+ return promise;
2421
+ }
2422
+ async ensureFonts(descriptors) {
2423
+ return Promise.all(descriptors.map((desc) => this.ensureFont(desc)));
2424
+ }
2425
+ async waitForFontsReady() {
2426
+ if (typeof document === "undefined" || !document.fonts) {
2427
+ return;
2428
+ }
2429
+ await document.fonts.ready;
2430
+ }
2431
+ isLoaded(descriptor) {
2432
+ const key = this.getFontKey(descriptor);
2433
+ return this.state.loaded.has(key);
2434
+ }
2435
+ getStats() {
2436
+ return {
2437
+ loaded: this.state.loaded.size,
2438
+ loading: this.state.loading.size,
2439
+ failed: this.state.failed.size
2440
+ };
2441
+ }
2442
+ clear() {
2443
+ this.state.loading.clear();
2444
+ this.state.loaded.clear();
2445
+ this.state.failed.clear();
2446
+ this.state.promises.clear();
2447
+ }
2448
+ async loadFont(descriptor) {
2449
+ const key = this.getFontKey(descriptor);
2450
+ const startTime = performance.now();
2451
+ this.state.loading.add(key);
2452
+ try {
2453
+ if (typeof document === "undefined" || !document.fonts) {
2454
+ throw new Error("Font API not available");
2455
+ }
2456
+ const weight = this.normalizeFontWeight(descriptor.weight);
2457
+ const style = descriptor.style || "normal";
2458
+ const fontFace = `${style} ${weight} 16px "${descriptor.family}"`;
2459
+ if (document.fonts.check(fontFace)) {
2460
+ this.state.loaded.add(key);
2461
+ this.state.loading.delete(key);
2462
+ this.state.promises.delete(key);
2463
+ return {
2464
+ font: descriptor,
2465
+ loaded: true,
2466
+ loadTimeMs: performance.now() - startTime
2467
+ };
2468
+ }
2469
+ await document.fonts.load(fontFace);
2470
+ if (!document.fonts.check(fontFace)) {
2471
+ throw new Error(`Font "${descriptor.family}" failed to load`);
2472
+ }
2473
+ this.state.loaded.add(key);
2474
+ this.state.loading.delete(key);
2475
+ this.state.promises.delete(key);
2476
+ return {
2477
+ font: descriptor,
2478
+ loaded: true,
2479
+ loadTimeMs: performance.now() - startTime
2480
+ };
2481
+ } catch (error) {
2482
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2483
+ this.state.failed.set(key, errorMessage);
2484
+ this.state.loading.delete(key);
2485
+ this.state.promises.delete(key);
2486
+ return {
2487
+ font: descriptor,
2488
+ loaded: false,
2489
+ error: errorMessage,
2490
+ loadTimeMs: performance.now() - startTime
2491
+ };
2492
+ }
2493
+ }
2494
+ getFontKey(descriptor) {
2495
+ const weight = this.normalizeFontWeight(descriptor.weight);
2496
+ const style = descriptor.style || "normal";
2497
+ return `${descriptor.family}|${weight}|${style}`;
2498
+ }
2499
+ normalizeFontWeight(weight) {
2500
+ if (typeof weight === "number") {
2501
+ return weight;
2502
+ }
2503
+ if (!weight) return 400;
2504
+ const asNum = parseInt(weight, 10);
2505
+ if (!isNaN(asNum) && asNum >= 100 && asNum <= 900) {
2506
+ return asNum;
2507
+ }
2508
+ const weightMap = {
2509
+ normal: 400,
2510
+ bold: 700,
2511
+ lighter: 300,
2512
+ bolder: 700
2513
+ };
2514
+ return weightMap[weight] ?? 400;
2515
+ }
2516
+ };
2517
+ var globalFontLoader = null;
2518
+ function getFontLoader() {
2519
+ if (!globalFontLoader) {
2520
+ globalFontLoader = new FontLoader();
2521
+ }
2522
+ return globalFontLoader;
2523
+ }
2524
+ function resetFontLoader() {
2525
+ globalFontLoader = null;
2526
+ }
2527
+ async function ensureFontsLoaded(descriptors) {
2528
+ const loader = getFontLoader();
2529
+ return loader.ensureFonts(descriptors);
2355
2530
  }
2356
2531
 
2357
2532
  // src/engine/timelineDefaults.ts
@@ -2724,6 +2899,212 @@ function mergeSceneIntoConfig(doc, base) {
2724
2899
  const out = sceneToConfig({ ...doc, legacyConfig: base });
2725
2900
  return out;
2726
2901
  }
2902
+ function resolveFontFamilyName(fontFamily) {
2903
+ const f = fontFamily?.toLowerCase() || "";
2904
+ if (f.includes("inter")) return "Inter Variable";
2905
+ if (f.includes("montserrat")) return "Montserrat Variable";
2906
+ if (f.includes("geist")) return "Geist Variable";
2907
+ if (f.includes("space grotesk") || f.includes("grotesk")) return "Space Grotesk Variable";
2908
+ if (f.includes("outfit")) return "Outfit Variable";
2909
+ if (f.includes("roboto variable")) return "Roboto Variable";
2910
+ if (f.includes("roboto condensed")) return "Roboto Condensed";
2911
+ if (f === "roboto") return "Roboto Variable";
2912
+ if (f.includes("open sans")) return "Open Sans Variable";
2913
+ if (f.includes("raleway")) return "Raleway Variable";
2914
+ if (f.includes("oswald")) return "Oswald Variable";
2915
+ if (f.includes("playfair display")) return "Playfair Display Variable";
2916
+ if (f.includes("nunito")) return "Nunito Variable";
2917
+ if (f.includes("dancing script")) return "Dancing Script Variable";
2918
+ if (f === "lato") return "Lato";
2919
+ if (f === "anton") return "Anton";
2920
+ if (f === "bebas neue") return "Bebas Neue";
2921
+ if (f === "poppins") return "Poppins";
2922
+ if (f === "permanent marker") return "Permanent Marker";
2923
+ if (f === "bangers") return "Bangers";
2924
+ if (f === "press start 2p") return "Press Start 2P";
2925
+ if (f === "pacifico") return "Pacifico";
2926
+ return fontFamily;
2927
+ }
2928
+ function _buildConfig(effect, text, fontSize, canvasWidth, canvasHeight, time, clipStartTime, clipDuration) {
2929
+ const fill = effect.fills?.[0];
2930
+ const stroke = effect.strokes?.[0];
2931
+ const shadow = effect.shadows?.[0];
2932
+ const bevel = effect.bevel;
2933
+ const panel = effect.panel;
2934
+ const ratio = fontSize / 100;
2935
+ const config = {
2936
+ // Canvas / text
2937
+ width: canvasWidth,
2938
+ height: canvasHeight,
2939
+ canvasWidth,
2940
+ canvasHeight,
2941
+ text,
2942
+ time: time ?? 0,
2943
+ clipStartTime: clipStartTime ?? 0,
2944
+ clipDuration: clipDuration ?? 5,
2945
+ // Font
2946
+ fontFamily: resolveFontFamilyName(effect.font.family),
2947
+ fontWeight: effect.font.weight,
2948
+ fontStyle: effect.font.style,
2949
+ fontSize,
2950
+ letterSpacing: effect.font.letterSpacing,
2951
+ lineHeight: effect.font.lineHeight
2952
+ };
2953
+ if (effect.animation) {
2954
+ config.animation = effect.animation;
2955
+ }
2956
+ if (fill) {
2957
+ if (fill.type !== void 0) config.fillType = fill.type;
2958
+ if (fill.color !== void 0) config.fillColor = fill.color;
2959
+ if (fill.gradient?.angle !== void 0) config.fillGradientAngle = fill.gradient.angle;
2960
+ if (fill.gradient?.stops !== void 0) config.fillGradientStops = fill.gradient.stops;
2961
+ if (fill.patternType !== void 0) config.patternType = fill.patternType;
2962
+ if (fill.perCharFillEnabled !== void 0) config.perCharFillEnabled = fill.perCharFillEnabled;
2963
+ if (fill.charFillColors !== void 0) config.charFillColors = fill.charFillColors;
2964
+ } else {
2965
+ config.fillType = "none";
2966
+ }
2967
+ config.strokeEnabled = !!stroke;
2968
+ if (stroke) {
2969
+ if (stroke.color !== void 0) config.strokeColor = stroke.color;
2970
+ if (stroke.width !== void 0) config.strokeWidth = stroke.width * ratio;
2971
+ if (stroke.position !== void 0) config.strokePosition = stroke.position;
2972
+ if (stroke.opacity !== void 0) config.strokeOpacity = stroke.opacity;
2973
+ if (stroke.lineJoin !== void 0) config.strokeLineJoin = stroke.lineJoin;
2974
+ if (stroke.blur !== void 0) config.strokeBlur = stroke.blur * ratio;
2975
+ if (stroke.type !== void 0) config.strokeType = stroke.type;
2976
+ if (stroke.colorSecondary !== void 0) config.strokeColorSecondary = stroke.colorSecondary;
2977
+ if (stroke.widthSecondary !== void 0) config.strokeWidthSecondary = stroke.widthSecondary * ratio;
2978
+ if (stroke.fadeRange !== void 0) config.strokeFadeRange = stroke.fadeRange;
2979
+ }
2980
+ config.shadowEnabled = !!shadow;
2981
+ if (shadow) {
2982
+ if (shadow.color !== void 0) config.shadowColor = shadow.color;
2983
+ if (shadow.blur !== void 0) config.shadowBlur = shadow.blur * ratio;
2984
+ if (shadow.offsetX !== void 0) config.shadowOffsetX = shadow.offsetX * ratio;
2985
+ if (shadow.offsetY !== void 0) config.shadowOffsetY = shadow.offsetY * ratio;
2986
+ if (shadow.opacity !== void 0) config.shadowOpacity = shadow.opacity;
2987
+ if (shadow.type !== void 0) config.shadowType = shadow.type;
2988
+ }
2989
+ config.bevelEnabled = !!bevel;
2990
+ if (bevel) {
2991
+ if (bevel.depth !== void 0) config.bevelDepth = Math.round(bevel.depth * ratio);
2992
+ if (bevel.highlightColor !== void 0) config.bevelHighlight = bevel.highlightColor;
2993
+ if (bevel.shadowColor !== void 0) config.bevelShadow = bevel.shadowColor;
2994
+ if (bevel.direction !== void 0) config.bevelDirection = bevel.direction;
2995
+ if (bevel.coreColor !== void 0) config.bevelCoreColor = bevel.coreColor;
2996
+ if (bevel.edgeColor !== void 0) config.bevelEdgeColor = bevel.edgeColor;
2997
+ if (bevel.edgeWidth !== void 0) config.bevelEdgeWidth = bevel.edgeWidth * ratio;
2998
+ if (bevel.blur !== void 0) config.bevelBlur = bevel.blur * ratio;
2999
+ if (bevel.blurColor !== void 0) config.bevelBlurColor = bevel.blurColor;
3000
+ if (bevel.perspectiveEnabled !== void 0) config.bevelPerspectiveEnabled = bevel.perspectiveEnabled;
3001
+ if (bevel.vanishingPointX !== void 0) config.bevelVanishingPointX = bevel.vanishingPointX;
3002
+ if (bevel.vanishingPointY !== void 0) config.bevelVanishingPointY = bevel.vanishingPointY;
3003
+ if (bevel.focalLength !== void 0) config.bevelFocalLength = bevel.focalLength;
3004
+ }
3005
+ if (effect.stack) {
3006
+ config.stackEnabled = !!effect.stack.count;
3007
+ if (effect.stack.count !== void 0) config.stackCount = effect.stack.count;
3008
+ if (effect.stack.offsetX !== void 0) config.stackOffsetX = effect.stack.offsetX * ratio;
3009
+ if (effect.stack.offsetY !== void 0) config.stackOffsetY = effect.stack.offsetY * ratio;
3010
+ if (effect.stack.opacityDecay !== void 0) config.stackOpacityDecay = effect.stack.opacityDecay;
3011
+ if (effect.stack.color1 !== void 0) config.stackColor1 = effect.stack.color1;
3012
+ if (effect.stack.color2 !== void 0) config.stackColor2 = effect.stack.color2;
3013
+ if (effect.stack.color3 !== void 0) config.stackColor3 = effect.stack.color3;
3014
+ if (effect.stack.color4 !== void 0) config.stackColor4 = effect.stack.color4;
3015
+ }
3016
+ config.panelEnabled = !!panel;
3017
+ if (panel) {
3018
+ if (panel.color !== void 0) config.panelColor = panel.color;
3019
+ if (panel.opacity !== void 0) config.panelOpacity = panel.opacity;
3020
+ if (panel.radius !== void 0) config.panelRadius = panel.radius;
3021
+ if (panel.paddingX !== void 0) config.panelPaddingX = panel.paddingX * ratio;
3022
+ if (panel.paddingY !== void 0) config.panelPaddingY = panel.paddingY * ratio;
3023
+ if (panel.stroke !== void 0) {
3024
+ config.panelStrokeEnabled = !!panel.stroke;
3025
+ if (panel.stroke.color !== void 0) config.panelStrokeColor = panel.stroke.color;
3026
+ if (panel.stroke.width !== void 0) config.panelStrokeWidth = panel.stroke.width * ratio;
3027
+ }
3028
+ }
3029
+ if (effect.glows) {
3030
+ config.glowLayers = effect.glows.map((g) => {
3031
+ const mappedGlow = {
3032
+ enabled: true,
3033
+ color: g.color,
3034
+ blur: typeof g.blur === "number" ? g.blur * ratio : g.blur ?? 0,
3035
+ opacity: g.opacity,
3036
+ type: g.type ?? "outer"
3037
+ };
3038
+ if (g.strength !== void 0) mappedGlow.strength = g.strength;
3039
+ if (g.spread !== void 0) mappedGlow.spread = g.spread * ratio;
3040
+ return mappedGlow;
3041
+ });
3042
+ }
3043
+ const standardKeys = /* @__PURE__ */ new Set([
3044
+ "id",
3045
+ "name",
3046
+ "category",
3047
+ "description",
3048
+ "tags",
3049
+ "font",
3050
+ "fills",
3051
+ "strokes",
3052
+ "shadows",
3053
+ "glows",
3054
+ "bevel",
3055
+ "panel",
3056
+ "text",
3057
+ "animation",
3058
+ "stack"
3059
+ ]);
3060
+ for (const key of Object.keys(effect)) {
3061
+ if (!standardKeys.has(key)) {
3062
+ config[key] = effect[key];
3063
+ }
3064
+ }
3065
+ return config;
3066
+ }
3067
+ function layerToTextEffectConfig(layer2) {
3068
+ const normWeight = typeof layer2.fontWeight === "number" ? layer2.fontWeight : layer2.fontWeight === "bold" ? 700 : 400;
3069
+ const config = {
3070
+ ...defaultConfig,
3071
+ width: layer2.width,
3072
+ height: layer2.height,
3073
+ canvasWidth: layer2.width,
3074
+ canvasHeight: layer2.height,
3075
+ text: layer2.text,
3076
+ fontFamily: resolveFontFamilyName(layer2.fontFamily),
3077
+ fontWeight: normWeight,
3078
+ fontStyle: layer2.fontStyle || "normal",
3079
+ fontSize: layer2.fontSize,
3080
+ letterSpacing: layer2.letterSpacing ?? 0,
3081
+ lineHeight: layer2.lineHeight ?? 1.2,
3082
+ fillType: layer2.color ? "solid" : "none",
3083
+ fillColor: layer2.color ?? "#FFFFFF",
3084
+ strokeEnabled: !!layer2.stroke,
3085
+ strokeColor: layer2.stroke?.color ?? "#000000",
3086
+ strokeWidth: layer2.stroke?.width ?? 0,
3087
+ strokePosition: "center",
3088
+ strokeOpacity: 100,
3089
+ strokeLineJoin: "round",
3090
+ shadowEnabled: !!layer2.shadow,
3091
+ shadowColor: layer2.shadow?.color ?? "#000000",
3092
+ shadowBlur: layer2.shadow?.blur ?? 0,
3093
+ shadowOffsetX: layer2.shadow?.offsetX ?? 0,
3094
+ shadowOffsetY: layer2.shadow?.offsetY ?? 0,
3095
+ shadowOpacity: 100,
3096
+ shadowType: "drop",
3097
+ panelEnabled: !!layer2.background,
3098
+ panelColor: layer2.background?.color ?? "#1E1E26",
3099
+ panelOpacity: 80,
3100
+ panelRadius: layer2.background?.borderRadius ?? 6,
3101
+ panelPaddingX: layer2.background?.padding ?? 12,
3102
+ panelPaddingY: layer2.background?.padding ?? 12,
3103
+ textPosX: layer2.textAlign || "center",
3104
+ textPosY: layer2.verticalAlign === "middle" ? "middle" : layer2.verticalAlign || "middle"
3105
+ };
3106
+ return config;
3107
+ }
2727
3108
 
2728
3109
  // src/engine/animation.ts
2729
3110
  function ease(t, kind = "linear") {
@@ -3009,6 +3390,10 @@ function getCompositor() {
3009
3390
  }
3010
3391
  return sharedCompositor;
3011
3392
  }
3393
+ function disposeSharedCompositor() {
3394
+ sharedCompositor?.dispose();
3395
+ sharedCompositor = null;
3396
+ }
3012
3397
  function evaluateScene(doc, time, ctx, options = {}) {
3013
3398
  const animated = applyTimelineAtTime(doc, time);
3014
3399
  const cfg = sceneToConfig(animated);
@@ -3029,47 +3414,23 @@ function evaluateScene(doc, time, ctx, options = {}) {
3029
3414
  finishFrame();
3030
3415
  return;
3031
3416
  }
3032
- if (supportsOffscreenCanvas()) {
3033
- const off = new OffscreenCanvas(w, h);
3034
- const offCtx = off.getContext("2d");
3035
- if (!offCtx) {
3036
- renderTextEffectCore(ctx, cfg);
3037
- finishFrame();
3038
- return;
3039
- }
3040
- renderTextEffectCore(offCtx, cfg);
3041
- applyMaskReveal(offCtx, animated, w, h);
3417
+ const temp = CanvasDevice.acquire(w, h);
3418
+ const tctx = temp.getContext("2d");
3419
+ if (tctx) {
3420
+ tctx.clearRect(0, 0, w, h);
3421
+ renderTextEffectCore(tctx, cfg);
3422
+ applyMaskReveal(tctx, animated, w, h);
3042
3423
  const compositor = options.compositor ?? getCompositor();
3043
3424
  if (compositor?.isSupported) {
3044
- compositor.renderToContext(ctx, off, comp);
3045
- return;
3046
- }
3047
- ctx.clearRect(0, 0, w, h);
3048
- ctx.drawImage(off, 0, 0);
3049
- return;
3050
- }
3051
- if (typeof document !== "undefined") {
3052
- const temp = document.createElement("canvas");
3053
- temp.width = w;
3054
- temp.height = h;
3055
- const tctx = temp.getContext("2d");
3056
- if (tctx) {
3057
- renderTextEffectCore(tctx, cfg);
3058
- applyMaskReveal(tctx, animated, w, h);
3059
- const compositor = options.compositor ?? getCompositor();
3060
- if (compositor?.isSupported) {
3061
- compositor.renderToContext(ctx, temp, comp);
3062
- temp.width = 0;
3063
- temp.height = 0;
3064
- return;
3065
- }
3425
+ compositor.renderToContext(ctx, temp, comp);
3426
+ } else {
3066
3427
  ctx.clearRect(0, 0, w, h);
3067
3428
  ctx.drawImage(temp, 0, 0);
3068
- temp.width = 0;
3069
- temp.height = 0;
3070
- return;
3071
3429
  }
3430
+ CanvasDevice.release(temp);
3431
+ return;
3072
3432
  }
3433
+ CanvasDevice.release(temp);
3073
3434
  renderTextEffectCore(ctx, cfg);
3074
3435
  finishFrame();
3075
3436
  }
@@ -6036,6 +6397,7 @@ function duplicateTrackAtPlayhead(doc, trackIndex, previewTime) {
6036
6397
  export {
6037
6398
  COMPOSITION_PRESETS,
6038
6399
  CUSTOM_ENGINE_IDS,
6400
+ CanvasDevice,
6039
6401
  DEFAULT_CANVAS_HEIGHT,
6040
6402
  DEFAULT_CANVAS_WIDTH,
6041
6403
  DEFAULT_DURATION,
@@ -6047,6 +6409,7 @@ export {
6047
6409
  ENTRANCE_PRESETS,
6048
6410
  EXIT_PRESETS,
6049
6411
  FONT_WEIGHT_OPTIONS,
6412
+ FontLoader,
6050
6413
  InkBrushEngine,
6051
6414
  LEGACY_RENDERER_MAP,
6052
6415
  LOOP_PRESETS,
@@ -6058,6 +6421,7 @@ export {
6058
6421
  TextEffectRenderer,
6059
6422
  WEBM_EXPORT_MAX_FRAMES,
6060
6423
  WebGLCompositor,
6424
+ _buildConfig,
6061
6425
  _resetPlatformCache,
6062
6426
  addImageLayer,
6063
6427
  addKeyframeAtTime,
@@ -6102,6 +6466,7 @@ export {
6102
6466
  createPulseOpacityTrack,
6103
6467
  defaultConfig,
6104
6468
  deleteKeyframe,
6469
+ disposeSharedCompositor,
6105
6470
  downloadDotLottie,
6106
6471
  downloadLottieJson,
6107
6472
  downloadPngSequenceZip,
@@ -6114,6 +6479,7 @@ export {
6114
6479
  encodeGif,
6115
6480
  ensureDefaultTimeline,
6116
6481
  ensureFontInLottie,
6482
+ ensureFontsLoaded,
6117
6483
  evaluateConfig,
6118
6484
  evaluateScene,
6119
6485
  findTrackIndex,
@@ -6121,6 +6487,7 @@ export {
6121
6487
  getAnimatableParamDef,
6122
6488
  getAnimatableParamsForLayer,
6123
6489
  getDefaultText,
6490
+ getFontLoader,
6124
6491
  getLayerById,
6125
6492
  getPresetScene,
6126
6493
  getSceneTime,
@@ -6139,6 +6506,7 @@ export {
6139
6506
  injectText,
6140
6507
  injectTextStyle,
6141
6508
  isWebMExportSupported,
6509
+ layerToTextEffectConfig,
6142
6510
  loadLottieFonts,
6143
6511
  lottieColorToHex,
6144
6512
  lottieJToAlign,
@@ -6161,10 +6529,12 @@ export {
6161
6529
  renderPngSequence,
6162
6530
  renderSceneWebM,
6163
6531
  renderTextEffectCore,
6532
+ resetFontLoader,
6164
6533
  resetSceneTime,
6165
6534
  resizeCharFillColors,
6166
6535
  resolveAnimatedScalar,
6167
6536
  resolveCustomEngineId,
6537
+ resolveFontFamilyName,
6168
6538
  restoreLetterSpacing,
6169
6539
  scanLottieFonts,
6170
6540
  scanTextLayers,