imagescript 1.3.1 → 3.1.0
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/LICENSE +4 -1
- package/README.md +76 -26
- package/THIRD_PARTY_NOTICES.md +156 -0
- package/dist/animation.d.ts +55 -0
- package/dist/animation.js +167 -0
- package/dist/codecs/crc.d.ts +9 -0
- package/dist/codecs/crc.js +29 -0
- package/dist/codecs/exif.d.ts +11 -0
- package/dist/codecs/exif.js +27 -0
- package/dist/codecs/gif.d.ts +44 -0
- package/dist/codecs/gif.js +401 -0
- package/dist/codecs/jpeg.d.ts +35 -0
- package/dist/codecs/jpeg.js +980 -0
- package/dist/codecs/png.d.ts +97 -0
- package/dist/codecs/png.js +664 -0
- package/dist/codecs/quantize.d.ts +25 -0
- package/dist/codecs/quantize.js +302 -0
- package/dist/codecs/tga.d.ts +16 -0
- package/dist/codecs/tga.js +203 -0
- package/dist/codecs/tiff.d.ts +6 -0
- package/dist/codecs/tiff.js +546 -0
- package/dist/codecs/vp8-tables.d.ts +10 -0
- package/dist/codecs/vp8-tables.js +17 -0
- package/dist/codecs/vp8.d.ts +8 -0
- package/dist/codecs/vp8.js +711 -0
- package/dist/codecs/vp8l.d.ts +62 -0
- package/dist/codecs/vp8l.js +909 -0
- package/dist/codecs/webp.d.ts +67 -0
- package/dist/codecs/webp.js +280 -0
- package/dist/codecs/zlib.d.ts +22 -0
- package/dist/codecs/zlib.js +607 -0
- package/dist/frame.d.ts +57 -0
- package/dist/frame.js +65 -0
- package/dist/gif.d.ts +31 -0
- package/dist/gif.js +20 -0
- package/dist/image-type.d.ts +24 -0
- package/dist/image-type.js +64 -0
- package/dist/image.d.ts +553 -0
- package/dist/image.js +1156 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +20 -0
- package/dist/package.json +1 -0
- package/dist/render/font.d.ts +92 -0
- package/dist/render/font.js +954 -0
- package/dist/render/hinting.d.ts +110 -0
- package/dist/render/hinting.js +1236 -0
- package/dist/render/svg.d.ts +11 -0
- package/dist/render/svg.js +1717 -0
- package/dist/render/text.d.ts +63 -0
- package/dist/render/text.js +342 -0
- package/dist/text-layout.d.ts +46 -0
- package/dist/text-layout.js +35 -0
- package/dist/util/blur.d.ts +13 -0
- package/dist/util/blur.js +146 -0
- package/dist/util/mem.d.ts +10 -0
- package/dist/util/mem.js +23 -0
- package/dist/util/resample.d.ts +12 -0
- package/dist/util/resample.js +99 -0
- package/dist/util/utf8.d.ts +5 -0
- package/dist/util/utf8.js +55 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/licenses/Apache-2.0.txt +202 -0
- package/licenses/FTL.txt +169 -0
- package/licenses/README.ijg +260 -0
- package/package.json +31 -7
- package/ImageScript.d.ts +0 -954
- package/ImageScript.js +0 -1669
- package/bench/_.mjs +0 -166
- package/bench/all.mjs +0 -4
- package/bench/blur.mjs +0 -59
- package/bench/flip.mjs +0 -63
- package/bench/overlay.mjs +0 -35
- package/bench/package.json +0 -7
- package/bench/readme.md +0 -64
- package/bench/resize.mjs +0 -52
- package/codecs/LICENSE +0 -21
- package/codecs/lib.d.ts +0 -50
- package/codecs/lib.js +0 -6
- package/codecs/lib.mjs +0 -1
- package/codecs/node/bin/arm64-darwin.node +0 -0
- package/codecs/node/bin/arm64-linux.node +0 -0
- package/codecs/node/bin/x64-darwin.node +0 -0
- package/codecs/node/bin/x64-linux.node +0 -0
- package/codecs/node/bin/x64-win32.node +0 -0
- package/codecs/node/index.js +0 -3
- package/codecs/node/index.mjs +0 -6
- package/codecs/package.json +0 -14
- package/codecs/readme.md +0 -38
- package/codecs/wasm/bin/codecs.wasm +0 -0
- package/codecs/wasm/bin/simd.wasm +0 -0
- package/codecs/wasm/bundle/browser.js +0 -1
- package/codecs/wasm/index.js +0 -1
- package/codecs/wasm/index.mjs +0 -1
- package/png/README.md +0 -1
- package/png/node.js +0 -1137
- package/png/src/crc.mjs +0 -49
- package/png/src/mem.mjs +0 -24
- package/png/src/png.mjs +0 -254
- package/png/src/zlib.mjs +0 -618
- package/tsconfig.json +0 -21
- package/utils/mem.js +0 -26
- package/v2/codecs/magic.js +0 -25
- package/v2/framebuffer.d.ts +0 -67
- package/v2/framebuffer.js +0 -2352
- package/v2/framebuffer.mjs +0 -126
- package/v2/framebuffer.rs +0 -1264
- package/v2/index.d.ts +0 -84
- package/v2/index.js +0 -261
- package/v2/ops/blur.mjs +0 -303
- package/v2/ops/color.mjs +0 -277
- package/v2/ops/crop.mjs +0 -64
- package/v2/ops/fill.mjs +0 -33
- package/v2/ops/flip.mjs +0 -33
- package/v2/ops/index.mjs +0 -9
- package/v2/ops/iterator.mjs +0 -34
- package/v2/ops/overlay.mjs +0 -63
- package/v2/ops/resize.mjs +0 -166
- package/v2/ops/rotate.mjs +0 -117
- package/v2/util/mem.mjs +0 -24
- package/wasm/any/font.wasm +0 -0
- package/wasm/any/gif.wasm +0 -0
- package/wasm/any/jpeg.wasm +0 -0
- package/wasm/any/png.wasm +0 -0
- package/wasm/any/svg.wasm +0 -0
- package/wasm/any/tiff.wasm +0 -0
- package/wasm/node/font.js +0 -142
- package/wasm/node/gif.js +0 -76
- package/wasm/node/jpeg.js +0 -92
- package/wasm/node/png.js +0 -49
- package/wasm/node/svg.js +0 -56
- package/wasm/node/tiff.js +0 -47
|
@@ -0,0 +1,1236 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* TrueType hinting interpreter. Where the TrueType specification leaves details open, its behavior and parts of its
|
|
3
|
+
* logic follow FreeType's TrueType interpreter (src/truetype/ttinterp.c): fixed point rounding, original distances
|
|
4
|
+
* on the unscaled outline (IP, IUP, MDRP, MD), the IUP interpolation, the movement along near-perpendicular vectors
|
|
5
|
+
* and the tolerance of invalid references. Portions of this software are copyright © 2026 The FreeType Project
|
|
6
|
+
* (https://freetype.org). All rights reserved. FreeType License, see THIRD_PARTY_NOTICES.md and licenses/FTL.txt
|
|
7
|
+
*/
|
|
8
|
+
import { contourToPath } from "./font.js";
|
|
9
|
+
class HintError extends Error {
|
|
10
|
+
}
|
|
11
|
+
class SkipInstruction extends Error {
|
|
12
|
+
}
|
|
13
|
+
const roundHalfAway = (v) => v < 0 ? -Math.round(-v) : Math.round(v);
|
|
14
|
+
const mulFix = (a, b) => roundHalfAway(a * b / 65536);
|
|
15
|
+
const divFix = (a, b) => b === 0 ? 0 : roundHalfAway(a * 65536 / b);
|
|
16
|
+
const ROUND_GRID = { period: 64, phase: 0, threshold: 32 };
|
|
17
|
+
const defaultState = () => ({
|
|
18
|
+
pv: { x: 1, y: 0 }, fv: { x: 1, y: 0 }, dv: { x: 1, y: 0 },
|
|
19
|
+
rp0: 0, rp1: 0, rp2: 0, zp0: 1, zp1: 1, zp2: 1, loop: 1,
|
|
20
|
+
minimumDistance: 64, cvtCutIn: 68, singleWidthCutIn: 0, singleWidth: 0,
|
|
21
|
+
deltaBase: 9, deltaShift: 3, autoFlip: true, round: ROUND_GRID, instructControl: 0
|
|
22
|
+
});
|
|
23
|
+
function makeZone(count) {
|
|
24
|
+
return {
|
|
25
|
+
curX: new Float64Array(count), curY: new Float64Array(count), orgX: new Float64Array(count), orgY: new Float64Array(count),
|
|
26
|
+
orusX: new Float64Array(count), orusY: new Float64Array(count),
|
|
27
|
+
touchX: new Uint8Array(count), touchY: new Uint8Array(count), on: new Uint8Array(count), ends: []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const MAX_INSTRUCTIONS = 1_000_000;
|
|
31
|
+
export class Hinter {
|
|
32
|
+
font;
|
|
33
|
+
fpgm;
|
|
34
|
+
prep;
|
|
35
|
+
cvt;
|
|
36
|
+
maxStack;
|
|
37
|
+
maxStorage;
|
|
38
|
+
maxTwilight;
|
|
39
|
+
sizes = new Map();
|
|
40
|
+
scaleFixed = 0;
|
|
41
|
+
constructor(font) {
|
|
42
|
+
this.font = font;
|
|
43
|
+
const table = (tag) => {
|
|
44
|
+
const t = font.tables[tag];
|
|
45
|
+
return t ? font.data.subarray(t.offset, t.offset + t.length) : new Uint8Array(0);
|
|
46
|
+
};
|
|
47
|
+
this.fpgm = table('fpgm');
|
|
48
|
+
this.prep = table('prep');
|
|
49
|
+
const cvt = table('cvt ');
|
|
50
|
+
this.cvt = new Int16Array(cvt.length >> 1);
|
|
51
|
+
for (let i = 0; i < this.cvt.length; i++)
|
|
52
|
+
this.cvt[i] = font.i16(font.tables['cvt '].offset + i * 2);
|
|
53
|
+
const maxp = font.tables.maxp.offset;
|
|
54
|
+
const version = font.u32(maxp);
|
|
55
|
+
this.maxTwilight = version >= 0x10000 ? font.u16(maxp + 16) : 0;
|
|
56
|
+
this.maxStorage = version >= 0x10000 ? font.u16(maxp + 18) : 0;
|
|
57
|
+
this.maxStack = version >= 0x10000 ? font.u16(maxp + 24) : 0;
|
|
58
|
+
}
|
|
59
|
+
get hasInstructions() {
|
|
60
|
+
return this.font.outlines === 'glyf' && (this.fpgm.length > 0 || this.prep.length > 0 || this.cvt.length > 0);
|
|
61
|
+
}
|
|
62
|
+
size(ppem) {
|
|
63
|
+
let size = this.sizes.get(ppem);
|
|
64
|
+
if (size)
|
|
65
|
+
return size;
|
|
66
|
+
const scaleFixed = divFix(ppem * 64, this.font.unitsPerEm);
|
|
67
|
+
const scale = scaleFixed / 65536;
|
|
68
|
+
size = {
|
|
69
|
+
ppem, scale, scaleFixed,
|
|
70
|
+
cvt: Float64Array.from(this.cvt, v => mulFix(v, scaleFixed)),
|
|
71
|
+
storage: new Int32Array(Math.max(this.maxStorage, 64)),
|
|
72
|
+
state: defaultState(),
|
|
73
|
+
twilight: makeZone(Math.max(this.maxTwilight, 1)),
|
|
74
|
+
functions: new Map(),
|
|
75
|
+
instructions: new Map(),
|
|
76
|
+
broken: false
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
const fpgm = new Execution(this, size, size.twilight, makeZone(0));
|
|
80
|
+
fpgm.run(this.fpgm);
|
|
81
|
+
const prep = new Execution(this, size, size.twilight, makeZone(0));
|
|
82
|
+
prep.run(this.prep);
|
|
83
|
+
size.state = { ...prep.gs, pv: { ...prep.gs.pv }, fv: { ...prep.gs.fv }, dv: { ...prep.gs.dv } };
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (!(err instanceof HintError))
|
|
87
|
+
throw err;
|
|
88
|
+
size.broken = true;
|
|
89
|
+
}
|
|
90
|
+
this.sizes.set(ppem, size);
|
|
91
|
+
return size;
|
|
92
|
+
}
|
|
93
|
+
glyph(glyph, size) {
|
|
94
|
+
const ppem = Math.round(size);
|
|
95
|
+
if (ppem < 1 || ppem > 1000)
|
|
96
|
+
return null;
|
|
97
|
+
const state = this.size(ppem);
|
|
98
|
+
if (state.broken)
|
|
99
|
+
return null;
|
|
100
|
+
try {
|
|
101
|
+
const outline = this.load(glyph, state, 0);
|
|
102
|
+
if (!outline)
|
|
103
|
+
return { path: [], advance: Math.round(this.font.advance(glyph) * state.scale / 64) };
|
|
104
|
+
const { zone, count } = outline;
|
|
105
|
+
const originX = zone.curX[count], advance = Math.round((zone.curX[count + 1] - originX) / 64);
|
|
106
|
+
const xs = new Float64Array(count), ys = new Float64Array(count);
|
|
107
|
+
for (let i = 0; i < count; i++) {
|
|
108
|
+
xs[i] = (zone.curX[i] - originX) / 64;
|
|
109
|
+
ys[i] = zone.curY[i] / 64;
|
|
110
|
+
}
|
|
111
|
+
const path = [];
|
|
112
|
+
let first = 0;
|
|
113
|
+
for (const last of zone.ends) {
|
|
114
|
+
contourToPath(path, xs, ys, zone.on, first, last);
|
|
115
|
+
first = last + 1;
|
|
116
|
+
}
|
|
117
|
+
return { path, advance };
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (!(err instanceof HintError))
|
|
121
|
+
throw err;
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
load(glyph, size, depth) {
|
|
126
|
+
if (depth > 8)
|
|
127
|
+
throw new HintError('composite glyphs nested too deep');
|
|
128
|
+
const font = this.font;
|
|
129
|
+
const data = font.glyfPoints(glyph);
|
|
130
|
+
if (!data)
|
|
131
|
+
return null;
|
|
132
|
+
const scale = size.scale, scaleFixed = this.scaleFixed = size.scaleFixed;
|
|
133
|
+
const lsb = font.leftSideBearing(glyph), advance = font.advance(glyph);
|
|
134
|
+
let count, zone;
|
|
135
|
+
if (data.components) {
|
|
136
|
+
const parts = [];
|
|
137
|
+
const placed = [];
|
|
138
|
+
let total = 0;
|
|
139
|
+
const xsAll = [], ysAll = [], on = [], ends = [];
|
|
140
|
+
let useMetrics = null;
|
|
141
|
+
for (const component of data.components) {
|
|
142
|
+
const part = this.load(component.glyph, size, depth + 1);
|
|
143
|
+
if (!part)
|
|
144
|
+
continue;
|
|
145
|
+
const [a, b, c, d] = component.matrix;
|
|
146
|
+
const transformed = a !== 1 || b !== 0 || c !== 0 || d !== 1;
|
|
147
|
+
let dx, dy;
|
|
148
|
+
if (component.match) {
|
|
149
|
+
const [parentPoint, childPoint] = component.match;
|
|
150
|
+
if (parentPoint >= total || childPoint >= part.count)
|
|
151
|
+
throw new HintError('invalid point matching');
|
|
152
|
+
const cx = part.zone.curX[childPoint], cy = part.zone.curY[childPoint];
|
|
153
|
+
dx = xsAll[parentPoint] - (a * cx + c * cy);
|
|
154
|
+
dy = ysAll[parentPoint] - (b * cx + d * cy);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
dx = mulFix(component.dx, scaleFixed);
|
|
158
|
+
dy = mulFix(component.dy, scaleFixed);
|
|
159
|
+
if (component.round) {
|
|
160
|
+
dx = Math.round(dx / 64) * 64;
|
|
161
|
+
dy = Math.round(dy / 64) * 64;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
placed.push({ dx, dy });
|
|
165
|
+
for (let i = 0; i < part.count; i++) {
|
|
166
|
+
const x = part.zone.curX[i], y = part.zone.curY[i];
|
|
167
|
+
xsAll.push(transformed ? a * x + c * y + dx : x + dx);
|
|
168
|
+
ysAll.push(transformed ? b * x + d * y + dy : y + dy);
|
|
169
|
+
on.push(part.zone.on[i]);
|
|
170
|
+
}
|
|
171
|
+
for (const end of part.zone.ends)
|
|
172
|
+
ends.push(end + total);
|
|
173
|
+
total += part.count;
|
|
174
|
+
parts.push(part);
|
|
175
|
+
if (component.useMetrics)
|
|
176
|
+
useMetrics = part;
|
|
177
|
+
}
|
|
178
|
+
count = total;
|
|
179
|
+
zone = makeZone(count + 4);
|
|
180
|
+
zone.ends = ends;
|
|
181
|
+
zone.curX.set(xsAll);
|
|
182
|
+
zone.curY.set(ysAll);
|
|
183
|
+
zone.orgX.set(xsAll);
|
|
184
|
+
zone.orgY.set(ysAll);
|
|
185
|
+
for (let i = 0; i < count; i++) {
|
|
186
|
+
zone.orusX[i] = xsAll[i] / scale;
|
|
187
|
+
zone.orusY[i] = ysAll[i] / scale;
|
|
188
|
+
}
|
|
189
|
+
zone.on.set(on);
|
|
190
|
+
this.phantoms(zone, count, data.xMin - lsb, advance, useMetrics);
|
|
191
|
+
if (data.instructions.length && !(size.state.instructControl & 1)) {
|
|
192
|
+
new Execution(this, size, size.twilight, zone).run(data.instructions, true);
|
|
193
|
+
}
|
|
194
|
+
return { zone, count };
|
|
195
|
+
}
|
|
196
|
+
count = data.xs.length;
|
|
197
|
+
zone = makeZone(count + 4);
|
|
198
|
+
zone.ends = data.ends;
|
|
199
|
+
for (let i = 0; i < count; i++) {
|
|
200
|
+
zone.orusX[i] = data.xs[i];
|
|
201
|
+
zone.orusY[i] = data.ys[i];
|
|
202
|
+
zone.orgX[i] = mulFix(data.xs[i], scaleFixed);
|
|
203
|
+
zone.orgY[i] = mulFix(data.ys[i], scaleFixed);
|
|
204
|
+
zone.on[i] = data.on[i] ? 1 : 0;
|
|
205
|
+
}
|
|
206
|
+
zone.curX.set(zone.orgX);
|
|
207
|
+
zone.curY.set(zone.orgY);
|
|
208
|
+
this.phantoms(zone, count, data.xMin - lsb, advance, null);
|
|
209
|
+
if (data.instructions.length && !(size.state.instructControl & 1))
|
|
210
|
+
new Execution(this, size, size.twilight, zone).run(data.instructions, true);
|
|
211
|
+
return { zone, count };
|
|
212
|
+
}
|
|
213
|
+
phantoms(zone, count, origin, advance, metrics) {
|
|
214
|
+
const font = this.font;
|
|
215
|
+
const units = [origin, origin + advance, font.ascender, font.descender];
|
|
216
|
+
const org = units.map(v => mulFix(v, this.scaleFixed));
|
|
217
|
+
zone.orusX[count] = units[0];
|
|
218
|
+
zone.orusX[count + 1] = units[1];
|
|
219
|
+
zone.orusY[count + 2] = units[2];
|
|
220
|
+
zone.orusY[count + 3] = units[3];
|
|
221
|
+
const grid = (v) => Math.round(v / 64) * 64;
|
|
222
|
+
zone.orgX[count] = org[0];
|
|
223
|
+
zone.orgX[count + 1] = org[1];
|
|
224
|
+
zone.curX[count] = grid(org[0]);
|
|
225
|
+
zone.curX[count + 1] = grid(org[1]);
|
|
226
|
+
zone.orgY[count + 2] = org[2];
|
|
227
|
+
zone.orgY[count + 3] = org[3];
|
|
228
|
+
zone.curY[count + 2] = grid(org[2]);
|
|
229
|
+
zone.curY[count + 3] = grid(org[3]);
|
|
230
|
+
if (metrics) {
|
|
231
|
+
for (const k of [0, 1]) {
|
|
232
|
+
zone.orusX[count + k] = metrics.zone.orusX[metrics.count + k];
|
|
233
|
+
zone.orgX[count + k] = metrics.zone.orgX[metrics.count + k];
|
|
234
|
+
zone.curX[count + k] = metrics.zone.curX[metrics.count + k];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
class Execution {
|
|
240
|
+
hinter;
|
|
241
|
+
size;
|
|
242
|
+
gs;
|
|
243
|
+
zones;
|
|
244
|
+
stack = [];
|
|
245
|
+
callDepth = 0;
|
|
246
|
+
steps = 0;
|
|
247
|
+
isGlyph = false;
|
|
248
|
+
constructor(hinter, size, twilight, glyph) {
|
|
249
|
+
this.hinter = hinter;
|
|
250
|
+
this.size = size;
|
|
251
|
+
const s = size.state;
|
|
252
|
+
this.gs = { ...s, pv: { ...s.pv }, fv: { ...s.fv }, dv: { ...s.dv } };
|
|
253
|
+
this.zones = [twilight, glyph];
|
|
254
|
+
}
|
|
255
|
+
run(code, glyph = false) {
|
|
256
|
+
this.isGlyph = glyph;
|
|
257
|
+
if (glyph) {
|
|
258
|
+
const gs = this.gs;
|
|
259
|
+
gs.pv = { x: 1, y: 0 };
|
|
260
|
+
gs.fv = { x: 1, y: 0 };
|
|
261
|
+
gs.dv = { x: 1, y: 0 };
|
|
262
|
+
gs.rp0 = gs.rp1 = gs.rp2 = 0;
|
|
263
|
+
gs.zp0 = gs.zp1 = gs.zp2 = 1;
|
|
264
|
+
gs.loop = 1;
|
|
265
|
+
gs.round = ROUND_GRID;
|
|
266
|
+
const t = this.zones[0];
|
|
267
|
+
t.curX.fill(0);
|
|
268
|
+
t.curY.fill(0);
|
|
269
|
+
t.orgX.fill(0);
|
|
270
|
+
t.orgY.fill(0);
|
|
271
|
+
t.touchX.fill(0);
|
|
272
|
+
t.touchY.fill(0);
|
|
273
|
+
}
|
|
274
|
+
this.execute(code, 0, code.length);
|
|
275
|
+
}
|
|
276
|
+
pop() {
|
|
277
|
+
return this.stack.length ? this.stack.pop() : 0;
|
|
278
|
+
}
|
|
279
|
+
push(v) {
|
|
280
|
+
if (this.stack.length > Math.max(this.hinter.maxStack, 256) + 64)
|
|
281
|
+
throw new HintError('stack overflow');
|
|
282
|
+
this.stack.push(v);
|
|
283
|
+
}
|
|
284
|
+
zone(n) {
|
|
285
|
+
if (n !== 0 && n !== 1)
|
|
286
|
+
throw new SkipInstruction();
|
|
287
|
+
return this.zones[n];
|
|
288
|
+
}
|
|
289
|
+
point(zone, p) {
|
|
290
|
+
if (!(p >= 0 && p < zone.curX.length))
|
|
291
|
+
throw new SkipInstruction();
|
|
292
|
+
return p;
|
|
293
|
+
}
|
|
294
|
+
project(dx, dy) {
|
|
295
|
+
return dx * this.gs.pv.x + dy * this.gs.pv.y;
|
|
296
|
+
}
|
|
297
|
+
dualProject(dx, dy) {
|
|
298
|
+
return dx * this.gs.dv.x + dy * this.gs.dv.y;
|
|
299
|
+
}
|
|
300
|
+
moveVector() {
|
|
301
|
+
const { fv, pv } = this.gs;
|
|
302
|
+
const dot = fv.x * pv.x + fv.y * pv.y;
|
|
303
|
+
if (dot >= 0x3ffe / 0x4000)
|
|
304
|
+
return fv;
|
|
305
|
+
if (Math.abs(dot) < 0x1555 / 0x4000)
|
|
306
|
+
return { x: 0, y: 0 };
|
|
307
|
+
return { x: fv.x / dot, y: fv.y / dot };
|
|
308
|
+
}
|
|
309
|
+
move(zone, p, distance, touch = true) {
|
|
310
|
+
const { fv } = this.gs;
|
|
311
|
+
const mv = this.moveVector();
|
|
312
|
+
if (fv.x !== 0) {
|
|
313
|
+
zone.curX[p] += distance * mv.x;
|
|
314
|
+
if (touch)
|
|
315
|
+
zone.touchX[p] = 1;
|
|
316
|
+
}
|
|
317
|
+
if (fv.y !== 0) {
|
|
318
|
+
zone.curY[p] += distance * mv.y;
|
|
319
|
+
if (touch)
|
|
320
|
+
zone.touchY[p] = 1;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
round(d) {
|
|
324
|
+
const r = this.gs.round;
|
|
325
|
+
if (!r)
|
|
326
|
+
return d;
|
|
327
|
+
if (d >= 0) {
|
|
328
|
+
const v = Math.floor((d - r.phase + r.threshold) / r.period) * r.period + r.phase;
|
|
329
|
+
return v < 0 ? r.phase : v;
|
|
330
|
+
}
|
|
331
|
+
const v = -(Math.floor((-d - r.phase + r.threshold) / r.period) * r.period + r.phase);
|
|
332
|
+
return v > 0 ? -r.phase : v;
|
|
333
|
+
}
|
|
334
|
+
setVector(target, x, y) {
|
|
335
|
+
const length = Math.hypot(x, y);
|
|
336
|
+
if (length === 0)
|
|
337
|
+
return;
|
|
338
|
+
this.gs[target] = { x: x / length, y: y / length };
|
|
339
|
+
}
|
|
340
|
+
lineVector(p1, p2, perpendicular, original) {
|
|
341
|
+
const z1 = this.zone(this.gs.zp1), z2 = this.zone(this.gs.zp2);
|
|
342
|
+
this.point(z1, p2);
|
|
343
|
+
this.point(z2, p1);
|
|
344
|
+
let x = (original ? z1.orgX[p2] - z2.orgX[p1] : z1.curX[p2] - z2.curX[p1]);
|
|
345
|
+
let y = (original ? z1.orgY[p2] - z2.orgY[p1] : z1.curY[p2] - z2.curY[p1]);
|
|
346
|
+
if (x === 0 && y === 0) {
|
|
347
|
+
x = 1;
|
|
348
|
+
y = 0;
|
|
349
|
+
perpendicular = false;
|
|
350
|
+
}
|
|
351
|
+
if (perpendicular)
|
|
352
|
+
[x, y] = [-y, x];
|
|
353
|
+
const length = Math.hypot(x, y);
|
|
354
|
+
return { x: x / length, y: y / length };
|
|
355
|
+
}
|
|
356
|
+
originalDistance(za, a, zb, b, twilight = za === 0 || zb === 0, round = true) {
|
|
357
|
+
const A = this.zones[za], B = this.zones[zb];
|
|
358
|
+
if (twilight)
|
|
359
|
+
return this.dualProject(A.orgX[a] - B.orgX[b], A.orgY[a] - B.orgY[b]);
|
|
360
|
+
const d = this.dualProject(A.orusX[a] - B.orusX[b], A.orusY[a] - B.orusY[b]);
|
|
361
|
+
return round ? mulFix(d, this.size.scaleFixed) : d * this.size.scale;
|
|
362
|
+
}
|
|
363
|
+
loopCount() {
|
|
364
|
+
const n = this.gs.loop;
|
|
365
|
+
this.gs.loop = 1;
|
|
366
|
+
return n;
|
|
367
|
+
}
|
|
368
|
+
skip(code, ip, end, stopAtElse) {
|
|
369
|
+
let depth = 0;
|
|
370
|
+
while (ip < end) {
|
|
371
|
+
const op = code[ip];
|
|
372
|
+
if (op === 0x58)
|
|
373
|
+
depth++;
|
|
374
|
+
else if (op === 0x59) {
|
|
375
|
+
if (depth === 0)
|
|
376
|
+
return ip;
|
|
377
|
+
depth--;
|
|
378
|
+
}
|
|
379
|
+
else if (op === 0x1b && depth === 0 && stopAtElse)
|
|
380
|
+
return ip;
|
|
381
|
+
ip += instructionLength(code, ip);
|
|
382
|
+
}
|
|
383
|
+
throw new HintError('unterminated block');
|
|
384
|
+
}
|
|
385
|
+
execute(code, start, end) {
|
|
386
|
+
if (++this.callDepth > 64)
|
|
387
|
+
throw new HintError('calls nested too deep');
|
|
388
|
+
const gs = this.gs, size = this.size;
|
|
389
|
+
let ip = start;
|
|
390
|
+
while (ip < end) {
|
|
391
|
+
if (++this.steps > MAX_INSTRUCTIONS)
|
|
392
|
+
throw new HintError('too many instructions');
|
|
393
|
+
const op = code[ip];
|
|
394
|
+
const length = instructionLength(code, ip);
|
|
395
|
+
if (ip + length > end)
|
|
396
|
+
throw new HintError('truncated instruction');
|
|
397
|
+
let next = ip + length;
|
|
398
|
+
const idef = size.instructions.get(op);
|
|
399
|
+
if (idef) {
|
|
400
|
+
this.execute(idef.code, idef.start, idef.end);
|
|
401
|
+
ip = next;
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
switch (op) {
|
|
406
|
+
case 0x00:
|
|
407
|
+
case 0x01: {
|
|
408
|
+
const v = op & 1 ? { x: 1, y: 0 } : { x: 0, y: 1 };
|
|
409
|
+
gs.pv = v;
|
|
410
|
+
gs.fv = { ...v };
|
|
411
|
+
gs.dv = { ...v };
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
case 0x02:
|
|
415
|
+
case 0x03:
|
|
416
|
+
gs.pv = op & 1 ? { x: 1, y: 0 } : { x: 0, y: 1 };
|
|
417
|
+
gs.dv = { ...gs.pv };
|
|
418
|
+
break;
|
|
419
|
+
case 0x04:
|
|
420
|
+
case 0x05:
|
|
421
|
+
gs.fv = op & 1 ? { x: 1, y: 0 } : { x: 0, y: 1 };
|
|
422
|
+
break;
|
|
423
|
+
case 0x06:
|
|
424
|
+
case 0x07: {
|
|
425
|
+
const p2 = this.pop(), p1 = this.pop();
|
|
426
|
+
gs.pv = this.lineVector(p1, p2, (op & 1) === 1, false);
|
|
427
|
+
gs.dv = { ...gs.pv };
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
case 0x08:
|
|
431
|
+
case 0x09: {
|
|
432
|
+
const p2 = this.pop(), p1 = this.pop();
|
|
433
|
+
gs.fv = this.lineVector(p1, p2, (op & 1) === 1, false);
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
case 0x86:
|
|
437
|
+
case 0x87: {
|
|
438
|
+
const p2 = this.pop(), p1 = this.pop();
|
|
439
|
+
gs.pv = this.lineVector(p1, p2, (op & 1) === 1, false);
|
|
440
|
+
gs.dv = this.lineVector(p1, p2, (op & 1) === 1, true);
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
case 0x0a:
|
|
444
|
+
case 0x0b: {
|
|
445
|
+
const y = this.pop() << 16 >> 16, x = this.pop() << 16 >> 16;
|
|
446
|
+
if (op === 0x0a) {
|
|
447
|
+
this.setVector('pv', x, y);
|
|
448
|
+
gs.dv = { ...gs.pv };
|
|
449
|
+
}
|
|
450
|
+
else
|
|
451
|
+
this.setVector('fv', x, y);
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
case 0x0c:
|
|
455
|
+
case 0x0d: {
|
|
456
|
+
const v = op === 0x0c ? gs.pv : gs.fv;
|
|
457
|
+
this.push(Math.round(v.x * 16384));
|
|
458
|
+
this.push(Math.round(v.y * 16384));
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
case 0x0e:
|
|
462
|
+
gs.fv = { ...gs.pv };
|
|
463
|
+
break;
|
|
464
|
+
case 0x0f: {
|
|
465
|
+
const b1 = this.pop(), b0 = this.pop(), a1 = this.pop(), a0 = this.pop(), p = this.pop();
|
|
466
|
+
const za = this.zone(gs.zp1), zb = this.zone(gs.zp0), z = this.zone(gs.zp2);
|
|
467
|
+
[a0, a1].forEach(i => this.point(za, i));
|
|
468
|
+
[b0, b1].forEach(i => this.point(zb, i));
|
|
469
|
+
this.point(z, p);
|
|
470
|
+
const ax0 = za.curX[a0], ay0 = za.curY[a0], ax1 = za.curX[a1], ay1 = za.curY[a1];
|
|
471
|
+
const bx0 = zb.curX[b0], by0 = zb.curY[b0], bx1 = zb.curX[b1], by1 = zb.curY[b1];
|
|
472
|
+
const dax = ax1 - ax0, day = ay1 - ay0, dbx = bx1 - bx0, dby = by1 - by0;
|
|
473
|
+
const det = dax * dby - day * dbx;
|
|
474
|
+
if (Math.abs(det) < 1e-9) {
|
|
475
|
+
z.curX[p] = (ax0 + ax1 + bx0 + bx1) / 4;
|
|
476
|
+
z.curY[p] = (ay0 + ay1 + by0 + by1) / 4;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
const t = ((bx0 - ax0) * dby - (by0 - ay0) * dbx) / det;
|
|
480
|
+
z.curX[p] = ax0 + t * dax;
|
|
481
|
+
z.curY[p] = ay0 + t * day;
|
|
482
|
+
}
|
|
483
|
+
z.touchX[p] = z.touchY[p] = 1;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
case 0x10:
|
|
487
|
+
gs.rp0 = this.pop();
|
|
488
|
+
break;
|
|
489
|
+
case 0x11:
|
|
490
|
+
gs.rp1 = this.pop();
|
|
491
|
+
break;
|
|
492
|
+
case 0x12:
|
|
493
|
+
gs.rp2 = this.pop();
|
|
494
|
+
break;
|
|
495
|
+
case 0x13:
|
|
496
|
+
gs.zp0 = this.zoneNumber();
|
|
497
|
+
break;
|
|
498
|
+
case 0x14:
|
|
499
|
+
gs.zp1 = this.zoneNumber();
|
|
500
|
+
break;
|
|
501
|
+
case 0x15:
|
|
502
|
+
gs.zp2 = this.zoneNumber();
|
|
503
|
+
break;
|
|
504
|
+
case 0x16:
|
|
505
|
+
gs.zp0 = gs.zp1 = gs.zp2 = this.zoneNumber();
|
|
506
|
+
break;
|
|
507
|
+
case 0x17: {
|
|
508
|
+
const n = this.pop();
|
|
509
|
+
if (n < 0)
|
|
510
|
+
throw new HintError('invalid loop');
|
|
511
|
+
gs.loop = Math.min(n, 65535) || 1;
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
case 0x18:
|
|
515
|
+
gs.round = ROUND_GRID;
|
|
516
|
+
break;
|
|
517
|
+
case 0x19:
|
|
518
|
+
gs.round = { period: 64, phase: 32, threshold: 32 };
|
|
519
|
+
break;
|
|
520
|
+
case 0x3d:
|
|
521
|
+
gs.round = { period: 32, phase: 0, threshold: 16 };
|
|
522
|
+
break;
|
|
523
|
+
case 0x7d:
|
|
524
|
+
gs.round = { period: 64, phase: 0, threshold: 0 };
|
|
525
|
+
break;
|
|
526
|
+
case 0x7c:
|
|
527
|
+
gs.round = { period: 64, phase: 0, threshold: 63 };
|
|
528
|
+
break;
|
|
529
|
+
case 0x7a:
|
|
530
|
+
gs.round = null;
|
|
531
|
+
break;
|
|
532
|
+
case 0x76:
|
|
533
|
+
case 0x77: {
|
|
534
|
+
const n = this.pop();
|
|
535
|
+
const grid = op === 0x76 ? 64 : 64 / Math.SQRT2;
|
|
536
|
+
const period = [grid / 2, grid, grid * 2, grid][n >> 6 & 3];
|
|
537
|
+
const phase = [0, period / 4, period / 2, period * 3 / 4][n >> 4 & 3];
|
|
538
|
+
const t = n & 15;
|
|
539
|
+
gs.round = { period, phase, threshold: t === 0 ? period - 1 : (t - 4) * period / 8 };
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
case 0x1a:
|
|
543
|
+
gs.minimumDistance = this.pop();
|
|
544
|
+
break;
|
|
545
|
+
case 0x1d:
|
|
546
|
+
gs.cvtCutIn = this.pop();
|
|
547
|
+
break;
|
|
548
|
+
case 0x1e:
|
|
549
|
+
gs.singleWidthCutIn = this.pop();
|
|
550
|
+
break;
|
|
551
|
+
case 0x1f:
|
|
552
|
+
gs.singleWidth = mulFix(this.pop(), size.scaleFixed);
|
|
553
|
+
break;
|
|
554
|
+
case 0x4d:
|
|
555
|
+
gs.autoFlip = true;
|
|
556
|
+
break;
|
|
557
|
+
case 0x4e:
|
|
558
|
+
gs.autoFlip = false;
|
|
559
|
+
break;
|
|
560
|
+
case 0x5e:
|
|
561
|
+
gs.deltaBase = this.pop();
|
|
562
|
+
break;
|
|
563
|
+
case 0x5f:
|
|
564
|
+
gs.deltaShift = this.pop();
|
|
565
|
+
break;
|
|
566
|
+
case 0x85:
|
|
567
|
+
case 0x8d:
|
|
568
|
+
case 0x7e:
|
|
569
|
+
case 0x7f:
|
|
570
|
+
case 0x4f:
|
|
571
|
+
this.pop();
|
|
572
|
+
break;
|
|
573
|
+
case 0x8e: {
|
|
574
|
+
const selector = this.pop(), value = this.pop();
|
|
575
|
+
if (!this.isGlyph && selector >= 1 && selector <= 3) {
|
|
576
|
+
const bit = 1 << selector - 1;
|
|
577
|
+
gs.instructControl = value ? gs.instructControl | bit : gs.instructControl & ~bit;
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
case 0x20: {
|
|
582
|
+
const v = this.pop();
|
|
583
|
+
this.push(v);
|
|
584
|
+
this.push(v);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
case 0x21:
|
|
588
|
+
this.pop();
|
|
589
|
+
break;
|
|
590
|
+
case 0x22:
|
|
591
|
+
this.stack.length = 0;
|
|
592
|
+
break;
|
|
593
|
+
case 0x23: {
|
|
594
|
+
const b = this.pop(), a = this.pop();
|
|
595
|
+
this.push(b);
|
|
596
|
+
this.push(a);
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case 0x24:
|
|
600
|
+
this.push(this.stack.length);
|
|
601
|
+
break;
|
|
602
|
+
case 0x25: {
|
|
603
|
+
const k = this.pop();
|
|
604
|
+
if (k < 1 || k > this.stack.length)
|
|
605
|
+
throw new HintError('invalid index');
|
|
606
|
+
this.push(this.stack[this.stack.length - k]);
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
case 0x26: {
|
|
610
|
+
const k = this.pop();
|
|
611
|
+
if (k < 1 || k > this.stack.length)
|
|
612
|
+
throw new HintError('invalid index');
|
|
613
|
+
this.push(this.stack.splice(this.stack.length - k, 1)[0]);
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
case 0x8a: {
|
|
617
|
+
const a = this.pop(), b = this.pop(), c = this.pop();
|
|
618
|
+
this.push(b);
|
|
619
|
+
this.push(a);
|
|
620
|
+
this.push(c);
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case 0x40:
|
|
624
|
+
case 0x41:
|
|
625
|
+
case 0xb0:
|
|
626
|
+
case 0xb1:
|
|
627
|
+
case 0xb2:
|
|
628
|
+
case 0xb3:
|
|
629
|
+
case 0xb4:
|
|
630
|
+
case 0xb5:
|
|
631
|
+
case 0xb6:
|
|
632
|
+
case 0xb7:
|
|
633
|
+
case 0xb8:
|
|
634
|
+
case 0xb9:
|
|
635
|
+
case 0xba:
|
|
636
|
+
case 0xbb:
|
|
637
|
+
case 0xbc:
|
|
638
|
+
case 0xbd:
|
|
639
|
+
case 0xbe:
|
|
640
|
+
case 0xbf: {
|
|
641
|
+
const words = op === 0x41 || op >= 0xb8;
|
|
642
|
+
const count = op === 0x40 || op === 0x41 ? code[ip + 1] : (op & 7) + 1;
|
|
643
|
+
let p = ip + (op === 0x40 || op === 0x41 ? 2 : 1);
|
|
644
|
+
for (let i = 0; i < count; i++) {
|
|
645
|
+
if (words) {
|
|
646
|
+
this.push(code[p] << 24 >> 16 | code[p + 1]);
|
|
647
|
+
p += 2;
|
|
648
|
+
}
|
|
649
|
+
else
|
|
650
|
+
this.push(code[p++]);
|
|
651
|
+
}
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case 0x42: {
|
|
655
|
+
const v = this.pop(), l = this.pop();
|
|
656
|
+
this.storageIndex(l);
|
|
657
|
+
size.storage[l] = v;
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
case 0x43: {
|
|
661
|
+
const l = this.pop();
|
|
662
|
+
this.push(l >= 0 && l < size.storage.length ? size.storage[l] : 0);
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
case 0x44: {
|
|
666
|
+
const v = this.pop(), l = this.pop();
|
|
667
|
+
this.cvtIndex(l);
|
|
668
|
+
size.cvt[l] = v;
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
case 0x70: {
|
|
672
|
+
const v = this.pop(), l = this.pop();
|
|
673
|
+
this.cvtIndex(l);
|
|
674
|
+
size.cvt[l] = mulFix(v, size.scaleFixed);
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
case 0x45: {
|
|
678
|
+
const l = this.pop();
|
|
679
|
+
this.push(l >= 0 && l < size.cvt.length ? Math.round(size.cvt[l]) : 0);
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
case 0x46:
|
|
683
|
+
case 0x47: {
|
|
684
|
+
const p = this.pop(), z = this.zone(gs.zp2);
|
|
685
|
+
this.point(z, p);
|
|
686
|
+
this.push(Math.round(op === 0x46 ? this.project(z.curX[p], z.curY[p]) : this.dualProject(z.orgX[p], z.orgY[p])));
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
case 0x48: {
|
|
690
|
+
const value = this.pop(), p = this.pop(), z = this.zone(gs.zp2);
|
|
691
|
+
this.point(z, p);
|
|
692
|
+
this.move(z, p, value - this.project(z.curX[p], z.curY[p]));
|
|
693
|
+
if (gs.zp2 === 0) {
|
|
694
|
+
z.orgX[p] = z.curX[p];
|
|
695
|
+
z.orgY[p] = z.curY[p];
|
|
696
|
+
}
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
case 0x49:
|
|
700
|
+
case 0x4a: {
|
|
701
|
+
const k = this.pop(), l = this.pop();
|
|
702
|
+
const z0 = this.zone(gs.zp0), z1 = this.zone(gs.zp1);
|
|
703
|
+
this.point(z0, l);
|
|
704
|
+
this.point(z1, k);
|
|
705
|
+
const d = op & 1 ? this.project(z0.curX[l] - z1.curX[k], z0.curY[l] - z1.curY[k])
|
|
706
|
+
: this.originalDistance(gs.zp0, l, gs.zp1, k);
|
|
707
|
+
this.push(Math.round(d));
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
case 0x4b:
|
|
711
|
+
case 0x4c:
|
|
712
|
+
this.push(size.ppem);
|
|
713
|
+
break;
|
|
714
|
+
case 0x50: {
|
|
715
|
+
const b = this.pop(), a = this.pop();
|
|
716
|
+
this.push(a < b ? 1 : 0);
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
case 0x51: {
|
|
720
|
+
const b = this.pop(), a = this.pop();
|
|
721
|
+
this.push(a <= b ? 1 : 0);
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
case 0x52: {
|
|
725
|
+
const b = this.pop(), a = this.pop();
|
|
726
|
+
this.push(a > b ? 1 : 0);
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
729
|
+
case 0x53: {
|
|
730
|
+
const b = this.pop(), a = this.pop();
|
|
731
|
+
this.push(a >= b ? 1 : 0);
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
case 0x54: {
|
|
735
|
+
const b = this.pop(), a = this.pop();
|
|
736
|
+
this.push(a === b ? 1 : 0);
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
case 0x55: {
|
|
740
|
+
const b = this.pop(), a = this.pop();
|
|
741
|
+
this.push(a !== b ? 1 : 0);
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
case 0x56:
|
|
745
|
+
case 0x57: {
|
|
746
|
+
const v = this.round(this.pop()) >> 6 & 1;
|
|
747
|
+
this.push(op === 0x56 ? v : 1 - v);
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
case 0x5a: {
|
|
751
|
+
const b = this.pop(), a = this.pop();
|
|
752
|
+
this.push(a && b ? 1 : 0);
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
case 0x5b: {
|
|
756
|
+
const b = this.pop(), a = this.pop();
|
|
757
|
+
this.push(a || b ? 1 : 0);
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
case 0x5c:
|
|
761
|
+
this.push(this.pop() ? 0 : 1);
|
|
762
|
+
break;
|
|
763
|
+
case 0x60: {
|
|
764
|
+
const b = this.pop(), a = this.pop();
|
|
765
|
+
this.push(a + b);
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case 0x61: {
|
|
769
|
+
const b = this.pop(), a = this.pop();
|
|
770
|
+
this.push(a - b);
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
case 0x62: {
|
|
774
|
+
const b = this.pop(), a = this.pop();
|
|
775
|
+
if (b === 0)
|
|
776
|
+
throw new HintError('division by zero');
|
|
777
|
+
this.push(Math.trunc(a * 64 / b));
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
case 0x63: {
|
|
781
|
+
const b = this.pop(), a = this.pop();
|
|
782
|
+
const product = a * b;
|
|
783
|
+
this.push(product < 0 ? -Math.floor((-product + 32) / 64) : Math.floor((product + 32) / 64));
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
case 0x64:
|
|
787
|
+
this.push(Math.abs(this.pop()));
|
|
788
|
+
break;
|
|
789
|
+
case 0x65:
|
|
790
|
+
this.push(-this.pop());
|
|
791
|
+
break;
|
|
792
|
+
case 0x66:
|
|
793
|
+
this.push(Math.floor(this.pop() / 64) * 64);
|
|
794
|
+
break;
|
|
795
|
+
case 0x67:
|
|
796
|
+
this.push(Math.ceil(this.pop() / 64) * 64);
|
|
797
|
+
break;
|
|
798
|
+
case 0x8b: {
|
|
799
|
+
const b = this.pop(), a = this.pop();
|
|
800
|
+
this.push(Math.max(a, b));
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
case 0x8c: {
|
|
804
|
+
const b = this.pop(), a = this.pop();
|
|
805
|
+
this.push(Math.min(a, b));
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
case 0x68:
|
|
809
|
+
case 0x69:
|
|
810
|
+
case 0x6a:
|
|
811
|
+
case 0x6b:
|
|
812
|
+
this.push(Math.round(this.round(this.pop())));
|
|
813
|
+
break;
|
|
814
|
+
case 0x6c:
|
|
815
|
+
case 0x6d:
|
|
816
|
+
case 0x6e:
|
|
817
|
+
case 0x6f: break;
|
|
818
|
+
case 0x58: {
|
|
819
|
+
if (!this.pop()) {
|
|
820
|
+
const target = this.skip(code, next, end, true);
|
|
821
|
+
next = target + 1;
|
|
822
|
+
}
|
|
823
|
+
break;
|
|
824
|
+
}
|
|
825
|
+
case 0x1b:
|
|
826
|
+
next = this.skip(code, next, end, false) + 1;
|
|
827
|
+
break;
|
|
828
|
+
case 0x59: break;
|
|
829
|
+
case 0x1c:
|
|
830
|
+
next = ip + this.pop();
|
|
831
|
+
break;
|
|
832
|
+
case 0x78: {
|
|
833
|
+
const e = this.pop(), offset = this.pop();
|
|
834
|
+
if (e)
|
|
835
|
+
next = ip + offset;
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
case 0x79: {
|
|
839
|
+
const e = this.pop(), offset = this.pop();
|
|
840
|
+
if (!e)
|
|
841
|
+
next = ip + offset;
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
case 0x2c:
|
|
845
|
+
case 0x89: {
|
|
846
|
+
const n = this.pop();
|
|
847
|
+
let p = next;
|
|
848
|
+
while (p < end && code[p] !== 0x2d) {
|
|
849
|
+
if (code[p] === 0x2c || code[p] === 0x89)
|
|
850
|
+
throw new HintError('nested definition');
|
|
851
|
+
p += instructionLength(code, p);
|
|
852
|
+
}
|
|
853
|
+
if (p >= end)
|
|
854
|
+
throw new HintError('unterminated definition');
|
|
855
|
+
(op === 0x2c ? size.functions : size.instructions).set(n, { code, start: next, end: p });
|
|
856
|
+
next = p + 1;
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
case 0x2d: return void this.callDepth--;
|
|
860
|
+
case 0x2b:
|
|
861
|
+
case 0x2a: {
|
|
862
|
+
const f = this.pop();
|
|
863
|
+
const count = op === 0x2a ? this.pop() : 1;
|
|
864
|
+
const def = size.functions.get(f);
|
|
865
|
+
if (!def)
|
|
866
|
+
throw new HintError('undefined function');
|
|
867
|
+
for (let i = 0; i < count; i++)
|
|
868
|
+
this.execute(def.code, def.start, def.end);
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
case 0x88: {
|
|
872
|
+
const selector = this.pop();
|
|
873
|
+
let result = 0;
|
|
874
|
+
if (selector & 1)
|
|
875
|
+
result |= 35;
|
|
876
|
+
if (selector & 32)
|
|
877
|
+
result |= 1 << 12;
|
|
878
|
+
this.push(result);
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
881
|
+
case 0x2e:
|
|
882
|
+
case 0x2f: {
|
|
883
|
+
const p = this.pop(), z = this.zone(gs.zp0);
|
|
884
|
+
this.point(z, p);
|
|
885
|
+
const d = this.project(z.curX[p], z.curY[p]);
|
|
886
|
+
this.move(z, p, op & 1 ? this.round(d) - d : 0);
|
|
887
|
+
gs.rp0 = gs.rp1 = p;
|
|
888
|
+
break;
|
|
889
|
+
}
|
|
890
|
+
case 0x3e:
|
|
891
|
+
case 0x3f: {
|
|
892
|
+
const n = this.pop(), p = this.pop(), z = this.zone(gs.zp0);
|
|
893
|
+
this.point(z, p);
|
|
894
|
+
this.cvtIndex(n);
|
|
895
|
+
let distance = size.cvt[n];
|
|
896
|
+
if (gs.zp0 === 0) {
|
|
897
|
+
z.orgX[p] = z.curX[p] = distance * gs.fv.x;
|
|
898
|
+
z.orgY[p] = z.curY[p] = distance * gs.fv.y;
|
|
899
|
+
}
|
|
900
|
+
const current = this.project(z.curX[p], z.curY[p]);
|
|
901
|
+
if (op & 1) {
|
|
902
|
+
if (Math.abs(distance - current) > gs.cvtCutIn)
|
|
903
|
+
distance = current;
|
|
904
|
+
distance = this.round(distance);
|
|
905
|
+
}
|
|
906
|
+
this.move(z, p, distance - current);
|
|
907
|
+
gs.rp0 = gs.rp1 = p;
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
910
|
+
case 0x3a:
|
|
911
|
+
case 0x3b: {
|
|
912
|
+
const d = this.pop(), p = this.pop();
|
|
913
|
+
const z0 = this.zone(gs.zp0), z1 = this.zone(gs.zp1);
|
|
914
|
+
this.point(z0, gs.rp0);
|
|
915
|
+
this.point(z1, p);
|
|
916
|
+
if (gs.zp1 === 0) {
|
|
917
|
+
z1.orgX[p] = z0.orgX[gs.rp0];
|
|
918
|
+
z1.orgY[p] = z0.orgY[gs.rp0];
|
|
919
|
+
z1.curX[p] = z1.orgX[p];
|
|
920
|
+
z1.curY[p] = z1.orgY[p];
|
|
921
|
+
}
|
|
922
|
+
const distance = this.project(z1.curX[p] - z0.curX[gs.rp0], z1.curY[p] - z0.curY[gs.rp0]);
|
|
923
|
+
this.move(z1, p, d - distance);
|
|
924
|
+
gs.rp1 = gs.rp0;
|
|
925
|
+
gs.rp2 = p;
|
|
926
|
+
if (op & 1)
|
|
927
|
+
gs.rp0 = p;
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
case 0x32:
|
|
931
|
+
case 0x33:
|
|
932
|
+
case 0x34:
|
|
933
|
+
case 0x35:
|
|
934
|
+
case 0x36:
|
|
935
|
+
case 0x37: {
|
|
936
|
+
const refZoneNumber = op & 1 ? gs.zp0 : gs.zp1;
|
|
937
|
+
const refZone = this.zone(refZoneNumber), ref = op & 1 ? gs.rp1 : gs.rp2;
|
|
938
|
+
this.point(refZone, ref);
|
|
939
|
+
const d = this.project(refZone.curX[ref] - refZone.orgX[ref], refZone.curY[ref] - refZone.orgY[ref]);
|
|
940
|
+
const mv = this.moveVector();
|
|
941
|
+
const dx = d * mv.x, dy = d * mv.y;
|
|
942
|
+
const shift = (z, p, touch) => {
|
|
943
|
+
z.curX[p] += dx;
|
|
944
|
+
z.curY[p] += dy;
|
|
945
|
+
if (touch) {
|
|
946
|
+
if (gs.fv.x !== 0)
|
|
947
|
+
z.touchX[p] = 1;
|
|
948
|
+
if (gs.fv.y !== 0)
|
|
949
|
+
z.touchY[p] = 1;
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
if (op <= 0x33) {
|
|
953
|
+
const z = this.zone(gs.zp2);
|
|
954
|
+
for (let n = this.loopCount(); n > 0; n--)
|
|
955
|
+
shift(z, this.point(z, this.pop()), true);
|
|
956
|
+
}
|
|
957
|
+
else if (op <= 0x35) {
|
|
958
|
+
const c = this.pop(), z = this.zone(gs.zp2);
|
|
959
|
+
if (c < 0 || c >= z.ends.length)
|
|
960
|
+
throw new HintError('invalid contour');
|
|
961
|
+
const first = c === 0 ? 0 : z.ends[c - 1] + 1;
|
|
962
|
+
for (let p = first; p <= z.ends[c]; p++)
|
|
963
|
+
if (z !== refZone || p !== ref)
|
|
964
|
+
shift(z, p, true);
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
const z = this.zone(this.pop());
|
|
968
|
+
const limit = z === this.zones[1] ? z.curX.length - 4 : z.curX.length;
|
|
969
|
+
for (let p = 0; p < limit; p++)
|
|
970
|
+
if (z !== refZone || p !== ref)
|
|
971
|
+
shift(z, p, false);
|
|
972
|
+
}
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
case 0x38: {
|
|
976
|
+
const d = this.pop(), z = this.zone(gs.zp2);
|
|
977
|
+
for (let n = this.loopCount(); n > 0; n--) {
|
|
978
|
+
const p = this.point(z, this.pop());
|
|
979
|
+
z.curX[p] += d * gs.fv.x;
|
|
980
|
+
z.curY[p] += d * gs.fv.y;
|
|
981
|
+
if (gs.fv.x !== 0)
|
|
982
|
+
z.touchX[p] = 1;
|
|
983
|
+
if (gs.fv.y !== 0)
|
|
984
|
+
z.touchY[p] = 1;
|
|
985
|
+
}
|
|
986
|
+
break;
|
|
987
|
+
}
|
|
988
|
+
case 0x39: {
|
|
989
|
+
const z0 = this.zone(gs.zp0), z1 = this.zone(gs.zp1), z2 = this.zone(gs.zp2);
|
|
990
|
+
this.point(z0, gs.rp1);
|
|
991
|
+
this.point(z1, gs.rp2);
|
|
992
|
+
const curBaseX = z0.curX[gs.rp1], curBaseY = z0.curY[gs.rp1];
|
|
993
|
+
const twilight = gs.zp0 === 0 || gs.zp1 === 0 || gs.zp2 === 0;
|
|
994
|
+
const oldRange = this.originalDistance(gs.zp1, gs.rp2, gs.zp0, gs.rp1, twilight, false);
|
|
995
|
+
const curRange = this.project(z1.curX[gs.rp2] - curBaseX, z1.curY[gs.rp2] - curBaseY);
|
|
996
|
+
for (let n = this.loopCount(); n > 0; n--) {
|
|
997
|
+
const p = this.point(z2, this.pop());
|
|
998
|
+
const orgDistance = this.originalDistance(gs.zp2, p, gs.zp0, gs.rp1, twilight, false);
|
|
999
|
+
const curDistance = this.project(z2.curX[p] - curBaseX, z2.curY[p] - curBaseY);
|
|
1000
|
+
const newDistance = orgDistance === 0 ? 0 : oldRange !== 0 ? roundHalfAway(orgDistance * curRange / oldRange) : roundHalfAway(orgDistance);
|
|
1001
|
+
this.move(z2, p, newDistance - curDistance);
|
|
1002
|
+
}
|
|
1003
|
+
break;
|
|
1004
|
+
}
|
|
1005
|
+
case 0x3c: {
|
|
1006
|
+
const z0 = this.zone(gs.zp0), z1 = this.zone(gs.zp1);
|
|
1007
|
+
this.point(z0, gs.rp0);
|
|
1008
|
+
for (let n = this.loopCount(); n > 0; n--) {
|
|
1009
|
+
const p = this.point(z1, this.pop());
|
|
1010
|
+
this.move(z1, p, -this.project(z1.curX[p] - z0.curX[gs.rp0], z1.curY[p] - z0.curY[gs.rp0]));
|
|
1011
|
+
}
|
|
1012
|
+
break;
|
|
1013
|
+
}
|
|
1014
|
+
case 0x27: {
|
|
1015
|
+
const p2 = this.pop(), p1 = this.pop();
|
|
1016
|
+
const z0 = this.zone(gs.zp0), z1 = this.zone(gs.zp1);
|
|
1017
|
+
this.point(z0, p2);
|
|
1018
|
+
this.point(z1, p1);
|
|
1019
|
+
const d = this.project(z0.curX[p2] - z1.curX[p1], z0.curY[p2] - z1.curY[p1]) / 2;
|
|
1020
|
+
this.move(z1, p1, d);
|
|
1021
|
+
this.move(z0, p2, -d);
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
case 0x29: {
|
|
1025
|
+
const p = this.pop(), z = this.zone(gs.zp0);
|
|
1026
|
+
this.point(z, p);
|
|
1027
|
+
if (gs.fv.x !== 0)
|
|
1028
|
+
z.touchX[p] = 0;
|
|
1029
|
+
if (gs.fv.y !== 0)
|
|
1030
|
+
z.touchY[p] = 0;
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
case 0x30:
|
|
1034
|
+
case 0x31:
|
|
1035
|
+
this.interpolateUntouched(op === 0x31);
|
|
1036
|
+
break;
|
|
1037
|
+
case 0x80: {
|
|
1038
|
+
const z = this.zone(gs.zp0);
|
|
1039
|
+
for (let n = this.loopCount(); n > 0; n--) {
|
|
1040
|
+
const p = this.point(z, this.pop());
|
|
1041
|
+
z.on[p] ^= 1;
|
|
1042
|
+
}
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
case 0x81:
|
|
1046
|
+
case 0x82: {
|
|
1047
|
+
const high = this.pop(), low = this.pop(), z = this.zone(gs.zp0);
|
|
1048
|
+
this.point(z, high);
|
|
1049
|
+
this.point(z, low);
|
|
1050
|
+
for (let p = low; p <= high; p++)
|
|
1051
|
+
z.on[p] = op === 0x81 ? 1 : 0;
|
|
1052
|
+
break;
|
|
1053
|
+
}
|
|
1054
|
+
case 0x5d:
|
|
1055
|
+
case 0x71:
|
|
1056
|
+
case 0x72:
|
|
1057
|
+
case 0x73:
|
|
1058
|
+
case 0x74:
|
|
1059
|
+
case 0x75: {
|
|
1060
|
+
const n = this.pop();
|
|
1061
|
+
const range = op === 0x5d || op === 0x73 ? 0 : op === 0x71 || op === 0x74 ? 16 : 32;
|
|
1062
|
+
for (let k = 0; k < n; k++) {
|
|
1063
|
+
const target = this.pop(), arg = this.pop();
|
|
1064
|
+
if ((arg >> 4 & 15) + gs.deltaBase + range !== size.ppem)
|
|
1065
|
+
continue;
|
|
1066
|
+
let steps = (arg & 15) - 8;
|
|
1067
|
+
if (steps >= 0)
|
|
1068
|
+
steps++;
|
|
1069
|
+
const amount = steps * 64 / (1 << gs.deltaShift);
|
|
1070
|
+
if (op <= 0x72) {
|
|
1071
|
+
const z = this.zone(gs.zp0);
|
|
1072
|
+
this.move(z, this.point(z, target), amount);
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
this.cvtIndex(target);
|
|
1076
|
+
size.cvt[target] += amount;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
default:
|
|
1082
|
+
if (op >= 0xc0) {
|
|
1083
|
+
this.relativePoint(op);
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
throw new HintError(`unknown instruction 0x${op.toString(16)}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
catch (err) {
|
|
1090
|
+
if (!(err instanceof SkipInstruction))
|
|
1091
|
+
throw err;
|
|
1092
|
+
}
|
|
1093
|
+
ip = next;
|
|
1094
|
+
}
|
|
1095
|
+
this.callDepth--;
|
|
1096
|
+
}
|
|
1097
|
+
zoneNumber() {
|
|
1098
|
+
const n = this.pop();
|
|
1099
|
+
if (n !== 0 && n !== 1)
|
|
1100
|
+
throw new SkipInstruction();
|
|
1101
|
+
return n;
|
|
1102
|
+
}
|
|
1103
|
+
storageIndex(l) {
|
|
1104
|
+
if (!(l >= 0 && l < this.size.storage.length))
|
|
1105
|
+
throw new SkipInstruction();
|
|
1106
|
+
}
|
|
1107
|
+
cvtIndex(l) {
|
|
1108
|
+
if (!(l >= 0 && l < this.size.cvt.length))
|
|
1109
|
+
throw new SkipInstruction();
|
|
1110
|
+
}
|
|
1111
|
+
relativePoint(op) {
|
|
1112
|
+
const gs = this.gs, size = this.size;
|
|
1113
|
+
const indirect = op >= 0xe0;
|
|
1114
|
+
const setRp0 = (op & 16) !== 0, keepMinimum = (op & 8) !== 0, round = (op & 4) !== 0;
|
|
1115
|
+
const n = indirect ? this.pop() : -1;
|
|
1116
|
+
const p = this.pop();
|
|
1117
|
+
const z0 = this.zone(gs.zp0), z1 = this.zone(gs.zp1);
|
|
1118
|
+
if (!(p >= 0 && p < z1.curX.length && gs.rp0 >= 0 && gs.rp0 < z0.curX.length && (!indirect || n >= -1 && n < size.cvt.length))) {
|
|
1119
|
+
gs.rp1 = gs.rp0;
|
|
1120
|
+
gs.rp2 = p;
|
|
1121
|
+
if (setRp0)
|
|
1122
|
+
gs.rp0 = p;
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
const orgDistance = indirect ? this.dualProject(z1.orgX[p] - z0.orgX[gs.rp0], z1.orgY[p] - z0.orgY[gs.rp0])
|
|
1126
|
+
: this.originalDistance(gs.zp1, p, gs.zp0, gs.rp0);
|
|
1127
|
+
const curDistance = this.project(z1.curX[p] - z0.curX[gs.rp0], z1.curY[p] - z0.curY[gs.rp0]);
|
|
1128
|
+
let distance;
|
|
1129
|
+
if (indirect) {
|
|
1130
|
+
let cvt = n < 0 ? 0 : size.cvt[n];
|
|
1131
|
+
if (Math.abs(cvt - gs.singleWidth) < gs.singleWidthCutIn)
|
|
1132
|
+
cvt = cvt >= 0 ? gs.singleWidth : -gs.singleWidth;
|
|
1133
|
+
if (gs.zp1 === 0) {
|
|
1134
|
+
z1.orgX[p] = z0.orgX[gs.rp0] + cvt * gs.fv.x;
|
|
1135
|
+
z1.orgY[p] = z0.orgY[gs.rp0] + cvt * gs.fv.y;
|
|
1136
|
+
z1.curX[p] = z1.orgX[p];
|
|
1137
|
+
z1.curY[p] = z1.orgY[p];
|
|
1138
|
+
}
|
|
1139
|
+
if (gs.autoFlip && (orgDistance ^ cvt) < 0 && orgDistance !== 0 && cvt !== 0)
|
|
1140
|
+
cvt = -cvt;
|
|
1141
|
+
if (round) {
|
|
1142
|
+
if (gs.zp0 === gs.zp1 && Math.abs(cvt - orgDistance) > gs.cvtCutIn)
|
|
1143
|
+
cvt = orgDistance;
|
|
1144
|
+
distance = this.round(cvt);
|
|
1145
|
+
}
|
|
1146
|
+
else
|
|
1147
|
+
distance = cvt;
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
let d = orgDistance;
|
|
1151
|
+
if (Math.abs(d - gs.singleWidth) < gs.singleWidthCutIn)
|
|
1152
|
+
d = d >= 0 ? gs.singleWidth : -gs.singleWidth;
|
|
1153
|
+
distance = round ? this.round(d) : d;
|
|
1154
|
+
}
|
|
1155
|
+
if (keepMinimum) {
|
|
1156
|
+
if (orgDistance >= 0) {
|
|
1157
|
+
if (distance < gs.minimumDistance)
|
|
1158
|
+
distance = gs.minimumDistance;
|
|
1159
|
+
}
|
|
1160
|
+
else if (distance > -gs.minimumDistance)
|
|
1161
|
+
distance = -gs.minimumDistance;
|
|
1162
|
+
}
|
|
1163
|
+
const current = indirect && gs.zp1 === 0 ? this.project(z1.curX[p] - z0.curX[gs.rp0], z1.curY[p] - z0.curY[gs.rp0]) : curDistance;
|
|
1164
|
+
this.move(z1, p, distance - current);
|
|
1165
|
+
gs.rp1 = gs.rp0;
|
|
1166
|
+
gs.rp2 = p;
|
|
1167
|
+
if (setRp0)
|
|
1168
|
+
gs.rp0 = p;
|
|
1169
|
+
}
|
|
1170
|
+
interpolateUntouched(x) {
|
|
1171
|
+
const z = this.zones[1];
|
|
1172
|
+
const cur = x ? z.curX : z.curY, org = x ? z.orgX : z.orgY, orus = x ? z.orusX : z.orusY, touched = x ? z.touchX : z.touchY;
|
|
1173
|
+
let first = 0;
|
|
1174
|
+
const interpolate = (from, to, a, b) => {
|
|
1175
|
+
if (from > to)
|
|
1176
|
+
return;
|
|
1177
|
+
if (orus[a] > orus[b])
|
|
1178
|
+
[a, b] = [b, a];
|
|
1179
|
+
const org1 = org[a], org2 = org[b], cur1 = cur[a], cur2 = cur[b];
|
|
1180
|
+
const delta1 = cur1 - org1, delta2 = cur2 - org2;
|
|
1181
|
+
const flat = cur1 === cur2 || orus[a] === orus[b];
|
|
1182
|
+
const factor = flat ? 0 : divFix(cur2 - cur1, orus[b] - orus[a]);
|
|
1183
|
+
for (let p = from; p <= to; p++) {
|
|
1184
|
+
const o = org[p];
|
|
1185
|
+
if (o <= org1)
|
|
1186
|
+
cur[p] = o + delta1;
|
|
1187
|
+
else if (o >= org2)
|
|
1188
|
+
cur[p] = o + delta2;
|
|
1189
|
+
else
|
|
1190
|
+
cur[p] = flat ? cur1 : cur1 + mulFix(orus[p] - orus[a], factor);
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
for (const last of z.ends) {
|
|
1194
|
+
let p = first;
|
|
1195
|
+
while (p <= last && !touched[p])
|
|
1196
|
+
p++;
|
|
1197
|
+
if (p > last) {
|
|
1198
|
+
first = last + 1;
|
|
1199
|
+
continue;
|
|
1200
|
+
}
|
|
1201
|
+
const firstTouched = p;
|
|
1202
|
+
let current = p;
|
|
1203
|
+
for (p++; p <= last; p++) {
|
|
1204
|
+
if (!touched[p])
|
|
1205
|
+
continue;
|
|
1206
|
+
interpolate(current + 1, p - 1, current, p);
|
|
1207
|
+
current = p;
|
|
1208
|
+
}
|
|
1209
|
+
if (current === firstTouched) {
|
|
1210
|
+
const d = cur[current] - org[current];
|
|
1211
|
+
if (d !== 0)
|
|
1212
|
+
for (let i = first; i <= last; i++)
|
|
1213
|
+
if (i !== current)
|
|
1214
|
+
cur[i] += d;
|
|
1215
|
+
}
|
|
1216
|
+
else {
|
|
1217
|
+
interpolate(current + 1, last, current, firstTouched);
|
|
1218
|
+
if (firstTouched > first)
|
|
1219
|
+
interpolate(first, firstTouched - 1, current, firstTouched);
|
|
1220
|
+
}
|
|
1221
|
+
first = last + 1;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function instructionLength(code, ip) {
|
|
1226
|
+
const op = code[ip];
|
|
1227
|
+
if (op === 0x40)
|
|
1228
|
+
return 2 + code[ip + 1];
|
|
1229
|
+
if (op === 0x41)
|
|
1230
|
+
return 2 + 2 * code[ip + 1];
|
|
1231
|
+
if (op >= 0xb0 && op <= 0xb7)
|
|
1232
|
+
return 2 + (op & 7);
|
|
1233
|
+
if (op >= 0xb8 && op <= 0xbf)
|
|
1234
|
+
return 1 + 2 * ((op & 7) + 1);
|
|
1235
|
+
return 1;
|
|
1236
|
+
}
|