@viewscript/renderer 0.1.0-202605140639
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/ast/types.d.ts +403 -0
- package/dist/ast/types.js +33 -0
- package/dist/compiler/chunk-splitter.d.ts +98 -0
- package/dist/compiler/chunk-splitter.js +361 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +17 -0
- package/dist/rasterizer/__tests__/error-distribution.test.d.ts +7 -0
- package/dist/rasterizer/__tests__/error-distribution.test.js +322 -0
- package/dist/rasterizer/canvas-mapper.d.ts +280 -0
- package/dist/rasterizer/canvas-mapper.js +414 -0
- package/dist/rasterizer/error-distribution.d.ts +143 -0
- package/dist/rasterizer/error-distribution.js +231 -0
- package/dist/rasterizer/gradient-mapper.d.ts +223 -0
- package/dist/rasterizer/gradient-mapper.js +352 -0
- package/dist/rasterizer/topology-rounding.d.ts +151 -0
- package/dist/rasterizer/topology-rounding.js +347 -0
- package/dist/runtime/__tests__/event-backpressure.test.d.ts +10 -0
- package/dist/runtime/__tests__/event-backpressure.test.js +190 -0
- package/dist/runtime/event-backpressure.d.ts +393 -0
- package/dist/runtime/event-backpressure.js +458 -0
- package/dist/runtime/render-loop.d.ts +277 -0
- package/dist/runtime/render-loop.js +435 -0
- package/dist/runtime/wasm-resource-manager.d.ts +122 -0
- package/dist/runtime/wasm-resource-manager.js +253 -0
- package/dist/runtime/wgpu-renderer-adapter.d.ts +168 -0
- package/dist/runtime/wgpu-renderer-adapter.js +230 -0
- package/dist/semantic/__tests__/semantic-translator.test.d.ts +4 -0
- package/dist/semantic/__tests__/semantic-translator.test.js +203 -0
- package/dist/semantic/semantic-translator.d.ts +229 -0
- package/dist/semantic/semantic-translator.js +398 -0
- package/package.json +28 -0
- package/playwright-report/data/0bafe4e0863f0e244bba68a838f73241f8f2efaa.md +226 -0
- package/playwright-report/data/9281aca8abfb06c6cecb35d5ddd13d61f8c752d8.md +226 -0
- package/playwright-report/index.html +90 -0
- package/playwright.config.ts +160 -0
- package/screenshot-chrome.png +0 -0
- package/screenshots/visual-demo-verification.png +0 -0
- package/screenshots/visual-demo.png +0 -0
- package/src/ast/types.ts +473 -0
- package/src/compiler/chunk-splitter.ts +534 -0
- package/src/index.ts +62 -0
- package/src/rasterizer/__tests__/error-distribution.test.ts +382 -0
- package/src/rasterizer/canvas-mapper.ts +677 -0
- package/src/rasterizer/error-distribution.ts +344 -0
- package/src/rasterizer/gradient-mapper.ts +563 -0
- package/src/rasterizer/topology-rounding.ts +499 -0
- package/src/runtime/__tests__/event-backpressure.test.ts +254 -0
- package/src/runtime/event-backpressure.ts +622 -0
- package/src/runtime/render-loop.ts +660 -0
- package/src/runtime/wasm-resource-manager.ts +349 -0
- package/src/runtime/wgpu-renderer-adapter.ts +318 -0
- package/src/semantic/__tests__/semantic-translator.test.ts +263 -0
- package/src/semantic/semantic-translator.ts +637 -0
- package/test-results/.last-run.json +4 -0
- package/tests/e2e/async-race.spec.ts +612 -0
- package/tests/e2e/bilayer-sync.spec.ts +405 -0
- package/tests/e2e/failures/.gitkeep +0 -0
- package/tests/e2e/fullstack.spec.ts +681 -0
- package/tests/e2e/g1-continuity.spec.ts +703 -0
- package/tests/e2e/golden/.gitkeep +0 -0
- package/tests/e2e/golden/conic-color-wheel.raw +0 -0
- package/tests/e2e/golden/conic-color-wheel.sha256 +1 -0
- package/tests/e2e/golden/conic-rotated.raw +0 -0
- package/tests/e2e/golden/conic-rotated.sha256 +1 -0
- package/tests/e2e/golden/linear-45deg.raw +0 -0
- package/tests/e2e/golden/linear-45deg.sha256 +1 -0
- package/tests/e2e/golden/linear-horizontal.raw +0 -0
- package/tests/e2e/golden/linear-horizontal.sha256 +1 -0
- package/tests/e2e/golden/linear-multi-stop.raw +0 -0
- package/tests/e2e/golden/linear-multi-stop.sha256 +1 -0
- package/tests/e2e/golden/radial-circle-center.raw +0 -0
- package/tests/e2e/golden/radial-circle-center.sha256 +1 -0
- package/tests/e2e/golden/radial-offset.raw +0 -0
- package/tests/e2e/golden/radial-offset.sha256 +1 -0
- package/tests/e2e/golden/tile-mirror.raw +0 -0
- package/tests/e2e/golden/tile-mirror.sha256 +1 -0
- package/tests/e2e/golden/tile-repeat.raw +0 -0
- package/tests/e2e/golden/tile-repeat.sha256 +1 -0
- package/tests/e2e/gradient-animation.spec.ts +606 -0
- package/tests/e2e/memory-stability.spec.ts +396 -0
- package/tests/e2e/path-topology.spec.ts +674 -0
- package/tests/e2e/performance-profile.spec.ts +501 -0
- package/tests/e2e/screenshot.spec.ts +60 -0
- package/tests/e2e/test-harness.html +1005 -0
- package/tests/e2e/text-layout.spec.ts +451 -0
- package/tests/e2e/visual-demo.html +340 -0
- package/tests/e2e/visual-regression.spec.ts +335 -0
- package/tsconfig.json +12 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subpixel Error Distribution Algorithm
|
|
3
|
+
*
|
|
4
|
+
* This module implements the Largest Remainder Method (LRM) for distributing
|
|
5
|
+
* subpixel rounding errors across child elements within a parent container.
|
|
6
|
+
*
|
|
7
|
+
* ## The Problem (Architect's Decision #2: Spatial Closure)
|
|
8
|
+
*
|
|
9
|
+
* Given a 100px container with 3 children of equal width (33.333...px each):
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* Naive rounding:
|
|
13
|
+
* floor(33.333) = 33
|
|
14
|
+
* 33 + 33 + 33 = 99px ← 1px HOLE!
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* This violates VS axiom: "Constraint holes cannot be hidden in theoretical blind spots"
|
|
18
|
+
*
|
|
19
|
+
* ## Solution: Largest Remainder Method
|
|
20
|
+
*
|
|
21
|
+
* 1. Compute integer quotients: floor(33.333) = 33 for each
|
|
22
|
+
* 2. Compute remainders: 0.333... for each
|
|
23
|
+
* 3. Total shortfall: 100 - 99 = 1px
|
|
24
|
+
* 4. Distribute 1px to elements with largest remainders
|
|
25
|
+
*
|
|
26
|
+
* Result: [34, 33, 33] or [33, 34, 33] or [33, 33, 34]
|
|
27
|
+
* Sum: 100px exactly ✓
|
|
28
|
+
*
|
|
29
|
+
* ## Mathematical Guarantee
|
|
30
|
+
*
|
|
31
|
+
* For any set of positive rationals r₁, r₂, ..., rₙ where Σrᵢ = T (integer):
|
|
32
|
+
*
|
|
33
|
+
* Σ⌊rᵢ⌋ ≤ T ≤ Σ⌈rᵢ⌉
|
|
34
|
+
*
|
|
35
|
+
* LRM distributes exactly (T - Σ⌊rᵢ⌋) extra pixels to achieve Σ = T.
|
|
36
|
+
*/
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// Largest Remainder Method
|
|
39
|
+
// =============================================================================
|
|
40
|
+
/**
|
|
41
|
+
* Distribute subpixel errors using the Largest Remainder Method.
|
|
42
|
+
*
|
|
43
|
+
* ## Algorithm
|
|
44
|
+
*
|
|
45
|
+
* ```
|
|
46
|
+
* INPUT: childDimensions = [33.333, 33.333, 33.333], parentDimension = 100
|
|
47
|
+
*
|
|
48
|
+
* Step 1: Compute floors
|
|
49
|
+
* floors = [33, 33, 33]
|
|
50
|
+
* sum(floors) = 99
|
|
51
|
+
*
|
|
52
|
+
* Step 2: Compute remainders
|
|
53
|
+
* remainders = [0.333, 0.333, 0.333]
|
|
54
|
+
*
|
|
55
|
+
* Step 3: Compute shortfall
|
|
56
|
+
* shortfall = 100 - 99 = 1
|
|
57
|
+
*
|
|
58
|
+
* Step 4: Sort by remainder (descending), distribute shortfall
|
|
59
|
+
* Give 1px to first element (arbitrary tie-break: leftmost)
|
|
60
|
+
*
|
|
61
|
+
* OUTPUT: [34, 33, 33]
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* ## Tie-Breaking Strategy
|
|
65
|
+
*
|
|
66
|
+
* When remainders are equal (common for equal-width elements):
|
|
67
|
+
* - Distribute extra pixels left-to-right (reading order)
|
|
68
|
+
* - This is visually predictable and matches user expectation
|
|
69
|
+
*/
|
|
70
|
+
export function distributeWithLargestRemainder(group) {
|
|
71
|
+
const { childIds, parentDimension, childDimensions } = group;
|
|
72
|
+
// Edge case: no children
|
|
73
|
+
if (childIds.length === 0) {
|
|
74
|
+
return {
|
|
75
|
+
dimensions: [],
|
|
76
|
+
totalPixels: 0,
|
|
77
|
+
isExact: parentDimension === 0,
|
|
78
|
+
method: 'largest-remainder',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const items = childIds.map((id, index) => {
|
|
82
|
+
const original = childDimensions.get(id) ?? 0;
|
|
83
|
+
const floor = Math.floor(original);
|
|
84
|
+
return {
|
|
85
|
+
entityId: id,
|
|
86
|
+
original,
|
|
87
|
+
floor,
|
|
88
|
+
remainder: original - floor,
|
|
89
|
+
index,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
// Step 2: Compute shortfall
|
|
93
|
+
const sumOfFloors = items.reduce((sum, item) => sum + item.floor, 0);
|
|
94
|
+
const shortfall = parentDimension - sumOfFloors;
|
|
95
|
+
// Sanity check: shortfall should be non-negative and <= childCount
|
|
96
|
+
if (shortfall < 0 || shortfall > items.length) {
|
|
97
|
+
// This indicates a constraint violation (children sum > parent)
|
|
98
|
+
// Fall back to proportional scaling
|
|
99
|
+
return distributeProportionally(group);
|
|
100
|
+
}
|
|
101
|
+
// Step 3: Sort by remainder (descending), then by index (ascending) for tie-break
|
|
102
|
+
const sorted = [...items].sort((a, b) => {
|
|
103
|
+
const remainderDiff = b.remainder - a.remainder;
|
|
104
|
+
if (Math.abs(remainderDiff) > 1e-10) {
|
|
105
|
+
return remainderDiff;
|
|
106
|
+
}
|
|
107
|
+
// Tie-break: leftmost first
|
|
108
|
+
return a.index - b.index;
|
|
109
|
+
});
|
|
110
|
+
// Step 4: Distribute shortfall to top N elements
|
|
111
|
+
const extraPixels = new Set();
|
|
112
|
+
for (let i = 0; i < shortfall; i++) {
|
|
113
|
+
extraPixels.add(sorted[i].entityId);
|
|
114
|
+
}
|
|
115
|
+
// Step 5: Build result
|
|
116
|
+
const dimensions = items.map(item => {
|
|
117
|
+
const pixels = item.floor + (extraPixels.has(item.entityId) ? 1 : 0);
|
|
118
|
+
return {
|
|
119
|
+
entityId: item.entityId,
|
|
120
|
+
pixels,
|
|
121
|
+
original: item.original,
|
|
122
|
+
error: pixels - item.original,
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
const totalPixels = dimensions.reduce((sum, d) => sum + d.pixels, 0);
|
|
126
|
+
return {
|
|
127
|
+
dimensions,
|
|
128
|
+
totalPixels,
|
|
129
|
+
isExact: totalPixels === parentDimension,
|
|
130
|
+
method: 'largest-remainder',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Fallback: Proportional scaling when LRM fails.
|
|
135
|
+
*
|
|
136
|
+
* Used when child sum exceeds parent (constraint violation).
|
|
137
|
+
*/
|
|
138
|
+
function distributeProportionally(group) {
|
|
139
|
+
const { childIds, parentDimension, childDimensions } = group;
|
|
140
|
+
const totalOriginal = childIds.reduce((sum, id) => sum + (childDimensions.get(id) ?? 0), 0);
|
|
141
|
+
if (totalOriginal === 0) {
|
|
142
|
+
return {
|
|
143
|
+
dimensions: childIds.map(id => ({
|
|
144
|
+
entityId: id,
|
|
145
|
+
pixels: 0,
|
|
146
|
+
original: 0,
|
|
147
|
+
error: 0,
|
|
148
|
+
})),
|
|
149
|
+
totalPixels: 0,
|
|
150
|
+
isExact: parentDimension === 0,
|
|
151
|
+
method: 'first-fit',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const scale = parentDimension / totalOriginal;
|
|
155
|
+
const scaled = childIds.map(id => {
|
|
156
|
+
const original = childDimensions.get(id) ?? 0;
|
|
157
|
+
return {
|
|
158
|
+
entityId: id,
|
|
159
|
+
original,
|
|
160
|
+
scaled: original * scale,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
// Apply LRM to scaled values
|
|
164
|
+
const scaledGroup = {
|
|
165
|
+
...group,
|
|
166
|
+
childDimensions: new Map(scaled.map(s => [s.entityId, s.scaled])),
|
|
167
|
+
};
|
|
168
|
+
const result = distributeWithLargestRemainder(scaledGroup);
|
|
169
|
+
result.method = 'first-fit';
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Apply error distribution to a set of sibling groups.
|
|
174
|
+
*
|
|
175
|
+
* This is called AFTER basic topology-preserving rounding,
|
|
176
|
+
* to ensure parent boundaries are exactly satisfied.
|
|
177
|
+
*/
|
|
178
|
+
export function applyErrorDistribution(roundedBounds, containments) {
|
|
179
|
+
const result = new Map(roundedBounds);
|
|
180
|
+
for (const constraint of containments) {
|
|
181
|
+
const parentBounds = result.get(constraint.parentId);
|
|
182
|
+
if (!parentBounds)
|
|
183
|
+
continue;
|
|
184
|
+
const parentDimension = constraint.axis === 'horizontal'
|
|
185
|
+
? parentBounds.width
|
|
186
|
+
: parentBounds.height;
|
|
187
|
+
// Build sibling group
|
|
188
|
+
const childDimensions = new Map();
|
|
189
|
+
for (const childId of constraint.childIds) {
|
|
190
|
+
const childBounds = result.get(childId);
|
|
191
|
+
if (childBounds) {
|
|
192
|
+
const dim = constraint.axis === 'horizontal'
|
|
193
|
+
? childBounds.width
|
|
194
|
+
: childBounds.height;
|
|
195
|
+
childDimensions.set(childId, dim);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const group = {
|
|
199
|
+
parentId: constraint.parentId,
|
|
200
|
+
childIds: constraint.childIds,
|
|
201
|
+
axis: constraint.axis,
|
|
202
|
+
parentDimension,
|
|
203
|
+
childDimensions,
|
|
204
|
+
};
|
|
205
|
+
// Distribute
|
|
206
|
+
const distribution = distributeWithLargestRemainder(group);
|
|
207
|
+
// Apply distributed dimensions
|
|
208
|
+
let offset = constraint.axis === 'horizontal' ? parentBounds.x : parentBounds.y;
|
|
209
|
+
for (const dist of distribution.dimensions) {
|
|
210
|
+
const childBounds = result.get(dist.entityId);
|
|
211
|
+
if (childBounds) {
|
|
212
|
+
if (constraint.axis === 'horizontal') {
|
|
213
|
+
childBounds.x = offset;
|
|
214
|
+
childBounds.width = dist.pixels;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
childBounds.y = offset;
|
|
218
|
+
childBounds.height = dist.pixels;
|
|
219
|
+
}
|
|
220
|
+
offset += dist.pixels;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
// =============================================================================
|
|
227
|
+
// Exports for Testing
|
|
228
|
+
// =============================================================================
|
|
229
|
+
export const _internals = {
|
|
230
|
+
distributeProportionally,
|
|
231
|
+
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gradient Shader Mapper: P-Dimension to GPU Shaders
|
|
3
|
+
*
|
|
4
|
+
* This module maps P-dimension gradient entities to GPU shader objects.
|
|
5
|
+
* It handles the critical transition from exact rational arithmetic to GPU-compatible
|
|
6
|
+
* floating-point representation while preserving visual fidelity.
|
|
7
|
+
*
|
|
8
|
+
* ## Architecture
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* P-Dimension (Exact) GPU (Float)
|
|
12
|
+
* ─────────────────────────────────────────────────────────────
|
|
13
|
+
*
|
|
14
|
+
* LinearGradient { GpuShaderBackend.Shader
|
|
15
|
+
* start: Rational(1, 3) ────────────▶ MakeLinearGradient(
|
|
16
|
+
* end: Rational(2, 3) [0.333..., 0.666...],
|
|
17
|
+
* stops: [ colors: Float32Array,
|
|
18
|
+
* ColorStop(r=255, ...) positions: Float32Array
|
|
19
|
+
* ] )
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* ┌─────────────────────────────────────────────────────────────┐
|
|
23
|
+
* │ CRITICAL: Topology-preserving rounding at this boundary │
|
|
24
|
+
* │ │
|
|
25
|
+
* │ - Color channels [0, 255] → [0.0, 1.0] with clamping │
|
|
26
|
+
* │ - Position values [0, 1] stay exact, no interpolation │
|
|
27
|
+
* │ - Control points use same rounding as other coordinates │
|
|
28
|
+
* └─────────────────────────────────────────────────────────────┘
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* ## Usage
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const shader = mapLinearGradientToShader(ck, gradient, bounds, dpr);
|
|
35
|
+
* paint.setShader(shader);
|
|
36
|
+
* canvas.drawRect(bounds, paint);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
import type { Rational, RasterBounds } from '../ast/types';
|
|
40
|
+
/** GPU shader backend interface (minimal subset needed for gradients) */
|
|
41
|
+
export interface GpuShaderBackend {
|
|
42
|
+
Shader: {
|
|
43
|
+
MakeLinearGradient(start: Float32Array, end: Float32Array, colors: Float32Array, positions: Float32Array | null, mode: number, localMatrix?: Float32Array): ShaderInstance;
|
|
44
|
+
MakeTwoPointConicalGradient(start: Float32Array, startRadius: number, end: Float32Array, endRadius: number, colors: Float32Array, positions: Float32Array | null, mode: number, localMatrix?: Float32Array): ShaderInstance;
|
|
45
|
+
MakeSweepGradient(cx: number, cy: number, colors: Float32Array, positions: Float32Array | null, mode: number, startAngle: number, endAngle: number, localMatrix?: Float32Array): ShaderInstance;
|
|
46
|
+
};
|
|
47
|
+
TileMode: {
|
|
48
|
+
Clamp: number;
|
|
49
|
+
Repeat: number;
|
|
50
|
+
Mirror: number;
|
|
51
|
+
Decal: number;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export interface ShaderInstance {
|
|
55
|
+
delete(): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* P-dimension color stop with exact rational values.
|
|
59
|
+
*/
|
|
60
|
+
export interface PColorStop {
|
|
61
|
+
/** Entity ID */
|
|
62
|
+
id: number;
|
|
63
|
+
/** Red channel [0, 255] (rational) */
|
|
64
|
+
r: Rational;
|
|
65
|
+
/** Green channel [0, 255] (rational) */
|
|
66
|
+
g: Rational;
|
|
67
|
+
/** Blue channel [0, 255] (rational) */
|
|
68
|
+
b: Rational;
|
|
69
|
+
/** Alpha channel [0, 1] (rational) */
|
|
70
|
+
a: Rational;
|
|
71
|
+
/** Position along gradient [0, 1] (rational) */
|
|
72
|
+
position: Rational;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* P-dimension control point with exact coordinates.
|
|
76
|
+
*/
|
|
77
|
+
export interface PControlPoint {
|
|
78
|
+
id: number;
|
|
79
|
+
x: Rational;
|
|
80
|
+
y: Rational;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* P-dimension linear gradient definition.
|
|
84
|
+
*/
|
|
85
|
+
export interface PLinearGradient {
|
|
86
|
+
id: number;
|
|
87
|
+
start: PControlPoint;
|
|
88
|
+
end: PControlPoint;
|
|
89
|
+
stops: PColorStop[];
|
|
90
|
+
tileMode: 'clamp' | 'repeat' | 'mirror' | 'decal';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* P-dimension radial gradient definition.
|
|
94
|
+
*/
|
|
95
|
+
export interface PRadialGradient {
|
|
96
|
+
id: number;
|
|
97
|
+
center: PControlPoint;
|
|
98
|
+
radiusX: Rational;
|
|
99
|
+
radiusY: Rational;
|
|
100
|
+
focalPoint?: PControlPoint;
|
|
101
|
+
focalRadius?: Rational;
|
|
102
|
+
stops: PColorStop[];
|
|
103
|
+
tileMode: 'clamp' | 'repeat' | 'mirror' | 'decal';
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* P-dimension conic (sweep) gradient definition.
|
|
107
|
+
*/
|
|
108
|
+
export interface PConicGradient {
|
|
109
|
+
id: number;
|
|
110
|
+
center: PControlPoint;
|
|
111
|
+
/** Rotation offset in degrees (rational) */
|
|
112
|
+
rotation: Rational;
|
|
113
|
+
/** Start angle in degrees (rational) */
|
|
114
|
+
startAngle: Rational;
|
|
115
|
+
/** End angle in degrees (rational) */
|
|
116
|
+
endAngle: Rational;
|
|
117
|
+
stops: PColorStop[];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Map a P-dimension linear gradient to a GPU shader.
|
|
121
|
+
*
|
|
122
|
+
* @param ck - GPU shader backend instance
|
|
123
|
+
* @param gradient - P-dimension linear gradient definition
|
|
124
|
+
* @param bounds - Rasterized bounds for coordinate transformation
|
|
125
|
+
* @param devicePixelRatio - Device pixel ratio for coordinate scaling
|
|
126
|
+
* @returns GPU shader instance (caller must call delete() when done)
|
|
127
|
+
*/
|
|
128
|
+
export declare function mapLinearGradientToShader(ck: GpuShaderBackend, gradient: PLinearGradient, bounds: RasterBounds, devicePixelRatio: number): ShaderInstance;
|
|
129
|
+
/**
|
|
130
|
+
* Map a P-dimension radial gradient to a GPU shader.
|
|
131
|
+
*
|
|
132
|
+
* The GPU backend uses two-point conical gradients which can express:
|
|
133
|
+
* - Circle gradients (same center, different radii)
|
|
134
|
+
* - Focal gradients (different centers)
|
|
135
|
+
*
|
|
136
|
+
* @param ck - GPU shader backend instance
|
|
137
|
+
* @param gradient - P-dimension radial gradient definition
|
|
138
|
+
* @param bounds - Rasterized bounds for coordinate transformation
|
|
139
|
+
* @param devicePixelRatio - Device pixel ratio for coordinate scaling
|
|
140
|
+
* @returns GPU shader instance
|
|
141
|
+
*/
|
|
142
|
+
export declare function mapRadialGradientToShader(ck: GpuShaderBackend, gradient: PRadialGradient, bounds: RasterBounds, devicePixelRatio: number): ShaderInstance;
|
|
143
|
+
/**
|
|
144
|
+
* Map a P-dimension conic (sweep) gradient to a GPU shader.
|
|
145
|
+
*
|
|
146
|
+
* @param ck - GPU shader backend instance
|
|
147
|
+
* @param gradient - P-dimension conic gradient definition
|
|
148
|
+
* @param bounds - Rasterized bounds for coordinate transformation
|
|
149
|
+
* @param devicePixelRatio - Device pixel ratio for coordinate scaling
|
|
150
|
+
* @returns GPU shader instance
|
|
151
|
+
*/
|
|
152
|
+
export declare function mapConicGradientToShader(ck: GpuShaderBackend, gradient: PConicGradient, bounds: RasterBounds, devicePixelRatio: number): ShaderInstance;
|
|
153
|
+
/**
|
|
154
|
+
* Convert a rational number to a floating-point number.
|
|
155
|
+
*
|
|
156
|
+
* This is the critical boundary where exact arithmetic meets GPU floats.
|
|
157
|
+
* The conversion is straightforward division, but precision loss is
|
|
158
|
+
* unavoidable and acceptable at this layer.
|
|
159
|
+
*/
|
|
160
|
+
export declare function rationalToFloat(r: Rational): number;
|
|
161
|
+
/**
|
|
162
|
+
* Convert P-dimension color stops to a Float32Array of RGBA values.
|
|
163
|
+
*
|
|
164
|
+
* GPU shader expects colors in RGBA order, with each channel in [0, 1].
|
|
165
|
+
* P-dimension stores RGB in [0, 255] and Alpha in [0, 1].
|
|
166
|
+
*
|
|
167
|
+
* ## Topology-Preserving Rounding (Clamping)
|
|
168
|
+
*
|
|
169
|
+
* Color values are clamped to [0, 1] to ensure GPU-valid input.
|
|
170
|
+
* This preserves the topological ordering of colors even if the
|
|
171
|
+
* original rational values were slightly out of range.
|
|
172
|
+
*/
|
|
173
|
+
export declare function colorStopsToFloat32Array(stops: PColorStop[]): Float32Array;
|
|
174
|
+
/**
|
|
175
|
+
* Convert P-dimension color stop positions to a Float32Array.
|
|
176
|
+
*
|
|
177
|
+
* Positions are kept as-is (already in [0, 1] in P-dimension),
|
|
178
|
+
* with clamping for safety.
|
|
179
|
+
*/
|
|
180
|
+
export declare function positionsToFloat32Array(stops: PColorStop[]): Float32Array;
|
|
181
|
+
/**
|
|
182
|
+
* Clamp a value to [0, 1] range.
|
|
183
|
+
*
|
|
184
|
+
* This is the "topology-preserving rounding" for color values:
|
|
185
|
+
* it ensures the value is valid for GPU while preserving ordering.
|
|
186
|
+
*/
|
|
187
|
+
declare function clamp01(value: number): number;
|
|
188
|
+
/**
|
|
189
|
+
* Map P-dimension tile mode to GPU tile mode constant.
|
|
190
|
+
*/
|
|
191
|
+
declare function mapTileMode(ck: GpuShaderBackend, mode: 'clamp' | 'repeat' | 'mirror' | 'decal'): number;
|
|
192
|
+
/**
|
|
193
|
+
* Create a GPU shader from a FillStyle gradient definition.
|
|
194
|
+
*
|
|
195
|
+
* This is a higher-level factory that integrates with the existing
|
|
196
|
+
* FillStyle type from the AST.
|
|
197
|
+
*/
|
|
198
|
+
export declare function createGradientShader(ck: GpuShaderBackend, fillType: 'linear-gradient' | 'radial-gradient', stops: Array<{
|
|
199
|
+
offset: Rational;
|
|
200
|
+
color: string;
|
|
201
|
+
}>, bounds: RasterBounds, devicePixelRatio: number): ShaderInstance | null;
|
|
202
|
+
/**
|
|
203
|
+
* Parse a CSS color string to RGBA values.
|
|
204
|
+
*
|
|
205
|
+
* Supports:
|
|
206
|
+
* - Hex: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
|
|
207
|
+
* - Named colors (basic set)
|
|
208
|
+
*/
|
|
209
|
+
declare function parseColorString(color: string): {
|
|
210
|
+
r: number;
|
|
211
|
+
g: number;
|
|
212
|
+
b: number;
|
|
213
|
+
a: number;
|
|
214
|
+
};
|
|
215
|
+
export declare const _internals: {
|
|
216
|
+
rationalToFloat: typeof rationalToFloat;
|
|
217
|
+
clamp01: typeof clamp01;
|
|
218
|
+
colorStopsToFloat32Array: typeof colorStopsToFloat32Array;
|
|
219
|
+
positionsToFloat32Array: typeof positionsToFloat32Array;
|
|
220
|
+
mapTileMode: typeof mapTileMode;
|
|
221
|
+
parseColorString: typeof parseColorString;
|
|
222
|
+
};
|
|
223
|
+
export {};
|