text-shaper 0.1.1 → 0.1.3

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.
Files changed (77) hide show
  1. package/README.md +1 -0
  2. package/dist/aat/state-machine.d.ts +18 -6
  3. package/dist/buffer/glyph-buffer.d.ts +35 -1
  4. package/dist/buffer/unicode-buffer.d.ts +2 -0
  5. package/dist/fluent/bitmap-builder.d.ts +146 -0
  6. package/dist/fluent/index.d.ts +102 -0
  7. package/dist/fluent/path-builder.d.ts +230 -0
  8. package/dist/fluent/pipe.d.ts +200 -0
  9. package/dist/fluent/types.d.ts +93 -0
  10. package/dist/font/face.d.ts +2 -0
  11. package/dist/font/font.d.ts +14 -0
  12. package/dist/font/tables/cff.d.ts +3 -1
  13. package/dist/font/tables/colr.d.ts +4 -1
  14. package/dist/font/tables/fvar.d.ts +3 -1
  15. package/dist/font/tables/glyf.d.ts +13 -0
  16. package/dist/font/tables/gpos.d.ts +9 -1
  17. package/dist/font/tables/gsub.d.ts +11 -0
  18. package/dist/font/tables/gvar.d.ts +10 -1
  19. package/dist/font/tables/head.d.ts +5 -0
  20. package/dist/font/tables/hhea.d.ts +5 -0
  21. package/dist/font/tables/loca.d.ts +3 -0
  22. package/dist/font/tables/maxp.d.ts +5 -0
  23. package/dist/font/tables/name.d.ts +5 -0
  24. package/dist/font/tables/os2.d.ts +5 -0
  25. package/dist/font/tables/post.d.ts +5 -0
  26. package/dist/index.d.ts +12 -5
  27. package/dist/index.js +12 -10
  28. package/dist/index.js.map +104 -93
  29. package/dist/layout/justify.d.ts +15 -2
  30. package/dist/layout/structures/class-def.d.ts +61 -8
  31. package/dist/layout/structures/coverage.d.ts +57 -10
  32. package/dist/layout/structures/device.d.ts +26 -5
  33. package/dist/layout/structures/layout-common.d.ts +18 -3
  34. package/dist/layout/structures/set-digest.d.ts +59 -0
  35. package/dist/perf/benchmark-runner-eksh2b26.js +0 -0
  36. package/dist/perf/benchmark-runner.html +517 -0
  37. package/dist/perf/comparison-tests.js +22676 -0
  38. package/dist/perf/comparison-tests.js.map +111 -0
  39. package/dist/perf/cpu/cpu-perf.js +21920 -0
  40. package/dist/perf/cpu/cpu-perf.js.map +108 -0
  41. package/dist/perf/gpu/webgl-perf.js +470 -0
  42. package/dist/perf/gpu/webgl-perf.js.map +11 -0
  43. package/dist/perf/gpu/webgpu-perf.js +442 -0
  44. package/dist/perf/gpu/webgpu-perf.js.map +11 -0
  45. package/dist/perf/index.html +258 -0
  46. package/dist/perf/module-loader.js +18 -0
  47. package/dist/perf/utils/perf-utils.js +170 -0
  48. package/dist/perf/utils/perf-utils.js.map +10 -0
  49. package/dist/raster/asymmetric-stroke.d.ts +61 -0
  50. package/dist/raster/bitmap-utils.d.ts +124 -0
  51. package/dist/raster/blur.d.ts +11 -0
  52. package/dist/raster/cascade-blur.d.ts +44 -0
  53. package/dist/raster/cell.d.ts +24 -0
  54. package/dist/raster/fixed-point.d.ts +1 -1
  55. package/dist/raster/gradient.d.ts +15 -0
  56. package/dist/raster/gray-raster.d.ts +7 -1
  57. package/dist/raster/lcd-filter.d.ts +15 -0
  58. package/dist/raster/msdf.d.ts +135 -0
  59. package/dist/raster/outline-decompose.d.ts +16 -17
  60. package/dist/raster/rasterize.d.ts +19 -2
  61. package/dist/raster/sdf.d.ts +3 -0
  62. package/dist/raster/stroker.d.ts +3 -0
  63. package/dist/raster/types.d.ts +24 -0
  64. package/dist/render/outline-transform.d.ts +169 -0
  65. package/dist/render/path.d.ts +56 -1
  66. package/dist/shaper/complex/arabic.d.ts +1 -0
  67. package/dist/shaper/shape-plan.d.ts +11 -8
  68. package/dist/shaper/shaper.d.ts +61 -3
  69. package/dist/unicode/bidi/brackets.d.ts +15 -0
  70. package/dist/unicode/bidi/char-types.d.ts +4 -0
  71. package/dist/unicode/bidi/embedding-levels.d.ts +3 -0
  72. package/dist/unicode/bidi/mirroring.d.ts +10 -0
  73. package/dist/unicode/bidi/reordering.d.ts +15 -0
  74. package/dist/unicode/normalize.d.ts +23 -0
  75. package/dist/unicode/script.d.ts +17 -0
  76. package/dist/unicode/segmentation.d.ts +18 -0
  77. package/package.json +12 -3
