@pibbl/core 0.0.3-alpha.85.1 → 0.0.3-alpha.90.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/features/viz/index.d.ts +13 -0
- package/dist/features/viz/index.js +57 -0
- package/dist/features/viz/index.js.map +2 -2
- package/dist/features/viz/lib/graph/bipartite.d.ts +14 -0
- package/dist/features/viz/lib/graph/bipartite.js +250 -0
- package/dist/features/viz/lib/graph/bipartite.js.map +7 -0
- package/dist/features/viz/lib/graph/chord.d.ts +44 -0
- package/dist/features/viz/lib/graph/chord.js +348 -0
- package/dist/features/viz/lib/graph/chord.js.map +7 -0
- package/dist/features/viz/lib/graph/dependency.d.ts +26 -0
- package/dist/features/viz/lib/graph/dependency.js +141 -0
- package/dist/features/viz/lib/graph/dependency.js.map +7 -0
- package/dist/features/viz/lib/graph/force.d.ts +28 -0
- package/dist/features/viz/lib/graph/force.js +580 -0
- package/dist/features/viz/lib/graph/force.js.map +7 -0
- package/dist/features/viz/lib/graph/hierarchy.d.ts +21 -0
- package/dist/features/viz/lib/graph/hierarchy.js +112 -0
- package/dist/features/viz/lib/graph/hierarchy.js.map +7 -0
- package/dist/features/viz/lib/graph/icicle.d.ts +16 -0
- package/dist/features/viz/lib/graph/icicle.js +153 -0
- package/dist/features/viz/lib/graph/icicle.js.map +7 -0
- package/dist/features/viz/lib/graph/matrix.d.ts +30 -0
- package/dist/features/viz/lib/graph/matrix.js +243 -0
- package/dist/features/viz/lib/graph/matrix.js.map +7 -0
- package/dist/features/viz/lib/graph/neighborhood.d.ts +11 -0
- package/dist/features/viz/lib/graph/neighborhood.js +281 -0
- package/dist/features/viz/lib/graph/neighborhood.js.map +7 -0
- package/dist/features/viz/lib/graph/routes.d.ts +35 -0
- package/dist/features/viz/lib/graph/routes.js +102 -0
- package/dist/features/viz/lib/graph/routes.js.map +7 -0
- package/dist/features/viz/lib/graph/sankey.d.ts +44 -0
- package/dist/features/viz/lib/graph/sankey.js +341 -0
- package/dist/features/viz/lib/graph/sankey.js.map +7 -0
- package/dist/features/viz/lib/graph/shared.d.ts +59 -0
- package/dist/features/viz/lib/graph/shared.js +296 -0
- package/dist/features/viz/lib/graph/shared.js.map +7 -0
- package/dist/features/viz/lib/graph/tree.d.ts +11 -0
- package/dist/features/viz/lib/graph/tree.js +232 -0
- package/dist/features/viz/lib/graph/tree.js.map +7 -0
- package/dist/features/viz/lib/graph/treemap.d.ts +20 -0
- package/dist/features/viz/lib/graph/treemap.js +227 -0
- package/dist/features/viz/lib/graph/treemap.js.map +7 -0
- package/dist/features/viz/lib/graph/types.d.ts +1097 -0
- package/dist/filters/backdrop-contrast-gpu.d.ts +5 -0
- package/dist/filters/backdrop-contrast-gpu.js +33 -0
- package/dist/filters/backdrop-contrast-gpu.js.map +7 -0
- package/dist/filters/backdrop-contrast.d.ts +21 -0
- package/dist/filters/backdrop-contrast.js +61 -0
- package/dist/filters/backdrop-contrast.js.map +7 -0
- package/dist/filters/filter-executor.d.ts +11 -4
- package/dist/filters/filter-executor.js +104 -49
- package/dist/filters/filter-executor.js.map +2 -2
- package/dist/filters/render-backdrop-contrast-boundary.d.ts +31 -0
- package/dist/filters/render-backdrop-contrast-boundary.js +116 -0
- package/dist/filters/render-backdrop-contrast-boundary.js.map +7 -0
- package/dist/filters.d.ts +9 -7
- package/dist/filters.js +2 -0
- package/dist/filters.js.map +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ReflectionGpu, type ReflectionGpuCallbacks } from "./reflection-gpu.js";
|
|
2
|
+
/** Shares the existing private GPU resource/context-loss implementation. */
|
|
3
|
+
export declare class BackdropContrastGpu extends ReflectionGpu {
|
|
4
|
+
constructor(callbacks?: Readonly<ReflectionGpuCallbacks>);
|
|
5
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReflectionGpu
|
|
3
|
+
} from "./reflection-gpu.js";
|
|
4
|
+
const fragment = `#version 300 es
|
|
5
|
+
precision highp float;
|
|
6
|
+
uniform sampler2D scene, map, lightMap;
|
|
7
|
+
in vec2 uv;
|
|
8
|
+
out vec4 outputColor;
|
|
9
|
+
vec3 linearRgb(vec3 c) {
|
|
10
|
+
return mix(c/12.92,pow((c+.055)/1.055,vec3(2.4)),step(vec3(.04045),c));
|
|
11
|
+
}
|
|
12
|
+
void main() {
|
|
13
|
+
float coverage=texture(scene,uv).a;
|
|
14
|
+
vec4 backdrop=texture(map,uv);
|
|
15
|
+
// A transparent current target is evaluated over white; DOM pixels are unavailable.
|
|
16
|
+
vec3 background=mix(vec3(1),backdrop.rgb,backdrop.a);
|
|
17
|
+
float luminance=dot(linearRgb(background),vec3(.2126,.7152,.0722));
|
|
18
|
+
// Equal black/white contrast occurs at sqrt(.05*1.05)-.05.
|
|
19
|
+
int choice=luminance<.179128784747792?0:1;
|
|
20
|
+
vec4 color=texelFetch(lightMap,ivec2(choice,0),0);
|
|
21
|
+
float alpha=coverage*color.a;
|
|
22
|
+
outputColor=vec4(color.rgb*alpha,alpha);
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
class BackdropContrastGpu extends ReflectionGpu {
|
|
26
|
+
constructor(callbacks = {}) {
|
|
27
|
+
super(callbacks, fragment);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
BackdropContrastGpu
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=backdrop-contrast-gpu.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/filters/backdrop-contrast-gpu.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n ReflectionGpu,\n type ReflectionGpuCallbacks,\n} from \"./reflection-gpu.js\";\n\nconst fragment = `#version 300 es\nprecision highp float;\nuniform sampler2D scene, map, lightMap;\nin vec2 uv;\nout vec4 outputColor;\nvec3 linearRgb(vec3 c) {\n return mix(c/12.92,pow((c+.055)/1.055,vec3(2.4)),step(vec3(.04045),c));\n}\nvoid main() {\n float coverage=texture(scene,uv).a;\n vec4 backdrop=texture(map,uv);\n // A transparent current target is evaluated over white; DOM pixels are unavailable.\n vec3 background=mix(vec3(1),backdrop.rgb,backdrop.a);\n float luminance=dot(linearRgb(background),vec3(.2126,.7152,.0722));\n // Equal black/white contrast occurs at sqrt(.05*1.05)-.05.\n int choice=luminance<.179128784747792?0:1;\n vec4 color=texelFetch(lightMap,ivec2(choice,0),0);\n float alpha=coverage*color.a;\n outputColor=vec4(color.rgb*alpha,alpha);\n}\n`;\n/** Shares the existing private GPU resource/context-loss implementation. */\nexport class BackdropContrastGpu extends ReflectionGpu {\n constructor(callbacks: Readonly<ReflectionGpuCallbacks> = {}) {\n super(callbacks, fragment);\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE;AAAA,OAEK;AAEP,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBV,MAAM,4BAA4B,cAAc;AAAA,EACrD,YAAY,YAA8C,CAAC,GAAG;AAC5D,UAAM,WAAW,QAAQ;AAAA,EAC3B;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SignalValue } from "../lib/signals/types.js";
|
|
2
|
+
import { type PibblFilter } from "../lib/style/filter-types.js";
|
|
3
|
+
/** Two foreground colors selected per backdrop pixel. See {@link backdropContrast}. */
|
|
4
|
+
export interface PibblBackdropContrastFilterOptions {
|
|
5
|
+
/** Color drawn over dark backdrop pixels; defaults to white. See {@link SignalValue}. */
|
|
6
|
+
readonly light?: SignalValue<string>;
|
|
7
|
+
/** Color drawn over light backdrop pixels; defaults to black. See {@link SignalValue}. */
|
|
8
|
+
readonly dark?: SignalValue<string>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Recolors a receiving primitive and its descendants per pixel, using their combined alpha
|
|
12
|
+
* as a mask. Chooses light below relative backdrop luminance 0.179129 and dark otherwise.
|
|
13
|
+
* Samples already-painted content in the current Canvas target; transparent backdrop is
|
|
14
|
+
* evaluated over white. Isolated/cached layers see their own local backdrop, not outer paint.
|
|
15
|
+
* Requires OffscreenCanvas and WebGL2; performs no scene pixel readback. Geometry is unchanged.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Optional signal-capable foreground colors; white and black by default.
|
|
18
|
+
* See {@link PibblBackdropContrastFilterOptions}.
|
|
19
|
+
* @returns An immutable descriptor for style.filter. See {@link PibblFilter}.
|
|
20
|
+
*/
|
|
21
|
+
export declare function backdropContrast(options?: PibblBackdropContrastFilterOptions): PibblFilter;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isSignal } from "../lib/signals/graph.js";
|
|
2
|
+
import { layoutDiagnostic } from "../lib/style/diagnostics.js";
|
|
3
|
+
import {
|
|
4
|
+
PIBBL_FILTER_EXECUTOR
|
|
5
|
+
} from "../lib/style/filter-types.js";
|
|
6
|
+
import { concreteColor } from "./native-color.js";
|
|
7
|
+
import {
|
|
8
|
+
filterBoundaryExecutor
|
|
9
|
+
} from "./filter-executor.js";
|
|
10
|
+
import { processBackdropContrast } from "./render-backdrop-contrast-boundary.js";
|
|
11
|
+
const definitions = /* @__PURE__ */ new WeakMap();
|
|
12
|
+
const executor = {
|
|
13
|
+
prepare(value, property, context) {
|
|
14
|
+
const options = definitions.get(value);
|
|
15
|
+
if (!options)
|
|
16
|
+
throw new Error(
|
|
17
|
+
"A backdropContrast filter was created by an incompatible Pibbl runtime."
|
|
18
|
+
);
|
|
19
|
+
const color = (key, fallback) => {
|
|
20
|
+
const input = options[key];
|
|
21
|
+
const value2 = input === void 0 ? fallback : isSignal(input) ? input.get() : input;
|
|
22
|
+
const result = concreteColor(value2, `${property}.${key}`, context);
|
|
23
|
+
if (typeof CSS === "undefined" || !CSS.supports("color", result))
|
|
24
|
+
throw layoutDiagnostic({
|
|
25
|
+
component: context.component,
|
|
26
|
+
property: `${property}.${key}`,
|
|
27
|
+
value: value2,
|
|
28
|
+
algorithm: context.algorithm,
|
|
29
|
+
constraints: context.constraints,
|
|
30
|
+
reason: "must be a supported concrete CSS color"
|
|
31
|
+
});
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
const light = color("light", "white"), dark = color("dark", "black");
|
|
35
|
+
return Object.freeze({
|
|
36
|
+
executor: filterBoundaryExecutor,
|
|
37
|
+
needsBackdrop: true,
|
|
38
|
+
reverseInfluence: (output) => ({ ...output }),
|
|
39
|
+
process: (source, windows, backdrop) => {
|
|
40
|
+
if (!backdrop)
|
|
41
|
+
throw new Error(
|
|
42
|
+
"backdropContrast requires the current Canvas backdrop."
|
|
43
|
+
);
|
|
44
|
+
return processBackdropContrast(source, windows, backdrop, {
|
|
45
|
+
light,
|
|
46
|
+
dark
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
render: filterBoundaryExecutor.render
|
|
52
|
+
};
|
|
53
|
+
function backdropContrast(options = {}) {
|
|
54
|
+
const value = Object.freeze({ [PIBBL_FILTER_EXECUTOR]: executor });
|
|
55
|
+
definitions.set(value, Object.freeze({ ...options }));
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
backdropContrast
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=backdrop-contrast.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/filters/backdrop-contrast.ts"],
|
|
4
|
+
"sourcesContent": ["import { isSignal } from \"../lib/signals/graph.js\";\nimport type { SignalValue } from \"../lib/signals/types.js\";\nimport { layoutDiagnostic } from \"../lib/style/diagnostics.js\";\nimport {\n PIBBL_FILTER_EXECUTOR,\n type PibblFilter,\n type PibblFilterExecutor,\n} from \"../lib/style/filter-types.js\";\nimport { concreteColor } from \"./native-color.js\";\nimport {\n filterBoundaryExecutor,\n type CustomPreparedFilter,\n} from \"./filter-executor.js\";\nimport { processBackdropContrast } from \"./render-backdrop-contrast-boundary.js\";\n\n/** Two foreground colors selected per backdrop pixel. See {@link backdropContrast}. */\nexport interface PibblBackdropContrastFilterOptions {\n /** Color drawn over dark backdrop pixels; defaults to white. See {@link SignalValue}. */\n readonly light?: SignalValue<string>;\n /** Color drawn over light backdrop pixels; defaults to black. See {@link SignalValue}. */\n readonly dark?: SignalValue<string>;\n}\nconst definitions = new WeakMap<\n PibblFilter,\n Readonly<PibblBackdropContrastFilterOptions>\n>();\nconst executor: PibblFilterExecutor = {\n prepare(value, property, context) {\n const options = definitions.get(value);\n if (!options)\n throw new Error(\n \"A backdropContrast filter was created by an incompatible Pibbl runtime.\",\n );\n const color = (key: \"light\" | \"dark\", fallback: string) => {\n const input = options[key];\n const value =\n input === undefined ? fallback : isSignal(input) ? input.get() : input;\n const result = concreteColor(value, `${property}.${key}`, context);\n if (typeof CSS === \"undefined\" || !CSS.supports(\"color\", result))\n throw layoutDiagnostic({\n component: context.component,\n property: `${property}.${key}`,\n value,\n algorithm: context.algorithm,\n constraints: context.constraints,\n reason: \"must be a supported concrete CSS color\",\n });\n return result;\n };\n const light = color(\"light\", \"white\"),\n dark = color(\"dark\", \"black\");\n return Object.freeze({\n executor: filterBoundaryExecutor,\n needsBackdrop: true,\n reverseInfluence: (output) => ({ ...output }),\n process: (source, windows, backdrop) => {\n if (!backdrop)\n throw new Error(\n \"backdropContrast requires the current Canvas backdrop.\",\n );\n return processBackdropContrast(source, windows, backdrop, {\n light,\n dark,\n });\n },\n } satisfies CustomPreparedFilter);\n },\n render: filterBoundaryExecutor.render,\n};\n/**\n * Recolors a receiving primitive and its descendants per pixel, using their combined alpha\n * as a mask. Chooses light below relative backdrop luminance 0.179129 and dark otherwise.\n * Samples already-painted content in the current Canvas target; transparent backdrop is\n * evaluated over white. Isolated/cached layers see their own local backdrop, not outer paint.\n * Requires OffscreenCanvas and WebGL2; performs no scene pixel readback. Geometry is unchanged.\n *\n * @param options - Optional signal-capable foreground colors; white and black by default.\n * See {@link PibblBackdropContrastFilterOptions}.\n * @returns An immutable descriptor for style.filter. See {@link PibblFilter}.\n */\nexport function backdropContrast(\n options: PibblBackdropContrastFilterOptions = {},\n): PibblFilter {\n const value = Object.freeze({ [PIBBL_FILTER_EXECUTOR]: executor });\n definitions.set(value, Object.freeze({ ...options }));\n return value;\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,gBAAgB;AAEzB,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,OAGK;AACP,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,OAEK;AACP,SAAS,+BAA+B;AASxC,MAAM,cAAc,oBAAI,QAGtB;AACF,MAAM,WAAgC;AAAA,EACpC,QAAQ,OAAO,UAAU,SAAS;AAChC,UAAM,UAAU,YAAY,IAAI,KAAK;AACrC,QAAI,CAAC;AACH,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AACF,UAAM,QAAQ,CAAC,KAAuB,aAAqB;AACzD,YAAM,QAAQ,QAAQ,GAAG;AACzB,YAAMA,SACJ,UAAU,SAAY,WAAW,SAAS,KAAK,IAAI,MAAM,IAAI,IAAI;AACnE,YAAM,SAAS,cAAcA,QAAO,GAAG,QAAQ,IAAI,GAAG,IAAI,OAAO;AACjE,UAAI,OAAO,QAAQ,eAAe,CAAC,IAAI,SAAS,SAAS,MAAM;AAC7D,cAAM,iBAAiB;AAAA,UACrB,WAAW,QAAQ;AAAA,UACnB,UAAU,GAAG,QAAQ,IAAI,GAAG;AAAA,UAC5B,OAAAA;AAAA,UACA,WAAW,QAAQ;AAAA,UACnB,aAAa,QAAQ;AAAA,UACrB,QAAQ;AAAA,QACV,CAAC;AACH,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,MAAM,SAAS,OAAO,GAClC,OAAO,MAAM,QAAQ,OAAO;AAC9B,WAAO,OAAO,OAAO;AAAA,MACnB,UAAU;AAAA,MACV,eAAe;AAAA,MACf,kBAAkB,CAAC,YAAY,EAAE,GAAG,OAAO;AAAA,MAC3C,SAAS,CAAC,QAAQ,SAAS,aAAa;AACtC,YAAI,CAAC;AACH,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AACF,eAAO,wBAAwB,QAAQ,SAAS,UAAU;AAAA,UACxD;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAgC;AAAA,EAClC;AAAA,EACA,QAAQ,uBAAuB;AACjC;AAYO,SAAS,iBACd,UAA8C,CAAC,GAClC;AACb,QAAM,QAAQ,OAAO,OAAO,EAAE,CAAC,qBAAqB,GAAG,SAAS,CAAC;AACjE,cAAY,IAAI,OAAO,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC;AACpD,SAAO;AACT;",
|
|
6
|
+
"names": ["value"]
|
|
7
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { TexturePaint } from
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { TexturePaint } from "../lib/textures/texture-protocol.js";
|
|
2
|
+
import type { Canvas } from "../lib/types.js";
|
|
3
|
+
import type { FilterWindows, PixelBounds } from "../lib/filter/filter-region.js";
|
|
4
|
+
import type { PibblFilterBoundaryExecutor, PreparedPibblFilter } from "../lib/style/filter-types.js";
|
|
5
|
+
/** Backing-pixel projection of the already-painted current render target. */
|
|
6
|
+
export interface FilterBackdrop {
|
|
7
|
+
readonly canvas: Canvas;
|
|
8
|
+
readonly transform: DOMMatrix;
|
|
9
|
+
}
|
|
4
10
|
/** An operation-owned pass. The shared executor knows nothing about its inputs. */
|
|
5
11
|
export interface CustomPreparedFilter extends PreparedPibblFilter {
|
|
6
12
|
readonly reverseInfluence: (output: Readonly<PixelBounds>, density: number) => PixelBounds;
|
|
@@ -16,6 +22,7 @@ export interface CustomPreparedFilter extends PreparedPibblFilter {
|
|
|
16
22
|
}>;
|
|
17
23
|
}>;
|
|
18
24
|
readonly fuseWithPrevious?: (previous: CustomPreparedFilter) => CustomPreparedFilter | undefined;
|
|
19
|
-
readonly
|
|
25
|
+
readonly needsBackdrop?: boolean;
|
|
26
|
+
readonly process?: (source: OffscreenCanvas, windows: FilterWindows, backdrop?: FilterBackdrop) => ImageBitmap;
|
|
20
27
|
}
|
|
21
28
|
export declare const filterBoundaryExecutor: PibblFilterBoundaryExecutor;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { GLOBAL_STATE } from "../lib/global-state.js";
|
|
2
2
|
import { getOptionalService } from "../lib/optional-services.js";
|
|
3
|
+
import { getCurrentPaintTransform } from "../lib/render-target.js";
|
|
3
4
|
import { enterPrimitivePresentation } from "../lib/primitive-presentation.js";
|
|
4
5
|
import { renderFilterBoundary } from "../lib/filter/render-filter-boundary.js";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
compileNativeFilters
|
|
8
|
+
} from "./native-operations.js";
|
|
6
9
|
import { assertNativeCanvasFilter } from "./native-canvas-filter.js";
|
|
7
10
|
const SCRATCH = /* @__PURE__ */ Symbol("pibbl.filter-pass-scratch");
|
|
8
11
|
class PassScratch {
|
|
@@ -24,11 +27,17 @@ class PassScratch {
|
|
|
24
27
|
if (success || !this.committed) this.dispose();
|
|
25
28
|
}
|
|
26
29
|
target(index, width, height) {
|
|
27
|
-
const canvas = this.surfaces[index] ??= new OffscreenCanvas(
|
|
30
|
+
const canvas = this.surfaces[index] ??= new OffscreenCanvas(
|
|
31
|
+
width,
|
|
32
|
+
height
|
|
33
|
+
);
|
|
28
34
|
if (canvas.width !== width) canvas.width = width;
|
|
29
35
|
if (canvas.height !== height) canvas.height = height;
|
|
30
36
|
const context = canvas.getContext("2d");
|
|
31
|
-
if (!context)
|
|
37
|
+
if (!context)
|
|
38
|
+
throw new Error(
|
|
39
|
+
"Filter passes require an OffscreenCanvas 2D scratch target."
|
|
40
|
+
);
|
|
32
41
|
context.reset();
|
|
33
42
|
return context;
|
|
34
43
|
}
|
|
@@ -60,58 +69,104 @@ const filterBoundaryExecutor = {
|
|
|
60
69
|
}
|
|
61
70
|
return;
|
|
62
71
|
}
|
|
63
|
-
if (operations.every(
|
|
64
|
-
|
|
72
|
+
if (operations.every(
|
|
73
|
+
(operation) => "css" in operation
|
|
74
|
+
)) {
|
|
75
|
+
renderFilterBoundary(
|
|
76
|
+
{
|
|
77
|
+
component: input.component,
|
|
78
|
+
filters: operations,
|
|
79
|
+
presentation: input.presentation
|
|
80
|
+
},
|
|
81
|
+
input.renderSubtree
|
|
82
|
+
);
|
|
65
83
|
return;
|
|
66
84
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const bitmap = operation.process(current, windows);
|
|
103
|
-
try {
|
|
85
|
+
const parent = operations.some(
|
|
86
|
+
(operation) => "needsBackdrop" in operation && operation.needsBackdrop
|
|
87
|
+
) ? GLOBAL_STATE.renderContext : void 0;
|
|
88
|
+
const parentTarget = parent ? GLOBAL_STATE.renderTarget : void 0;
|
|
89
|
+
const paint = parent ? getCurrentPaintTransform() : void 0;
|
|
90
|
+
const boundaryPaint = paint && input.presentation?.transform ? paint.multiply(input.presentation.transform) : paint;
|
|
91
|
+
const bounds = operations.map(
|
|
92
|
+
(operation) => "css" in operation ? operation : {
|
|
93
|
+
executor: filterBoundaryExecutor,
|
|
94
|
+
css: () => "none",
|
|
95
|
+
reverseInfluence: operation.reverseInfluence
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
renderFilterBoundary(
|
|
99
|
+
{
|
|
100
|
+
component: input.component,
|
|
101
|
+
filters: bounds,
|
|
102
|
+
presentation: input.presentation,
|
|
103
|
+
process(source, result, windows) {
|
|
104
|
+
const instance = GLOBAL_STATE.currentPibblInstance;
|
|
105
|
+
if (!instance)
|
|
106
|
+
throw new Error(
|
|
107
|
+
"Filter processing requires an active Pibbl instance."
|
|
108
|
+
);
|
|
109
|
+
const scratch = getOptionalService(
|
|
110
|
+
instance,
|
|
111
|
+
SCRATCH,
|
|
112
|
+
() => new PassScratch(instance)
|
|
113
|
+
);
|
|
114
|
+
scratch.used = true;
|
|
115
|
+
let current = source, pass = 0;
|
|
116
|
+
let native = [];
|
|
117
|
+
const target = () => scratch.target(pass++ % 2, source.width, source.height);
|
|
118
|
+
const applyNative = () => {
|
|
119
|
+
if (!native.length) return;
|
|
104
120
|
const context = target();
|
|
105
|
-
|
|
121
|
+
assertNativeCanvasFilter(
|
|
122
|
+
context,
|
|
123
|
+
compileNativeFilters(native, windows.density),
|
|
124
|
+
input.component,
|
|
125
|
+
() => {
|
|
126
|
+
for (const operation of native)
|
|
127
|
+
if (operation.onRejected) {
|
|
128
|
+
assertNativeCanvasFilter(
|
|
129
|
+
context,
|
|
130
|
+
operation.css(windows.density),
|
|
131
|
+
input.component,
|
|
132
|
+
operation.onRejected
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
context.drawImage(current, 0, 0);
|
|
106
138
|
current = context.canvas;
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
native = [];
|
|
140
|
+
};
|
|
141
|
+
for (const operation of operations) {
|
|
142
|
+
if ("css" in operation) {
|
|
143
|
+
native.push(operation);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
applyNative();
|
|
147
|
+
const backdrop = parent && parentTarget && boundaryPaint ? {
|
|
148
|
+
canvas: parent.canvas,
|
|
149
|
+
transform: windows.source.renderTransform.multiply(boundaryPaint.inverse()).multiply(parentTarget.inverseRenderTransform)
|
|
150
|
+
} : void 0;
|
|
151
|
+
const bitmap = operation.process(current, windows, backdrop);
|
|
152
|
+
try {
|
|
153
|
+
const context = target();
|
|
154
|
+
context.drawImage(bitmap, 0, 0);
|
|
155
|
+
current = context.canvas;
|
|
156
|
+
} finally {
|
|
157
|
+
bitmap.close();
|
|
158
|
+
}
|
|
109
159
|
}
|
|
160
|
+
applyNative();
|
|
161
|
+
result.drawImage(
|
|
162
|
+
current,
|
|
163
|
+
windows.sourceToOutputX,
|
|
164
|
+
windows.sourceToOutputY
|
|
165
|
+
);
|
|
110
166
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}, input.renderSubtree);
|
|
167
|
+
},
|
|
168
|
+
input.renderSubtree
|
|
169
|
+
);
|
|
115
170
|
}
|
|
116
171
|
};
|
|
117
172
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/filters/filter-executor.ts"],
|
|
4
|
-
"sourcesContent": ["import type { TexturePaint } from
|
|
5
|
-
"mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,kCAAkC;AAC3C,SAAS,4BAA4B;
|
|
4
|
+
"sourcesContent": ["import type { TexturePaint } from \"../lib/textures/texture-protocol.js\";\nimport { GLOBAL_STATE } from \"../lib/global-state.js\";\nimport { getOptionalService } from \"../lib/optional-services.js\";\nimport { getCurrentPaintTransform } from \"../lib/render-target.js\";\nimport type { Canvas } from \"../lib/types.js\";\nimport { enterPrimitivePresentation } from \"../lib/primitive-presentation.js\";\nimport { renderFilterBoundary } from \"../lib/filter/render-filter-boundary.js\";\nimport type {\n FilterWindows,\n PixelBounds,\n} from \"../lib/filter/filter-region.js\";\nimport type {\n PibblFilterBoundaryExecutor,\n PreparedPibblFilter,\n} from \"../lib/style/filter-types.js\";\nimport type { PibblInstance } from \"../lib/types.js\";\nimport {\n compileNativeFilters,\n type NativePreparedFilter,\n} from \"./native-operations.js\";\nimport { assertNativeCanvasFilter } from \"./native-canvas-filter.js\";\n\n/** Backing-pixel projection of the already-painted current render target. */\nexport interface FilterBackdrop {\n readonly canvas: Canvas;\n readonly transform: DOMMatrix;\n}\n\n/** An operation-owned pass. The shared executor knows nothing about its inputs. */\nexport interface CustomPreparedFilter extends PreparedPibblFilter {\n readonly reverseInfluence: (\n output: Readonly<PixelBounds>,\n density: number,\n ) => PixelBounds;\n /** Private fusion capability; no concrete GPU implementation enters this module. */\n readonly textureWarp?: Readonly<{\n map: TexturePaint;\n strength: number;\n region: Readonly<{ x: number; y: number; width: number; height: number }>;\n }>;\n readonly fuseWithPrevious?: (\n previous: CustomPreparedFilter,\n ) => CustomPreparedFilter | undefined;\n readonly needsBackdrop?: boolean;\n readonly process?: (\n source: OffscreenCanvas,\n windows: FilterWindows,\n backdrop?: FilterBackdrop,\n ) => ImageBitmap;\n}\ntype Operation = NativePreparedFilter | CustomPreparedFilter;\nconst SCRATCH = Symbol(\"pibbl.filter-pass-scratch\");\nclass PassScratch {\n private readonly surfaces: OffscreenCanvas[] = [];\n used = false;\n private committed = false;\n constructor(private readonly instance: PibblInstance) {}\n beginRender(): void {\n this.used = false;\n }\n endRender(success: boolean): void {\n if (success && this.used) {\n this.committed = true;\n return;\n }\n if (success || !this.committed) this.dispose();\n }\n target(\n index: number,\n width: number,\n height: number,\n ): OffscreenCanvasRenderingContext2D {\n const canvas = (this.surfaces[index] ??= new OffscreenCanvas(\n width,\n height,\n ));\n if (canvas.width !== width) canvas.width = width;\n if (canvas.height !== height) canvas.height = height;\n const context = canvas.getContext(\"2d\");\n if (!context)\n throw new Error(\n \"Filter passes require an OffscreenCanvas 2D scratch target.\",\n );\n context.reset();\n return context;\n }\n dispose(): void {\n for (const canvas of this.surfaces) {\n canvas.width = 0;\n canvas.height = 0;\n }\n this.surfaces.length = 0;\n this.instance.optionalServices.delete(SCRATCH);\n }\n}\n\nexport const filterBoundaryExecutor: PibblFilterBoundaryExecutor = {\n render(input) {\n const operations: Operation[] = [];\n for (const operation of input.filters as readonly Operation[]) {\n if (!(\"css\" in operation) && !operation.process) continue;\n const previous = operations.at(-1);\n const fused =\n previous && !(\"css\" in previous) && !(\"css\" in operation)\n ? operation.fuseWithPrevious?.(previous)\n : undefined;\n if (fused) operations[operations.length - 1] = fused;\n else operations.push(operation);\n }\n if (!operations.length) {\n const release = enterPrimitivePresentation(input.presentation);\n try {\n input.renderSubtree();\n } finally {\n release();\n }\n return;\n }\n if (\n operations.every(\n (operation): operation is NativePreparedFilter => \"css\" in operation,\n )\n ) {\n renderFilterBoundary(\n {\n component: input.component,\n filters: operations,\n presentation: input.presentation,\n },\n input.renderSubtree,\n );\n return;\n }\n // Snapshot the parent projection before capture switches the current render target.\n const parent = operations.some(\n (operation) => \"needsBackdrop\" in operation && operation.needsBackdrop,\n )\n ? GLOBAL_STATE.renderContext\n : undefined;\n const parentTarget = parent ? GLOBAL_STATE.renderTarget : undefined;\n const paint = parent ? getCurrentPaintTransform() : undefined;\n const boundaryPaint =\n paint && input.presentation?.transform\n ? paint.multiply(input.presentation.transform)\n : paint;\n const bounds = operations.map((operation) =>\n \"css\" in operation\n ? operation\n : {\n executor: filterBoundaryExecutor,\n css: () => \"none\",\n reverseInfluence: operation.reverseInfluence,\n },\n );\n renderFilterBoundary(\n {\n component: input.component,\n filters: bounds,\n presentation: input.presentation,\n process(source, result, windows) {\n const instance = GLOBAL_STATE.currentPibblInstance;\n if (!instance)\n throw new Error(\n \"Filter processing requires an active Pibbl instance.\",\n );\n const scratch = getOptionalService(\n instance,\n SCRATCH,\n () => new PassScratch(instance),\n );\n scratch.used = true;\n let current = source,\n pass = 0;\n let native: NativePreparedFilter[] = [];\n const target = () =>\n scratch.target(pass++ % 2, source.width, source.height);\n const applyNative = () => {\n if (!native.length) return;\n const context = target();\n assertNativeCanvasFilter(\n context,\n compileNativeFilters(native, windows.density),\n input.component,\n () => {\n for (const operation of native)\n if (operation.onRejected) {\n assertNativeCanvasFilter(\n context,\n operation.css(windows.density),\n input.component,\n operation.onRejected,\n );\n }\n },\n );\n context.drawImage(current, 0, 0);\n current = context.canvas;\n native = [];\n };\n for (const operation of operations) {\n if (\"css\" in operation) {\n native.push(operation);\n continue;\n }\n applyNative();\n const backdrop =\n parent && parentTarget && boundaryPaint\n ? {\n canvas: parent.canvas,\n transform: windows.source.renderTransform\n .multiply(boundaryPaint.inverse())\n .multiply(parentTarget.inverseRenderTransform),\n }\n : undefined;\n const bitmap = operation.process!(current, windows, backdrop);\n try {\n const context = target();\n context.drawImage(bitmap, 0, 0);\n current = context.canvas;\n } finally {\n bitmap.close();\n }\n }\n applyNative();\n result.drawImage(\n current,\n windows.sourceToOutputX,\n windows.sourceToOutputY,\n );\n },\n },\n input.renderSubtree,\n );\n },\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PibblInstance } from "../lib/types.js";
|
|
2
|
+
import type { FilterWindows } from "../lib/filter/filter-region.js";
|
|
3
|
+
import type { FilterBackdrop } from "./filter-executor.js";
|
|
4
|
+
import { BackdropContrastGpu } from "./backdrop-contrast-gpu.js";
|
|
5
|
+
declare class BackdropContrastService {
|
|
6
|
+
private readonly instance;
|
|
7
|
+
readonly gpu: BackdropContrastGpu;
|
|
8
|
+
private readonly backdrop;
|
|
9
|
+
private readonly palette;
|
|
10
|
+
private revision;
|
|
11
|
+
private paletteRevision;
|
|
12
|
+
private colors;
|
|
13
|
+
private committed;
|
|
14
|
+
private disposed;
|
|
15
|
+
used: boolean;
|
|
16
|
+
constructor(instance: PibblInstance);
|
|
17
|
+
beginRender(): void;
|
|
18
|
+
endRender(success: boolean): void;
|
|
19
|
+
apply(source: OffscreenCanvas, windows: FilterWindows, input: FilterBackdrop, colors: Readonly<{
|
|
20
|
+
light: string;
|
|
21
|
+
dark: string;
|
|
22
|
+
}>): ImageBitmap;
|
|
23
|
+
dispose(): void;
|
|
24
|
+
}
|
|
25
|
+
/** @internal Read-only ownership evidence; never creates resources. */
|
|
26
|
+
export declare function peekBackdropContrastService(instance: PibblInstance): BackdropContrastService | undefined;
|
|
27
|
+
export declare function processBackdropContrast(source: OffscreenCanvas, windows: FilterWindows, backdrop: FilterBackdrop, colors: Readonly<{
|
|
28
|
+
light: string;
|
|
29
|
+
dark: string;
|
|
30
|
+
}>): ImageBitmap;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { GLOBAL_STATE } from "../lib/global-state.js";
|
|
2
|
+
import { getOptionalService } from "../lib/optional-services.js";
|
|
3
|
+
import { update } from "../lib/update.js";
|
|
4
|
+
import { BackdropContrastGpu } from "./backdrop-contrast-gpu.js";
|
|
5
|
+
const SERVICE = /* @__PURE__ */ Symbol("pibbl.backdrop-contrast");
|
|
6
|
+
class BackdropContrastService {
|
|
7
|
+
constructor(instance) {
|
|
8
|
+
this.instance = instance;
|
|
9
|
+
this.gpu = new BackdropContrastGpu({
|
|
10
|
+
onContextRestored: () => {
|
|
11
|
+
if (!this.disposed && !instance.mainTeardowns.closed)
|
|
12
|
+
update(instance.canvas, void 0, true);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
instance;
|
|
17
|
+
gpu;
|
|
18
|
+
backdrop = new OffscreenCanvas(1, 1);
|
|
19
|
+
palette = new OffscreenCanvas(2, 1);
|
|
20
|
+
revision = 0;
|
|
21
|
+
paletteRevision = 0;
|
|
22
|
+
colors = "";
|
|
23
|
+
committed = false;
|
|
24
|
+
disposed = false;
|
|
25
|
+
used = false;
|
|
26
|
+
beginRender() {
|
|
27
|
+
this.used = false;
|
|
28
|
+
}
|
|
29
|
+
endRender(success) {
|
|
30
|
+
if (success && this.used) {
|
|
31
|
+
this.committed = true;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (success || !this.committed) this.dispose();
|
|
35
|
+
}
|
|
36
|
+
apply(source, windows, input, colors) {
|
|
37
|
+
this.used = true;
|
|
38
|
+
if (this.backdrop.width !== source.width)
|
|
39
|
+
this.backdrop.width = source.width;
|
|
40
|
+
if (this.backdrop.height !== source.height)
|
|
41
|
+
this.backdrop.height = source.height;
|
|
42
|
+
const context = this.backdrop.getContext("2d");
|
|
43
|
+
if (!context)
|
|
44
|
+
throw new Error("backdropContrast requires OffscreenCanvas 2D.");
|
|
45
|
+
context.reset();
|
|
46
|
+
context.setTransform(input.transform);
|
|
47
|
+
context.drawImage(input.canvas, 0, 0);
|
|
48
|
+
const colorKey = JSON.stringify([colors.light, colors.dark]);
|
|
49
|
+
if (this.colors !== colorKey) {
|
|
50
|
+
const palette = this.palette.getContext("2d");
|
|
51
|
+
if (!palette)
|
|
52
|
+
throw new Error("backdropContrast colors require OffscreenCanvas 2D.");
|
|
53
|
+
palette.reset();
|
|
54
|
+
palette.fillStyle = colors.light;
|
|
55
|
+
palette.fillRect(0, 0, 1, 1);
|
|
56
|
+
palette.fillStyle = colors.dark;
|
|
57
|
+
palette.fillRect(1, 0, 1, 1);
|
|
58
|
+
this.colors = colorKey;
|
|
59
|
+
this.paletteRevision++;
|
|
60
|
+
}
|
|
61
|
+
const raster = (canvas, revision) => ({
|
|
62
|
+
source: canvas,
|
|
63
|
+
width: canvas.width,
|
|
64
|
+
height: canvas.height,
|
|
65
|
+
resource: canvas,
|
|
66
|
+
revision,
|
|
67
|
+
placement: { mode: "stretch" }
|
|
68
|
+
});
|
|
69
|
+
const region = windows.source.logicalBounds;
|
|
70
|
+
return this.gpu.apply(
|
|
71
|
+
source,
|
|
72
|
+
raster(this.backdrop, ++this.revision),
|
|
73
|
+
{ raster: raster(this.palette, this.paletteRevision) },
|
|
74
|
+
{ opacity: 1, threshold: 0, softness: 1, direction: [0, 1] },
|
|
75
|
+
{
|
|
76
|
+
originX: region.x,
|
|
77
|
+
originY: region.y,
|
|
78
|
+
density: windows.density,
|
|
79
|
+
region
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
dispose() {
|
|
84
|
+
if (this.disposed) return;
|
|
85
|
+
this.disposed = true;
|
|
86
|
+
this.gpu.dispose();
|
|
87
|
+
this.backdrop.width = this.backdrop.height = this.palette.width = this.palette.height = 0;
|
|
88
|
+
this.instance.optionalServices.delete(SERVICE);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function peekBackdropContrastService(instance) {
|
|
92
|
+
return instance.optionalServices.get(SERVICE);
|
|
93
|
+
}
|
|
94
|
+
function processBackdropContrast(source, windows, backdrop, colors) {
|
|
95
|
+
const instance = GLOBAL_STATE.currentPibblInstance;
|
|
96
|
+
if (!instance)
|
|
97
|
+
throw new Error("backdropContrast requires an active Pibbl instance.");
|
|
98
|
+
const service = getOptionalService(
|
|
99
|
+
instance,
|
|
100
|
+
SERVICE,
|
|
101
|
+
() => new BackdropContrastService(instance)
|
|
102
|
+
);
|
|
103
|
+
try {
|
|
104
|
+
return service.apply(source, windows, backdrop, colors);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`backdropContrast processing failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
108
|
+
{ cause: error }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
peekBackdropContrastService,
|
|
114
|
+
processBackdropContrast
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=render-backdrop-contrast-boundary.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/filters/render-backdrop-contrast-boundary.ts"],
|
|
4
|
+
"sourcesContent": ["import { GLOBAL_STATE } from \"../lib/global-state.js\";\nimport { getOptionalService } from \"../lib/optional-services.js\";\nimport { update } from \"../lib/update.js\";\nimport type { PibblInstance } from \"../lib/types.js\";\nimport type { FilterWindows } from \"../lib/filter/filter-region.js\";\nimport type { FilterBackdrop } from \"./filter-executor.js\";\nimport type { BorrowedTextureRaster } from \"../lib/textures/texture-protocol.js\";\nimport { BackdropContrastGpu } from \"./backdrop-contrast-gpu.js\";\n\nconst SERVICE = Symbol(\"pibbl.backdrop-contrast\");\nclass BackdropContrastService {\n readonly gpu: BackdropContrastGpu;\n private readonly backdrop = new OffscreenCanvas(1, 1);\n private readonly palette = new OffscreenCanvas(2, 1);\n private revision = 0;\n private paletteRevision = 0;\n private colors = \"\";\n private committed = false;\n private disposed = false;\n used = false;\n constructor(private readonly instance: PibblInstance) {\n this.gpu = new BackdropContrastGpu({\n onContextRestored: () => {\n if (!this.disposed && !instance.mainTeardowns.closed)\n update(instance.canvas, undefined, true);\n },\n });\n }\n beginRender(): void {\n this.used = false;\n }\n endRender(success: boolean): void {\n if (success && this.used) {\n this.committed = true;\n return;\n }\n if (success || !this.committed) this.dispose();\n }\n apply(\n source: OffscreenCanvas,\n windows: FilterWindows,\n input: FilterBackdrop,\n colors: Readonly<{ light: string; dark: string }>,\n ): ImageBitmap {\n this.used = true;\n if (this.backdrop.width !== source.width)\n this.backdrop.width = source.width;\n if (this.backdrop.height !== source.height)\n this.backdrop.height = source.height;\n const context = this.backdrop.getContext(\"2d\");\n if (!context)\n throw new Error(\"backdropContrast requires OffscreenCanvas 2D.\");\n context.reset();\n context.setTransform(input.transform);\n context.drawImage(input.canvas, 0, 0);\n const colorKey = JSON.stringify([colors.light, colors.dark]);\n if (this.colors !== colorKey) {\n const palette = this.palette.getContext(\"2d\");\n if (!palette)\n throw new Error(\"backdropContrast colors require OffscreenCanvas 2D.\");\n palette.reset();\n palette.fillStyle = colors.light;\n palette.fillRect(0, 0, 1, 1);\n palette.fillStyle = colors.dark;\n palette.fillRect(1, 0, 1, 1);\n this.colors = colorKey;\n this.paletteRevision++;\n }\n const raster = (\n canvas: OffscreenCanvas,\n revision: number,\n ): BorrowedTextureRaster => ({\n source: canvas,\n width: canvas.width,\n height: canvas.height,\n resource: canvas,\n revision,\n placement: { mode: \"stretch\" },\n });\n const region = windows.source.logicalBounds;\n return this.gpu.apply(\n source,\n raster(this.backdrop, ++this.revision),\n { raster: raster(this.palette, this.paletteRevision) },\n { opacity: 1, threshold: 0, softness: 1, direction: [0, 1] },\n {\n originX: region.x,\n originY: region.y,\n density: windows.density,\n region,\n },\n );\n }\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.gpu.dispose();\n this.backdrop.width =\n this.backdrop.height =\n this.palette.width =\n this.palette.height =\n 0;\n this.instance.optionalServices.delete(SERVICE);\n }\n}\n/** @internal Read-only ownership evidence; never creates resources. */\nexport function peekBackdropContrastService(\n instance: PibblInstance,\n): BackdropContrastService | undefined {\n return instance.optionalServices.get(SERVICE) as\n BackdropContrastService | undefined;\n}\nexport function processBackdropContrast(\n source: OffscreenCanvas,\n windows: FilterWindows,\n backdrop: FilterBackdrop,\n colors: Readonly<{ light: string; dark: string }>,\n): ImageBitmap {\n const instance = GLOBAL_STATE.currentPibblInstance;\n if (!instance)\n throw new Error(\"backdropContrast requires an active Pibbl instance.\");\n const service = getOptionalService(\n instance,\n SERVICE,\n () => new BackdropContrastService(instance),\n );\n try {\n return service.apply(source, windows, backdrop, colors);\n } catch (error) {\n throw new Error(\n `backdropContrast processing failed: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/filters.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export { blur, brightness, contrast, dropShadow, grayscale, hueRotate, invert, opacity, saturate, sepia, } from
|
|
2
|
-
export { displacement } from
|
|
3
|
-
export type { PibblFilter, PibblBlurFilterOptions, PibblBrightnessFilterOptions, PibblContrastFilterOptions, PibblDropShadowFilterOptions, PibblGrayscaleFilterOptions, PibblHueRotateFilterOptions, PibblInvertFilterOptions, PibblOpacityFilterOptions, PibblSaturateFilterOptions, PibblSepiaFilterOptions, PibblDisplacementFilterOptions, } from
|
|
4
|
-
export { reflectedLight } from
|
|
5
|
-
export type { PibblReflectedLightFilterOptions, PibblReflectedLightSource } from
|
|
6
|
-
export { emboss } from
|
|
7
|
-
export type { PibblEmbossFilterOptions } from
|
|
1
|
+
export { blur, brightness, contrast, dropShadow, grayscale, hueRotate, invert, opacity, saturate, sepia, } from "./filters/native.js";
|
|
2
|
+
export { displacement } from "./filters/displacement.js";
|
|
3
|
+
export type { PibblFilter, PibblBlurFilterOptions, PibblBrightnessFilterOptions, PibblContrastFilterOptions, PibblDropShadowFilterOptions, PibblGrayscaleFilterOptions, PibblHueRotateFilterOptions, PibblInvertFilterOptions, PibblOpacityFilterOptions, PibblSaturateFilterOptions, PibblSepiaFilterOptions, PibblDisplacementFilterOptions, } from "./lib/style/filter-types.js";
|
|
4
|
+
export { reflectedLight } from "./filters/reflected-light.js";
|
|
5
|
+
export type { PibblReflectedLightFilterOptions, PibblReflectedLightSource, } from "./lib/style/filter-types.js";
|
|
6
|
+
export { emboss } from "./filters/emboss.js";
|
|
7
|
+
export type { PibblEmbossFilterOptions } from "./filters/emboss.js";
|
|
8
|
+
export { backdropContrast } from "./filters/backdrop-contrast.js";
|
|
9
|
+
export type { PibblBackdropContrastFilterOptions } from "./filters/backdrop-contrast.js";
|
package/dist/filters.js
CHANGED
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
import { displacement } from "./filters/displacement.js";
|
|
14
14
|
import { reflectedLight } from "./filters/reflected-light.js";
|
|
15
15
|
import { emboss } from "./filters/emboss.js";
|
|
16
|
+
import { backdropContrast } from "./filters/backdrop-contrast.js";
|
|
16
17
|
export {
|
|
18
|
+
backdropContrast,
|
|
17
19
|
blur,
|
|
18
20
|
brightness,
|
|
19
21
|
contrast,
|