text-shaper 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Multi-channel Signed Distance Field (MSDF) rasterizer
3
+ *
4
+ * MSDF uses three channels (RGB) to encode distance information,
5
+ * allowing sharp corners to be preserved when scaling.
6
+ *
7
+ * Algorithm:
8
+ * 1. Decompose glyph outline into edges (lines, quadratics, cubics)
9
+ * 2. Color edges so adjacent edges at sharp corners have different colors
10
+ * 3. For each pixel, compute signed distance to nearest edge of each color
11
+ * 4. Store R, G, B distances in output bitmap
12
+ * 5. Shader reconstructs using: median(r, g, b)
13
+ *
14
+ * Reference: https://github.com/Chlumsky/msdfgen
15
+ */
16
+ import type { Font } from "../font/font.ts";
17
+ import type { GlyphPath } from "../render/path.ts";
18
+ import { type Bitmap, type GlyphAtlas, type MsdfAtlasOptions } from "./types.ts";
19
+ /**
20
+ * A 2D point
21
+ */
22
+ export interface Point {
23
+ x: number;
24
+ y: number;
25
+ }
26
+ /**
27
+ * Edge color channels for MSDF
28
+ * 0 = red, 1 = green, 2 = blue
29
+ */
30
+ export type EdgeColor = 0 | 1 | 2;
31
+ /**
32
+ * An edge with color assignment
33
+ */
34
+ export type MsdfEdge = {
35
+ type: "line";
36
+ p0: Point;
37
+ p1: Point;
38
+ color: EdgeColor;
39
+ } | {
40
+ type: "quadratic";
41
+ p0: Point;
42
+ p1: Point;
43
+ p2: Point;
44
+ color: EdgeColor;
45
+ } | {
46
+ type: "cubic";
47
+ p0: Point;
48
+ p1: Point;
49
+ p2: Point;
50
+ p3: Point;
51
+ color: EdgeColor;
52
+ };
53
+ /**
54
+ * Signed distance result with parameter t
55
+ */
56
+ export interface SignedDistanceResult {
57
+ distance: number;
58
+ t: number;
59
+ }
60
+ /**
61
+ * Options for MSDF rendering
62
+ */
63
+ export interface MsdfOptions {
64
+ /** Width in pixels */
65
+ width: number;
66
+ /** Height in pixels */
67
+ height: number;
68
+ /** Scale factor (font units to pixels) */
69
+ scale: number;
70
+ /** X offset in pixels */
71
+ offsetX?: number;
72
+ /** Y offset in pixels */
73
+ offsetY?: number;
74
+ /** Flip Y axis (font coords are Y-up, bitmap is Y-down) */
75
+ flipY?: boolean;
76
+ /** Spread/radius - how far the distance field extends in pixels (default: 8) */
77
+ spread?: number;
78
+ }
79
+ /**
80
+ * Return the median of three numbers
81
+ */
82
+ export declare function median(a: number, b: number, c: number): number;
83
+ /**
84
+ * Compute signed distance from point to line segment
85
+ */
86
+ export declare function signedDistanceToLine(px: number, py: number, p0: Point, p1: Point): SignedDistanceResult;
87
+ /**
88
+ * Compute signed distance from point to quadratic bezier curve
89
+ * Uses Newton-Raphson iteration to find closest point
90
+ */
91
+ export declare function signedDistanceToQuadratic(px: number, py: number, p0: Point, p1: Point, p2: Point): SignedDistanceResult;
92
+ /**
93
+ * Compute signed distance from point to cubic bezier curve
94
+ * Uses Newton-Raphson iteration with multiple starting points
95
+ */
96
+ export declare function signedDistanceToCubic(px: number, py: number, p0: Point, p1: Point, p2: Point, p3: Point): SignedDistanceResult;
97
+ /**
98
+ * Assign colors to edges based on corner angles
99
+ * At sharp corners, adjacent edges get different colors
100
+ */
101
+ export declare function assignEdgeColors(contours: MsdfEdge[][]): void;
102
+ /**
103
+ * Render a glyph path as a multi-channel signed distance field
104
+ */
105
+ export declare function renderMsdf(path: GlyphPath, options: MsdfOptions): Bitmap;
106
+ /**
107
+ * Build an MSDF texture atlas from a set of glyphs
108
+ */
109
+ export declare function buildMsdfAtlas(font: Font, glyphIds: number[], options: MsdfAtlasOptions): GlyphAtlas;
110
+ /**
111
+ * Build MSDF atlas for ASCII printable characters (32-126)
112
+ */
113
+ export declare function buildMsdfAsciiAtlas(font: Font, options: MsdfAtlasOptions): GlyphAtlas;
114
+ /**
115
+ * Build MSDF atlas for a specific string (including all unique glyphs)
116
+ */
117
+ export declare function buildMsdfStringAtlas(font: Font, text: string, options: MsdfAtlasOptions): GlyphAtlas;
118
+ /**
119
+ * Export MSDF atlas as RGB texture data
120
+ */
121
+ export declare function msdfAtlasToRGB(atlas: GlyphAtlas): Uint8Array;
122
+ /**
123
+ * Export MSDF atlas as RGBA texture data
124
+ */
125
+ export declare function msdfAtlasToRGBA(atlas: GlyphAtlas): Uint8Array;
@@ -160,6 +160,21 @@ export interface AtlasOptions {
160
160
  /** Enable hinting */
161
161
  hinting?: boolean;
162
162
  }