@@ -0,0 +1,258 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Text Shaper Performance Tests</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ max-width: 800px;
11
+ margin: 0 auto;
12
+ padding: 40px 20px;
13
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
14
+ min-height: 100vh;
15
+ color: white;
16
+ }
17
+ .container {
18
+ background: rgba(255, 255, 255, 0.1);
19
+ backdrop-filter: blur(10px);
20
+ border-radius: 20px;
21
+ padding: 40px;
22
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
23
+ border: 1px solid rgba(255, 255, 255, 0.2);
24
+ }
25
+ h1 {
26
+ text-align: center;
27
+ margin-bottom: 10px;
28
+ font-size: 2.5rem;
29
+ font-weight: 700;
30
+ }
31
+ .subtitle {
32
+ text-align: center;
33
+ margin-bottom: 40px;
34
+ opacity: 0.9;
35
+ font-size: 1.1rem;
36
+ }
37
+ .feature-grid {
38
+ display: grid;
39
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
40
+ gap: 20px;
41
+ margin: 40px 0;
42
+ }
43
+ .feature-card {
44
+ background: rgba(255, 255, 255, 0.1);
45
+ border-radius: 15px;
46
+ padding: 25px;
47
+ text-align: center;
48
+ border: 1px solid rgba(255, 255, 255, 0.2);
49
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
50
+ }
51
+ .feature-card:hover {
52
+ transform: translateY(-5px);
53
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
54
+ }
55
+ .feature-icon {
56
+ font-size: 3rem;
57
+ margin-bottom: 15px;
58
+ display: block;
59
+ }
60
+ .feature-title {
61
+ font-size: 1.2rem;
62
+ font-weight: 600;
63
+ margin-bottom: 10px;
64
+ }
65
+ .feature-description {
66
+ opacity: 0.8;
67
+ font-size: 0.9rem;
68
+ line-height: 1.5;
69
+ }
70
+ .cta-section {
71
+ text-align: center;
72
+ margin-top: 40px;
73
+ }
74
+ .cta-button {
75
+ display: inline-block;
76
+ background: rgba(255, 255, 255, 0.2);
77
+ color: white;
78
+ padding: 15px 30px;
79
+ border-radius: 50px;
80
+ text-decoration: none;
81
+ font-weight: 600;
82
+ font-size: 1.1rem;
83
+ border: 2px solid rgba(255, 255, 255, 0.3);
84
+ transition: all 0.3s ease;
85
+ margin: 10px;
86
+ }
87
+ .cta-button:hover {
88
+ background: rgba(255, 255, 255, 0.3);
89
+ border-color: rgba(255, 255, 255, 0.5);
90
+ transform: translateY(-2px);
91
+ }
92
+ .tech-info {
93
+ background: rgba(0, 0, 0, 0.2);
94
+ border-radius: 15px;
95
+ padding: 20px;
96
+ margin: 30px 0;
97
+ border: 1px solid rgba(255, 255, 255, 0.1);
98
+ }
99
+ .tech-info h3 {
100
+ margin-top: 0;
101
+ color: #fff;
102
+ }
103
+ .tech-list {
104
+ display: flex;
105
+ flex-wrap: wrap;
106
+ gap: 10px;
107
+ margin-top: 15px;
108
+ }
109
+ .tech-tag {
110
+ background: rgba(255, 255, 255, 0.2);
111
+ padding: 5px 12px;
112
+ border-radius: 20px;
113
+ font-size: 0.85rem;
114
+ font-weight: 500;
115
+ }
116
+ .warning {
117
+ background: rgba(255, 193, 7, 0.2);
118
+ border: 1px solid rgba(255, 193, 7, 0.3);
119
+ color: #ffc107;
120
+ padding: 15px;
121
+ border-radius: 10px;
122
+ margin: 20px 0;
123
+ text-align: center;
124
+ }
125
+ </style>
126
+ </head>
127
+ <body>
128
+ <div class="container">
129
+ <h1>🚀 Text Shaper Performance Lab</h1>
130
+ <p class="subtitle">Benchmarking CPU vs GPU Performance for Text Rasterization</p>
131
+
132
+ <div class="feature-grid">
133
+ <div class="feature-card">
134
+ <span class="feature-icon">⚡</span>
135
+ <div class="feature-title">CPU Performance</div>
136
+ <div class="feature-description">
137
+ Test the performance of your current TypeScript implementation with various optimization levels
138
+ </div>
139
+ </div>
140
+ <div class="feature-card">
141
+ <span class="feature-icon">🎮</span>
142
+ <div class="feature-title">WebGL Acceleration</div>
143
+ <div class="feature-description">
144
+ Compare WebGL shader performance against CPU implementation for real-time rendering
145
+ </div>
146
+ </div>
147
+ <div class="feature-card">
148
+ <span class="feature-icon">🔥</span>
149
+ <div class="feature-title">WebGPU Compute</div>
150
+ <div class="feature-description">
151
+ Leverage modern GPU compute shaders for parallel text processing and SDF generation
152
+ </div>
153
+ </div>
154
+ <div class="feature-card">
155
+ <span class="feature-icon">📊</span>
156
+ <div class="feature-title">Detailed Analytics</div>
157
+ <div class="feature-description">
158
+ Get comprehensive performance reports with timing breakdowns and speedup analysis
159
+ </div>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="tech-info">
164
+ <h3>🛠️ Technologies Tested</h3>
165
+ <div class="tech-list">
166
+ <span class="tech-tag">TypeScript</span>
167
+ <span class="tech-tag">WebGL</span>
168
+ <span class="tech-tag">WebGPU</span>
169
+ <span class="tech-tag">SDF Generation</span>
170
+ <span class="tech-tag">Texture Atlas</span>
171
+ <span class="tech-tag">Glyph Rasterization</span>
172
+ <span class="tech-tag">Text Shaping</span>
173
+ <span class="tech-tag">Performance Benchmarking</span>
174
+ </div>
175
+ </div>
176
+
177
+ <div class="cta-section">
178
+ <a href="benchmark-runner.html" class="cta-button">
179
+ 🎯 Start Benchmarking
180
+ </a>
181
+ <a href="#" onclick="runQuickTest()" class="cta-button">
182
+ ⚡ Quick Test
183
+ </a>
184
+ </div>
185
+
186
+ <div id="quick-results" style="display: none; margin-top: 30px;">
187
+ <div class="tech-info">
188
+ <h3>🖥️ System Information</h3>
189
+ <div id="system-info"></div>
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ <script type="module">
195
+ import { getGPUInfo } from '../utils/perf-utils.js';
196
+
197
+ // Quick system check
198
+ async function runQuickTest() {
199
+ const resultsDiv = document.getElementById('quick-results');
200
+ const systemInfo = document.getElementById('system-info');
201
+
202
+ try {
203
+ const gpuInfo = await getGPUInfo();
204
+
205
+ systemInfo.innerHTML = `
206
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
207
+ <div>
208
+ <strong>GPU Vendor:</strong><br>
209
+ ${gpuInfo.vendor}
210
+ </div>
211
+ <div>
212
+ <strong>GPU Renderer:</strong><br>
213
+ ${gpuInfo.renderer}
214
+ </div>
215
+ <div>
216
+ <strong>WebGPU Support:</strong><br>
217
+ ${gpuInfo.webgpu ? '✅ Available' : '❌ Not Available'}
218
+ </div>
219
+ <div>
220
+ <strong>WebGL Support:</strong><br>
221
+ ${gpuInfo.webgl ? '✅ Available' : '❌ Not Available'}
222
+ </div>
223
+ </div>
224
+ <div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid rgba(255,255,255,0.2);">
225
+ <strong>Recommendation:</strong>
226
+ ${gpuInfo.webgpu ? 'Use WebGPU for best performance' :
227
+ gpuInfo.webgl ? 'Use WebGL for GPU acceleration' :
228
+ 'CPU-only benchmarks recommended'}
229
+ </div>
230
+ `;
231
+
232
+ resultsDiv.style.display = 'block';
233
+ } catch (error) {
234
+ systemInfo.innerHTML = `
235
+ <div class="warning">
236
+ Unable to detect GPU information. This might be due to browser security restrictions.
237
+ <br><br>
238
+ <strong>Try the full benchmark instead:</strong>
239
+ <br>
240
+ <a href="benchmark-runner.html" style="color: #ffc107; text-decoration: underline;">
241
+ Go to Benchmark Runner
242
+ </a>
243
+ </div>
244
+ `;
245
+ resultsDiv.style.display = 'block';
246
+ }
247
+ }
248
+
249
+ // Make it available globally
250
+ window.runQuickTest = runQuickTest;
251
+
252
+ // Auto-run quick test on load
253
+ window.addEventListener('load', () => {
254
+ setTimeout(runQuickTest, 1000);
255
+ });
256
+ </script>
257
+ </body>
258
+ </html>
@@ -0,0 +1,18 @@
1
+
2
+ // Performance test module loader
3
+ // This helps with module resolution in browser
4
+
5
+ // Export all modules globally for easy access
6
+ window.PerformanceTests = {
7
+ // Modules will be loaded by the HTML files
8
+ modules: new Map(),
9
+
10
+ loadModule: function(name, module) {
11
+ this.modules.set(name, module);
12
+ console.log('Loaded module:', name);
13
+ },
14
+
15
+ getModule: function(name) {
16
+ return this.modules.get(name);
17
+ }
18
+ };
@@ -0,0 +1,170 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, {
5
+ get: all[name],
6
+ enumerable: true,
7
+ configurable: true,
8
+ set: (newValue) => all[name] = () => newValue
9
+ });
10
+ };
11
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
12
+
13
+ // tests/performance/utils/perf-utils.ts
14
+ class PerformanceTimer {
15
+ startTime = 0;
16
+ endTime = 0;
17
+ start() {
18
+ this.startTime = performance.now();
19
+ }
20
+ end() {
21
+ this.endTime = performance.now();
22
+ return this.duration();
23
+ }
24
+ duration() {
25
+ return this.endTime - this.startTime;
26
+ }
27
+ static now() {
28
+ return performance.now();
29
+ }
30
+ }
31
+ async function benchmark(name, fn, options = {}) {
32
+ const {
33
+ iterations = 100,
34
+ warmupIterations = 10,
35
+ gcBetweenRuns = false
36
+ } = options;
37
+ for (let i = 0;i < warmupIterations; i++) {
38
+ await fn();
39
+ }
40
+ if (gcBetweenRuns && globalThis.gc) {
41
+ globalThis.gc();
42
+ }
43
+ const samples = [];
44
+ let totalTime = 0;
45
+ let minTime = Infinity;
46
+ let maxTime = -Infinity;
47
+ for (let i = 0;i < iterations; i++) {
48
+ const start = PerformanceTimer.now();
49
+ await fn();
50
+ const end = PerformanceTimer.now();
51
+ const duration = end - start;
52
+ samples.push(duration);
53
+ totalTime += duration;
54
+ minTime = Math.min(minTime, duration);
55
+ maxTime = Math.max(maxTime, duration);
56
+ if (i % 10 === 9) {
57
+ await new Promise((resolve) => setTimeout(resolve, 1));
58
+ }
59
+ }
60
+ return {
61
+ name,
62
+ iterations,
63
+ totalTime,
64
+ avgTime: totalTime / iterations,
65
+ minTime,
66
+ maxTime,
67
+ samples
68
+ };
69
+ }
70
+ async function comparePerformance(name, impl1, impl2, options = {}) {
71
+ const result1 = await benchmark(`${name}-impl1`, impl1, options);
72
+ const result2 = await benchmark(`${name}-impl2`, impl2, options);
73
+ const speedup = result1.avgTime / result2.avgTime;
74
+ const winner = result1.avgTime < result2.avgTime ? "impl1" : "impl2";
75
+ return {
76
+ impl1: result1,
77
+ impl2: result2,
78
+ speedup: Math.max(speedup, 1 / speedup),
79
+ winner
80
+ };
81
+ }
82
+
83
+ class MemoryTracker {
84
+ initialUsage = 0;
85
+ start() {
86
+ if (globalThis.performance?.memory) {
87
+ this.initialUsage = globalThis.performance.memory.usedJSHeapSize;
88
+ }
89
+ }
90
+ getUsage() {
91
+ if (globalThis.performance?.memory) {
92
+ return globalThis.performance.memory.usedJSHeapSize - this.initialUsage;
93
+ }
94
+ return 0;
95
+ }
96
+ format(bytes) {
97
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
98
+ }
99
+ }
100
+ function createPerformanceReport(results) {
101
+ const report = [];
102
+ report.push("=== Performance Benchmark Report ===");
103
+ report.push(`Generated: ${new Date().toISOString()}`);
104
+ report.push("");
105
+ results.forEach((result) => {
106
+ report.push(`${result.name}:`);
107
+ report.push(` Iterations: ${result.iterations}`);
108
+ report.push(` Average: ${result.avgTime.toFixed(3)} ms`);
109
+ report.push(` Min: ${result.minTime.toFixed(3)} ms`);
110
+ report.push(` Max: ${result.maxTime.toFixed(3)} ms`);
111
+ report.push(` Total: ${result.totalTime.toFixed(3)} ms`);
112
+ report.push("");
113
+ });
114
+ return report.join(`
115
+ `);
116
+ }
117
+ function isWebGPUSupported() {
118
+ return !!(navigator.gpu && navigator.gpu.requestAdapter);
119
+ }
120
+ function isWebGLSupported() {
121
+ try {
122
+ const canvas = document.createElement("canvas");
123
+ return !!(canvas.getContext("webgl") || canvas.getContext("webgl2"));
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+ async function getGPUInfo() {
129
+ const info = {
130
+ vendor: "Unknown",
131
+ renderer: "Unknown",
132
+ webgpu: isWebGPUSupported(),
133
+ webgl: isWebGLSupported()
134
+ };
135
+ if (info.webgpu) {
136
+ try {
137
+ const adapter = await navigator.gpu.requestAdapter();
138
+ if (adapter) {
139
+ info.vendor = adapter.info?.vendor || "Unknown";
140
+ info.renderer = adapter.info?.architecture || "Unknown";
141
+ }
142
+ } catch {}
143
+ }
144
+ if (info.webgl && info.vendor === "Unknown") {
145
+ try {
146
+ const canvas = document.createElement("canvas");
147
+ const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
148
+ if (gl) {
149
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
150
+ if (debugInfo) {
151
+ info.vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
152
+ info.renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
153
+ }
154
+ }
155
+ } catch {}
156
+ }
157
+ return info;
158
+ }
159
+ export {
160
+ isWebGPUSupported,
161
+ isWebGLSupported,
162
+ getGPUInfo,
163
+ createPerformanceReport,
164
+ comparePerformance,
165
+ benchmark,
166
+ PerformanceTimer,
167
+ MemoryTracker
168
+ };
169
+
170
+ //# debugId=BE03333C43482BE064756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../tests/performance/utils/perf-utils.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Performance testing utilities for browser-based benchmarks\n */\n\nexport interface PerformanceResult {\n name: string;\n iterations: number;\n totalTime: number;\n avgTime: number;\n minTime: number;\n maxTime: number;\n samples: number[];\n}\n\nexport interface BenchmarkOptions {\n iterations?: number;\n warmupIterations?: number;\n gcBetweenRuns?: boolean;\n}\n\n/**\n * High-precision timing utility\n */\nexport class PerformanceTimer {\n private startTime = 0;\n private endTime = 0;\n\n start(): void {\n this.startTime = performance.now();\n }\n\n end(): number {\n this.endTime = performance.now();\n return this.duration();\n }\n\n duration(): number {\n return this.endTime - this.startTime;\n }\n\n static now(): number {\n return performance.now();\n }\n}\n\n/**\n * Run a performance benchmark on a function\n */\nexport async function benchmark(\n name: string,\n fn: () => void | Promise<void>,\n options: BenchmarkOptions = {}\n): Promise<PerformanceResult> {\n const {\n iterations = 100,\n warmupIterations = 10,\n gcBetweenRuns = false\n } = options;\n\n // Warmup\n for (let i = 0; i < warmupIterations; i++) {\n await fn();\n }\n\n // Force GC if available and requested\n if (gcBetweenRuns && (globalThis as any).gc) {\n (globalThis as any).gc();\n }\n\n const samples: number[] = [];\n let totalTime = 0;\n let minTime = Infinity;\n let maxTime = -Infinity;\n\n // Run benchmark\n for (let i = 0; i < iterations; i++) {\n const start = PerformanceTimer.now();\n await fn();\n const end = PerformanceTimer.now();\n \n const duration = end - start;\n samples.push(duration);\n totalTime += duration;\n minTime = Math.min(minTime, duration);\n maxTime = Math.max(maxTime, duration);\n\n // Small delay between iterations to prevent overheating\n if (i % 10 === 9) {\n await new Promise(resolve => setTimeout(resolve, 1));\n }\n }\n\n return {\n name,\n iterations,\n totalTime,\n avgTime: totalTime / iterations,\n minTime,\n maxTime,\n samples\n };\n}\n\n/**\n * Compare performance between two implementations\n */\nexport async function comparePerformance(\n name: string,\n impl1: () => void | Promise<void>,\n impl2: () => void | Promise<void>,\n options: BenchmarkOptions = {}\n): Promise<{\n impl1: PerformanceResult;\n impl2: PerformanceResult;\n speedup: number;\n winner: string;\n}> {\n const result1 = await benchmark(`${name}-impl1`, impl1, options);\n const result2 = await benchmark(`${name}-impl2`, impl2, options);\n\n const speedup = result1.avgTime / result2.avgTime;\n const winner = result1.avgTime < result2.avgTime ? 'impl1' : 'impl2';\n\n return {\n impl1: result1,\n impl2: result2,\n speedup: Math.max(speedup, 1 / speedup),\n winner\n };\n}\n\n/**\n * Memory usage tracker\n */\nexport class MemoryTracker {\n private initialUsage: number = 0;\n\n start(): void {\n if ((globalThis as any).performance?.memory) {\n this.initialUsage = (globalThis as any).performance.memory.usedJSHeapSize;\n }\n }\n\n getUsage(): number {\n if ((globalThis as any).performance?.memory) {\n return (globalThis as any).performance.memory.usedJSHeapSize - this.initialUsage;\n }\n return 0;\n }\n\n format(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`;\n }\n}\n\n/**\n * Create a performance report\n */\nexport function createPerformanceReport(results: PerformanceResult[]): string {\n const report: string[] = [];\n \n report.push('=== Performance Benchmark Report ===');\n report.push(`Generated: ${new Date().toISOString()}`);\n report.push('');\n\n results.forEach(result => {\n report.push(`${result.name}:`);\n report.push(` Iterations: ${result.iterations}`);\n report.push(` Average: ${result.avgTime.toFixed(3)} ms`);\n report.push(` Min: ${result.minTime.toFixed(3)} ms`);\n report.push(` Max: ${result.maxTime.toFixed(3)} ms`);\n report.push(` Total: ${result.totalTime.toFixed(3)} ms`);\n report.push('');\n });\n\n return report.join('\\n');\n}\n\n/**\n * WebGPU support detection\n */\nexport function isWebGPUSupported(): boolean {\n return !!(navigator.gpu && navigator.gpu.requestAdapter);\n}\n\n/**\n * WebGL support detection\n */\nexport function isWebGLSupported(): boolean {\n try {\n const canvas = document.createElement('canvas');\n return !!(canvas.getContext('webgl') || canvas.getContext('webgl2'));\n } catch {\n return false;\n }\n}\n\n/**\n * Get GPU information\n */\nexport async function getGPUInfo(): Promise<{\n vendor: string;\n renderer: string;\n webgpu: boolean;\n webgl: boolean;\n}> {\n const info = {\n vendor: 'Unknown',\n renderer: 'Unknown',\n webgpu: isWebGPUSupported(),\n webgl: isWebGLSupported()\n };\n\n // Try WebGPU\n if (info.webgpu) {\n try {\n const adapter = await navigator.gpu.requestAdapter();\n if (adapter) {\n info.vendor = adapter.info?.vendor || 'Unknown';\n info.renderer = adapter.info?.architecture || 'Unknown';\n }\n } catch {\n // Ignore\n }\n }\n\n // Fallback to WebGL\n if (info.webgl && info.vendor === 'Unknown') {\n try {\n const canvas = document.createElement('canvas');\n const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');\n if (gl) {\n const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\n if (debugInfo) {\n info.vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) as string;\n info.renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) as string;\n }\n }\n } catch {\n // Ignore\n }\n }\n\n return info;\n}"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "BE03333C43482BE064756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Asymmetric Stroke
3
+ *
4
+ * Generates stroked outlines with independent X and Y border widths.
5
+ * This enables effects like directional shadows, stretched borders,
6
+ * and other asymmetric outline effects.
7
+ *
8
+ * The algorithm:
9
+ * 1. Flatten curves to polylines with configurable precision
10
+ * 2. For each segment, compute offset vectors scaled by (xBorder, yBorder)
11
+ * 3. Handle line joins (miter, round, bevel)
12
+ * 4. Generate both inner and outer contours for closed paths
13
+ * 5. Handle caps for open paths
14
+ */
15
+ import type { GlyphPath } from "../render/path.ts";
16
+ /**
17
+ * Options for asymmetric stroking
18
+ */
19
+ export interface AsymmetricStrokeOptions {
20
+ /** X-axis border width (in font units) */
21
+ xBorder: number;
22
+ /** Y-axis border width (in font units) */
23
+ yBorder: number;
24
+ /** Precision for curve flattening (smaller = more accurate, default: 1) */
25
+ eps?: number;
26
+ /** Line join style (default: "round") */
27
+ lineJoin?: "miter" | "round" | "bevel";
28
+ /** Miter limit for miter joins (default: 4) */
29
+ miterLimit?: number;
30
+ }
31
+ /**
32
+ * Stroke a path with asymmetric X/Y borders
33
+ * For filled text with border: combine outer outline with original fill,
34
+ * or use outer outline alone for hollow border effect
35
+ * @param path Input path to stroke
36
+ * @param options Stroke options including xBorder and yBorder
37
+ * @returns Two paths: outer (positive offset) and inner (negative offset)
38
+ */
39
+ export declare function strokeAsymmetric(path: GlyphPath, options: AsymmetricStrokeOptions): {
40
+ outer: GlyphPath;
41
+ inner: GlyphPath;
42
+ };
43
+ /**
44
+ * Create a combined stroke path (both inner and outer as single path)
45
+ * This creates a ring/donut shape that can be filled
46
+ * @param path Input path to stroke
47
+ * @param options Stroke options including xBorder and yBorder
48
+ * @returns Single path containing both outer and inner borders as a fillable ring
49
+ */
50
+ export declare function strokeAsymmetricCombined(path: GlyphPath, options: AsymmetricStrokeOptions): GlyphPath;
51
+ /**
52
+ * Stroke with uniform border (convenience function)
53
+ * @param path Input path to stroke
54
+ * @param border Border width in font units (applied to both X and Y)
55
+ * @param options Additional stroke options (precision, line join, miter limit)
56
+ * @returns Two paths: outer and inner borders
57
+ */
58
+ export declare function strokeUniform(path: GlyphPath, border: number, options?: Omit<AsymmetricStrokeOptions, "xBorder" | "yBorder">): {
59
+ outer: GlyphPath;
60
+ inner: GlyphPath;
61
+ };
@@ -5,21 +5,145 @@ import { type Bitmap, PixelMode } from "./types.ts";
5
5
  /**
6
6
  * Embolden a bitmap by dilating pixel values
7
7
  * Makes text bolder by spreading coverage in x and y directions
8
+ * @param bitmap Source bitmap to embolden
9
+ * @param xStrength Horizontal dilation strength in pixels
10
+ * @param yStrength Vertical dilation strength in pixels
11
+ * @returns New bitmap with emboldened content
8
12
  */
9
13
  export declare function emboldenBitmap(bitmap: Bitmap, xStrength: number, yStrength: number): Bitmap;
10
14
  /**
11
15
  * Convert bitmap between pixel modes
16
+ * @param bitmap Source bitmap to convert
17
+ * @param targetMode Target pixel format
18
+ * @returns New bitmap in the target format
12
19
  */
13
20
  export declare function convertBitmap(bitmap: Bitmap, targetMode: PixelMode): Bitmap;
14
21
  /**
15
22
  * Alpha blend src bitmap onto dst bitmap at position (x, y)
23
+ * @param dst Destination bitmap to blend onto (modified in place)
24
+ * @param src Source bitmap to blend
25
+ * @param x X position in destination
26
+ * @param y Y position in destination
27
+ * @param opacity Blend opacity from 0 to 1
16
28
  */
17
29
  export declare function blendBitmap(dst: Bitmap, src: Bitmap, x: number, y: number, opacity: number): void;
18
30
  /**
19
31
  * Create a deep copy of a bitmap
32
+ * @param bitmap Bitmap to copy
33
+ * @returns New bitmap with copied data
20
34
  */
21
35
  export declare function copyBitmap(bitmap: Bitmap): Bitmap;
22
36
  /**
23
37
  * Resize bitmap using nearest-neighbor interpolation
38
+ * @param bitmap Source bitmap to resize
39
+ * @param newWidth Target width in pixels
40
+ * @param newHeight Target height in pixels
41
+ * @returns New bitmap resized to target dimensions
24
42
  */
25
43
  export declare function resizeBitmap(bitmap: Bitmap, newWidth: number, newHeight: number): Bitmap;
44
+ /**
45
+ * Resize bitmap using bilinear interpolation
46
+ * Produces smoother results than nearest-neighbor, ideal for downsampling
47
+ * @param bitmap Source bitmap to resize
48
+ * @param newWidth Target width in pixels
49
+ * @param newHeight Target height in pixels
50
+ * @returns New bitmap resized with smooth interpolation
51
+ */
52
+ export declare function resizeBitmapBilinear(bitmap: Bitmap, newWidth: number, newHeight: number): Bitmap;
53
+ /**
54
+ * Add two bitmaps together (additive blend)
55
+ * Result: dst = clamp(dst + src, 0, 255)
56
+ * Used for combining glyph with shadow/glow
57
+ * @param dst Destination bitmap (modified in place)
58
+ * @param src Source bitmap to add
59
+ * @param srcX X offset of source in destination (default: 0)
60
+ * @param srcY Y offset of source in destination (default: 0)
61
+ */
62
+ export declare function addBitmaps(dst: Bitmap, src: Bitmap, srcX?: number, srcY?: number): void;
63
+ /**
64
+ * Multiply two bitmaps (multiplicative blend)
65
+ * Result: dst = (dst * src) / 255
66
+ * Used for masking operations
67
+ * @param dst Destination bitmap (modified in place)
68
+ * @param src Source bitmap to multiply
69
+ * @param srcX X offset of source in destination (default: 0)
70
+ * @param srcY Y offset of source in destination (default: 0)
71
+ */
72
+ export declare function mulBitmaps(dst: Bitmap, src: Bitmap, srcX?: number, srcY?: number): void;
73
+ /**
74
+ * Subtract src from dst (subtractive blend)
75
+ * Result: dst = clamp(dst - src, 0, 255)
76
+ * Used for outline effects
77
+ * @param dst Destination bitmap (modified in place)
78
+ * @param src Source bitmap to subtract
79
+ * @param srcX X offset of source in destination (default: 0)
80
+ * @param srcY Y offset of source in destination (default: 0)
81
+ */
82
+ export declare function subBitmaps(dst: Bitmap, src: Bitmap, srcX?: number, srcY?: number): void;
83
+ /**
84
+ * Alpha composite src over dst using src as alpha
85
+ * Result: dst = src + dst * (1 - src/255)
86
+ * Standard Porter-Duff "over" operation
87
+ * @param dst Destination bitmap (modified in place)
88
+ * @param src Source bitmap to composite
89
+ * @param srcX X offset of source in destination (default: 0)
90
+ * @param srcY Y offset of source in destination (default: 0)
91
+ */
92
+ export declare function compositeBitmaps(dst: Bitmap, src: Bitmap, srcX?: number, srcY?: number): void;
93
+ /**
94
+ * Shift bitmap position by integer offset
95
+ * Creates a new bitmap with the content shifted
96
+ * @param bitmap Source bitmap to shift
97
+ * @param shiftX Horizontal shift in pixels
98
+ * @param shiftY Vertical shift in pixels
99
+ * @returns New bitmap with shifted content
100
+ */
101
+ export declare function shiftBitmap(bitmap: Bitmap, shiftX: number, shiftY: number): Bitmap;
102
+ /**
103
+ * Fix outline bitmap by removing glyph interior
104
+ * Used when you want only the border, not the filled shape
105
+ * Result: outline = outline - glyph (where glyph coverage > threshold)
106
+ * @param outlineBitmap Outline bitmap to fix (modified in place)
107
+ * @param glyphBitmap Glyph bitmap containing filled shape
108
+ * @param glyphX X position of glyph in outline (default: 0)
109
+ * @param glyphY Y position of glyph in outline (default: 0)
110
+ * @param threshold Coverage threshold for removal (default: 128)
111
+ */
112
+ export declare function fixOutline(outlineBitmap: Bitmap, glyphBitmap: Bitmap, glyphX?: number, glyphY?: number, threshold?: number): void;
113
+ /**
114
+ * Maximum blend: take the maximum of two bitmaps
115
+ * Result: dst = max(dst, src)
116
+ * Used for combining multiple layers
117
+ * @param dst Destination bitmap (modified in place)
118
+ * @param src Source bitmap to compare
119
+ * @param srcX X offset of source in destination (default: 0)
120
+ * @param srcY Y offset of source in destination (default: 0)
121
+ */
122
+ export declare function maxBitmaps(dst: Bitmap, src: Bitmap, srcX?: number, srcY?: number): void;
123
+ /**
124
+ * Create a padded copy of a bitmap with extra space around edges
125
+ * Useful before blur operations to prevent edge artifacts
126
+ * @param bitmap Source bitmap to pad
127
+ * @param padLeft Left padding in pixels
128
+ * @param padTop Top padding in pixels
129
+ * @param padRight Right padding in pixels
130
+ * @param padBottom Bottom padding in pixels
131
+ * @returns New bitmap with padding added
132
+ */
133
+ export declare function padBitmap(bitmap: Bitmap, padLeft: number, padTop: number, padRight: number, padBottom: number): Bitmap;
134
+ /**
135
+ * Create an expanded bitmap that can contain both dst and src
136
+ * Returns the expanded bitmap and the offsets for both original bitmaps
137
+ * @param dst Destination bitmap
138
+ * @param src Source bitmap to fit
139
+ * @param srcX X position of source relative to destination
140
+ * @param srcY Y position of source relative to destination
141
+ * @returns Expanded bitmap and offset positions for both original bitmaps
142
+ */
143
+ export declare function expandToFit(dst: Bitmap, src: Bitmap, srcX: number, srcY: number): {
144
+ expanded: Bitmap;
145
+ dstOffsetX: number;
146
+ dstOffsetY: number;
147
+ srcOffsetX: number;
148
+ srcOffsetY: number;
149
+ };