163
+ /**
164
+ * Options for building an MSDF atlas
165
+ */
166
+ export interface MsdfAtlasOptions {
167
+ /** Font size in pixels (size of each glyph cell in the atlas) */
168
+ fontSize: number;
169
+ /** Padding between glyphs */
170
+ padding?: number;
171
+ /** Maximum atlas width */
172
+ maxWidth?: number;
173
+ /** Maximum atlas height */
174
+ maxHeight?: number;
175
+ /** SDF spread/radius in pixels (default: 4) */
176
+ spread?: number;
177
+ }
163
178
  /**
164
179
  * Create an empty bitmap
165
180
  */
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Outline Transform Operations
3
+ *
4
+ * Provides geometric transformations for glyph outlines:
5
+ * - 90° rotation
6
+ * - Power-of-2 scaling
7
+ * - 2D affine transforms (translate, rotate, scale, shear)
8
+ * - 3D perspective transforms
9
+ * - Bounding box computation
10
+ */
11
+ import type { GlyphPath } from "./path.ts";
12
+ /**
13
+ * 2D affine transformation matrix [a, b, c, d, tx, ty]
14
+ * Transforms point (x, y) to:
15
+ * x' = a*x + c*y + tx
16
+ * y' = b*x + d*y + ty
17
+ */
18
+ export type Matrix2D = [number, number, number, number, number, number];
19
+ /**
20
+ * 3x3 transformation matrix for 2D homogeneous coordinates
21
+ * | m00 m01 m02 | | x | | x' |
22
+ * | m10 m11 m12 | × | y | = | y' |
23
+ * | m20 m21 m22 | | 1 | | w |
24
+ *
25
+ * Final coordinates: (x'/w, y'/w)
26
+ */
27
+ export type Matrix3x3 = [
28
+ [
29
+ number,
30
+ number,
31
+ number
32
+ ],
33
+ [
34
+ number,
35
+ number,
36
+ number
37
+ ],
38
+ [
39
+ number,
40
+ number,
41
+ number
42
+ ]
43
+ ];
44
+ /**
45
+ * Axis-aligned bounding box
46
+ */
47
+ export interface BoundingBox {
48
+ xMin: number;
49
+ yMin: number;
50
+ xMax: number;
51
+ yMax: number;
52
+ }
53
+ /**
54
+ * Control box (bounding box of control points, not tight bounds)
55
+ */
56
+ export interface ControlBox extends BoundingBox {
57
+ }
58
+ /**
59
+ * Create identity 2D matrix
60
+ */
61
+ export declare function identity2D(): Matrix2D;
62
+ /**
63
+ * Create identity 3x3 matrix
64
+ */
65
+ export declare function identity3x3(): Matrix3x3;
66
+ /**
67
+ * Create translation matrix
68
+ */
69
+ export declare function translate2D(tx: number, ty: number): Matrix2D;
70
+ /**
71
+ * Create scale matrix
72
+ */
73
+ export declare function scale2D(sx: number, sy: number): Matrix2D;
74
+ /**
75
+ * Create rotation matrix (angle in radians)
76
+ */
77
+ export declare function rotate2D(angle: number): Matrix2D;
78
+ /**
79
+ * Create shear/skew matrix
80
+ */
81
+ export declare function shear2D(shearX: number, shearY: number): Matrix2D;
82
+ /**
83
+ * Multiply two 2D matrices: result = a × b
84
+ */
85
+ export declare function multiply2D(a: Matrix2D, b: Matrix2D): Matrix2D;
86
+ /**
87
+ * Multiply two 3x3 matrices: result = a × b
88
+ */
89
+ export declare function multiply3x3(a: Matrix3x3, b: Matrix3x3): Matrix3x3;
90
+ /**
91
+ * Transform a point using 2D matrix
92
+ */
93
+ export declare function transformPoint2D(x: number, y: number, m: Matrix2D): {
94
+ x: number;
95
+ y: number;
96
+ };
97
+ /**
98
+ * Transform a point using 3x3 matrix (with perspective division)
99
+ */
100
+ export declare function transformPoint3x3(x: number, y: number, m: Matrix3x3): {
101
+ x: number;
102
+ y: number;
103
+ };
104
+ /**
105
+ * Rotate outline by 90 degrees counter-clockwise around origin
106
+ * Optionally apply offset after rotation
107
+ */
108
+ export declare function rotateOutline90(path: GlyphPath, offsetX?: number, offsetY?: number): GlyphPath;
109
+ /**
110
+ * Scale outline by power of 2
111
+ * scaleOrdX/Y: shift amount (positive = enlarge, negative = shrink)
112
+ * e.g., scaleOrdX=1 means multiply x by 2, scaleOrdX=-1 means divide by 2
113
+ */
114
+ export declare function scaleOutlinePow2(path: GlyphPath, scaleOrdX: number, scaleOrdY: number): GlyphPath;
115
+ /**
116
+ * Apply 2D affine transformation to outline
117
+ */
118
+ export declare function transformOutline2D(path: GlyphPath, m: Matrix2D): GlyphPath;
119
+ /**
120
+ * Apply 3D perspective transformation to outline
121
+ * Uses homogeneous coordinates for perspective projection
122
+ */
123
+ export declare function transformOutline3D(path: GlyphPath, m: Matrix3x3): GlyphPath;
124
+ /**
125
+ * Compute control box (bounding box of all control points)
126
+ * This is faster than computing tight bounds but may be larger
127
+ */
128
+ export declare function computeControlBox(path: GlyphPath): ControlBox;
129
+ /**
130
+ * Compute tight bounding box (exact bounds considering curve extrema)
131
+ */
132
+ export declare function computeTightBounds(path: GlyphPath): BoundingBox;
133
+ /**
134
+ * Update control box with transformed outline
135
+ * Used for finding minimum X after 3D transform (like libass)
136
+ */
137
+ export declare function updateMinTransformedX(path: GlyphPath, m: Matrix3x3, currentMinX: number): number;
138
+ /**
139
+ * Translate outline by offset
140
+ */
141
+ export declare function translateOutline(path: GlyphPath, dx: number, dy: number): GlyphPath;
142
+ /**
143
+ * Scale outline uniformly
144
+ */
145
+ export declare function scaleOutline(path: GlyphPath, sx: number, sy?: number): GlyphPath;
146
+ /**
147
+ * Rotate outline by angle (in radians) around origin
148
+ */
149
+ export declare function rotateOutline(path: GlyphPath, angle: number): GlyphPath;
150
+ /**
151
+ * Apply italic/oblique shear to outline
152
+ * @param angle Italic angle in degrees (typically 12-15 for italic)
153
+ */
154
+ export declare function italicizeOutline(path: GlyphPath, angle: number): GlyphPath;
155
+ /**
156
+ * Create 3x3 perspective matrix
157
+ * @param vanishingPointX X coordinate of vanishing point
158
+ * @param vanishingPointY Y coordinate of vanishing point
159
+ * @param strength Perspective strength (0 = none, larger = more perspective)
160
+ */
161
+ export declare function perspectiveMatrix(vanishingPointX: number, vanishingPointY: number, strength: number): Matrix3x3;
162
+ /**
163
+ * Combine multiple paths into one
164
+ */
165
+ export declare function combinePaths(paths: GlyphPath[]): GlyphPath;
166
+ /**
167
+ * Clone a path
168
+ */
169
+ export declare function clonePath(path: GlyphPath): GlyphPath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "text-shaper",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "devDependencies": {
@@ -57,4 +57,4 @@
57
57
  },
58
58
  "type": "module",
59
59
  "types": "./dist/index.d.ts"
60
- }
60
+ }