ntk 8.18.0 → 8.19.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/lib/app.js +217 -35
- package/lib/precise.js +90 -36
- package/lib/renderingcontext_2d.js +1105 -442
- package/lib/text/layout.js +12 -6
- package/lib/trapezoid.js +48 -26
- package/package.json +1 -1
package/lib/app.js
CHANGED
|
@@ -52,6 +52,39 @@ const DEFAULT_FRAME_INTERVAL = 16;
|
|
|
52
52
|
const MIN_REFRESH_RATE = 20;
|
|
53
53
|
const MAX_REFRESH_RATE = 1000;
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* How many of the solids drawings make for themselves a connection keeps
|
|
57
|
+
* (`App#transientSolid`). A fade folds each new alpha into every colour it
|
|
58
|
+
* draws, so it makes a few a frame; a frame faded at one alpha draws with
|
|
59
|
+
* fewer than this, and finds them all again on the next.
|
|
60
|
+
*/
|
|
61
|
+
const TRANSIENT_SOLIDS = 256;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* How many of the solids contexts paint their colour styles with a
|
|
65
|
+
* connection keeps (`App#styleSolid`): `fillStyle`, `strokeStyle`, a
|
|
66
|
+
* `TextLayout` span's colour. An app's palette is far fewer; a colour
|
|
67
|
+
* animated through a transition or a tween is a new one every frame.
|
|
68
|
+
*/
|
|
69
|
+
const STYLE_SOLIDS = 1024;
|
|
70
|
+
|
|
71
|
+
/** How many CSS colour strings it finds them by before starting over. */
|
|
72
|
+
const STYLE_NAMES = 1024;
|
|
73
|
+
|
|
74
|
+
// a solid, and on a server too old for CreateSolidFill the 1x1 pixmap it
|
|
75
|
+
// repeats
|
|
76
|
+
function freeSolid(picture) {
|
|
77
|
+
picture.destroy();
|
|
78
|
+
picture._sourcePixmap?.destroy();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// the value `key` names, taken out of `map`
|
|
82
|
+
function take(map, key) {
|
|
83
|
+
const value = map.get(key);
|
|
84
|
+
if (value !== undefined) map.delete(key);
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
55
88
|
/**
|
|
56
89
|
* A RandR mode's vertical refresh rate in Hz, or 0 when it does not describe
|
|
57
90
|
* one.
|
|
@@ -147,9 +180,21 @@ export default class App {
|
|
|
147
180
|
// client costs no I/O — discovery waits for the first page to bind.
|
|
148
181
|
sharedGlyphsFor(this);
|
|
149
182
|
this._cursors = null;
|
|
183
|
+
// the solids a caller holds for good (solidPicture)
|
|
150
184
|
this._solidPictures = new Map();
|
|
151
|
-
// the
|
|
185
|
+
// the solids contexts paint their colour styles with, least recently
|
|
186
|
+
// used first; a clock that ticks as they are made and moved up; and the
|
|
187
|
+
// same pictures by the CSS colour that asked for them (styleSolid)
|
|
188
|
+
this._styleSolids = new Map();
|
|
189
|
+
this._styleSolidLimit = STYLE_SOLIDS;
|
|
190
|
+
this._styleClock = 0;
|
|
152
191
|
this._solidByName = new Map();
|
|
192
|
+
// style solids let go of in this job, freed once it has run
|
|
193
|
+
this._evictedSolids = [];
|
|
194
|
+
// the solids one drawing makes for itself, least recently used first
|
|
195
|
+
// (transientSolid)
|
|
196
|
+
this._transientSolids = new Map();
|
|
197
|
+
this._transientSolidLimit = TRANSIENT_SOLIDS;
|
|
153
198
|
this._rasterizer = undefined;
|
|
154
199
|
this._shm = undefined;
|
|
155
200
|
this._extensionPromises = new Map();
|
|
@@ -959,14 +1004,18 @@ export default class App {
|
|
|
959
1004
|
}
|
|
960
1005
|
|
|
961
1006
|
/**
|
|
962
|
-
* A repeating source Picture of one colour, for compositing
|
|
963
|
-
* are 0..1 floats,
|
|
1007
|
+
* A repeating source Picture of one colour, for compositing, held for as
|
|
1008
|
+
* long as the connection is open. Components are 0..1 floats,
|
|
1009
|
+
* premultiplied by alpha.
|
|
964
1010
|
*
|
|
965
|
-
* Cached per connection, not per context:
|
|
966
|
-
*
|
|
967
|
-
*
|
|
968
|
-
*
|
|
969
|
-
*
|
|
1011
|
+
* Cached per connection, not per context: `Surface.render` builds a
|
|
1012
|
+
* context per call, and a per-context cache made every render recreate
|
|
1013
|
+
* its colours server-side. Nothing evicts: whoever asked may hold the
|
|
1014
|
+
* picture as long as it likes — a caller what `createSolidPicture` handed
|
|
1015
|
+
* back, a context its glyph source — so this is for the few colours an
|
|
1016
|
+
* app holds. Freed in `close()`. A context's colour styles are
|
|
1017
|
+
* `styleSolid`'s, and the colours a drawing makes for itself
|
|
1018
|
+
* `transientSolid`'s: once a colour animates, neither is few.
|
|
970
1019
|
*
|
|
971
1020
|
* On RENDER >= 0.10 a solid is one CreateSolidFill request; an older
|
|
972
1021
|
* server (or a hand-built App that skipped the version handshake in
|
|
@@ -976,7 +1025,151 @@ export default class App {
|
|
|
976
1025
|
const key = `${r}|${g}|${b}|${a}`;
|
|
977
1026
|
let p = this._solidPictures.get(key);
|
|
978
1027
|
if (p) return p;
|
|
1028
|
+
// one a style paints with, or a drawing made for itself, is held from
|
|
1029
|
+
// here on: out of its LRU, rather than made a second time
|
|
1030
|
+
p =
|
|
1031
|
+
take(this._styleSolids, key) ??
|
|
1032
|
+
take(this._transientSolids, key) ??
|
|
1033
|
+
this._createSolid(key, r, g, b, a);
|
|
1034
|
+
// and no LRU's clock ages it now (_usedStyle)
|
|
1035
|
+
p._used = Infinity;
|
|
1036
|
+
this._solidPictures.set(key, p);
|
|
1037
|
+
return p;
|
|
1038
|
+
}
|
|
979
1039
|
|
|
1040
|
+
/**
|
|
1041
|
+
* The solid a context paints a colour style with — `fillStyle`,
|
|
1042
|
+
* `strokeStyle`, a `TextLayout` span's colour: a CSS colour string, or a
|
|
1043
|
+
* premultiplied `[r, g, b, a]`. A string that is not a colour throws, as
|
|
1044
|
+
* `fillStyle` always has.
|
|
1045
|
+
*
|
|
1046
|
+
* Found by the string, where there is one: a UI sets the same few strings
|
|
1047
|
+
* again on every paint — every box's background, every border — and the
|
|
1048
|
+
* way to a solid is a parse, a premultiply and a key made of four numbers
|
|
1049
|
+
* turned into text, where by the string it is one lookup.
|
|
1050
|
+
*
|
|
1051
|
+
* Not held for good, as `solidPicture`'s are: a colour animated through a
|
|
1052
|
+
* transition or a tween is a new solid every frame, so the `STYLE_SOLIDS`
|
|
1053
|
+
* most recently used are kept and the rest let go. One let go is marked
|
|
1054
|
+
* `_evicted` at once, so that nothing hands it out again, and freed when
|
|
1055
|
+
* the job that let it go has run. X runs a client's requests in order, so
|
|
1056
|
+
* every request sent with it in that job reaches the server first: within
|
|
1057
|
+
* a job, holding one is safe however many colours are asked for meanwhile
|
|
1058
|
+
* — and a drawing does ask, taking its style before the shadow it paints
|
|
1059
|
+
* first makes colours of its own. Past the job, whoever holds one keeps
|
|
1060
|
+
* the style too, and asks again on finding the mark, as a context does
|
|
1061
|
+
* for `fillStyle` and `strokeStyle` (its `_backgroundPicture`).
|
|
1062
|
+
*/
|
|
1063
|
+
styleSolid(value) {
|
|
1064
|
+
if (typeof value !== 'string') return this._styleSolid(value);
|
|
1065
|
+
const p = this._solidByName.get(value);
|
|
1066
|
+
if (p !== undefined && !p._evicted) return this._usedStyle(p);
|
|
1067
|
+
const c = cssColor(value);
|
|
1068
|
+
if (!c) throw new Error(`Not a color: ${JSON.stringify(value)}`);
|
|
1069
|
+
const solid = this._styleSolid(c);
|
|
1070
|
+
// spellings are unbounded too: an app animating a colour writes a new
|
|
1071
|
+
// string every frame
|
|
1072
|
+
if (this._solidByName.size >= STYLE_NAMES) this._solidByName.clear();
|
|
1073
|
+
this._solidByName.set(value, solid);
|
|
1074
|
+
return solid;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// styleSolid by the numbers: a premultiplied [r, g, b, a]
|
|
1078
|
+
_styleSolid([r, g, b, a]) {
|
|
1079
|
+
const key = `${r}|${g}|${b}|${a}`;
|
|
1080
|
+
const held = this._solidPictures.get(key);
|
|
1081
|
+
if (held) return held;
|
|
1082
|
+
const lru = this._styleSolids;
|
|
1083
|
+
let p = lru.get(key);
|
|
1084
|
+
if (p) return this._usedStyle(p);
|
|
1085
|
+
p = this._createSolid(key, r, g, b, a);
|
|
1086
|
+
p._used = ++this._styleClock;
|
|
1087
|
+
lru.set(key, p);
|
|
1088
|
+
for (const [oldest, picture] of lru) {
|
|
1089
|
+
if (lru.size <= this._styleSolidLimit || picture === p) break;
|
|
1090
|
+
lru.delete(oldest);
|
|
1091
|
+
this._evictStyle(picture);
|
|
1092
|
+
}
|
|
1093
|
+
return p;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* A style solid asked for again: moved to the recent end of the LRU, but
|
|
1098
|
+
* only once it has aged into the older half. Moved on every ask, a
|
|
1099
|
+
* `fillStyle` set to a string it knows cost six times what the lookup
|
|
1100
|
+
* does; this way it stays a lookup and a comparison, and a colour asked
|
|
1101
|
+
* for every frame is still not the one let go.
|
|
1102
|
+
*/
|
|
1103
|
+
_usedStyle(p) {
|
|
1104
|
+
if (this._styleClock - p._used > this._styleSolidLimit / 2) {
|
|
1105
|
+
const lru = this._styleSolids;
|
|
1106
|
+
lru.delete(p._key);
|
|
1107
|
+
lru.set(p._key, p);
|
|
1108
|
+
p._used = ++this._styleClock;
|
|
1109
|
+
}
|
|
1110
|
+
return p;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// a style solid let go: marked, and freed once the job has run (styleSolid)
|
|
1114
|
+
_evictStyle(picture) {
|
|
1115
|
+
picture._evicted = true;
|
|
1116
|
+
const evicted = this._evictedSolids;
|
|
1117
|
+
evicted.push(picture);
|
|
1118
|
+
if (evicted.length === 1) queueMicrotask(() => this._freeEvictedSolids());
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
_freeEvictedSolids() {
|
|
1122
|
+
const evicted = this._evictedSolids;
|
|
1123
|
+
this._evictedSolids = [];
|
|
1124
|
+
for (const picture of evicted) freeSolid(picture);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* A solid for the requests of one drawing call, and nothing after them:
|
|
1129
|
+
* the colours a drawing makes for itself — a style with `globalAlpha`
|
|
1130
|
+
* folded into it, or the alpha alone in a mask slot. A fade makes new ones
|
|
1131
|
+
* nearly every frame; kept the way `solidPicture` keeps its own, every
|
|
1132
|
+
* fade left frames × colours of them on the server until the connection
|
|
1133
|
+
* closed. So these are kept apart: the `TRANSIENT_SOLIDS` most recently
|
|
1134
|
+
* used, the least recent freed as the next is made. A drawing faded at
|
|
1135
|
+
* one alpha frame after frame finds its few again each time.
|
|
1136
|
+
*
|
|
1137
|
+
* Freeing one is safe because X runs a client's requests in the order
|
|
1138
|
+
* they were sent, so the FreePicture reaches the server after every
|
|
1139
|
+
* request sent with the picture before it. What that cannot cover is a
|
|
1140
|
+
* request sent after — hence the contract: use the solid in the requests
|
|
1141
|
+
* of the call that asked for it, and drop it. Never keep one or hand it
|
|
1142
|
+
* out. Once evicted its id goes back to the pool, and the next solid made
|
|
1143
|
+
* can take it, in another colour.
|
|
1144
|
+
*
|
|
1145
|
+
* A colour the app keeps anyway, for a caller (`solidPicture`) or a style
|
|
1146
|
+
* (`styleSolid`), is answered from there: at full opacity, a rounded
|
|
1147
|
+
* box's corners paint with the style's own.
|
|
1148
|
+
*/
|
|
1149
|
+
transientSolid(r, g, b, a) {
|
|
1150
|
+
const key = `${r}|${g}|${b}|${a}`;
|
|
1151
|
+
let p = this._solidPictures.get(key) ?? this._styleSolids.get(key);
|
|
1152
|
+
if (p) return p;
|
|
1153
|
+
const lru = this._transientSolids;
|
|
1154
|
+
p = lru.get(key);
|
|
1155
|
+
if (p) {
|
|
1156
|
+
// Map iteration order is insertion order — re-insert to mark recent
|
|
1157
|
+
lru.delete(key);
|
|
1158
|
+
lru.set(key, p);
|
|
1159
|
+
return p;
|
|
1160
|
+
}
|
|
1161
|
+
p = this._createSolid(key, r, g, b, a);
|
|
1162
|
+
lru.set(key, p);
|
|
1163
|
+
for (const [oldest, picture] of lru) {
|
|
1164
|
+
if (lru.size <= this._transientSolidLimit || picture === p) break;
|
|
1165
|
+
lru.delete(oldest);
|
|
1166
|
+
freeSolid(picture);
|
|
1167
|
+
}
|
|
1168
|
+
return p;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
_createSolid(key, r, g, b, a) {
|
|
1172
|
+
let p;
|
|
980
1173
|
const Render = this.display.Render;
|
|
981
1174
|
const [major, minor] = Render.version || [0, 0];
|
|
982
1175
|
if (major > 0 || minor >= 10) {
|
|
@@ -991,27 +1184,13 @@ export default class App {
|
|
|
991
1184
|
p = new Picture(this, { id: pid });
|
|
992
1185
|
p._sourcePixmap = pixmap; // keep the 1x1 pixmap alive alongside the picture
|
|
993
1186
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
* and the way to `solidPicture` is a parse, a premultiply and a key made of
|
|
1002
|
-
* four numbers turned into text; by the string it is one lookup. Throws, as
|
|
1003
|
-
* `fillStyle` always has, for a string that is not a colour.
|
|
1004
|
-
*/
|
|
1005
|
-
solidPictureOf(value) {
|
|
1006
|
-
let p = this._solidByName.get(value);
|
|
1007
|
-
if (p !== undefined) return p;
|
|
1008
|
-
const c = cssColor(value);
|
|
1009
|
-
if (!c) throw new Error(`Not a color: ${JSON.stringify(value)}`);
|
|
1010
|
-
p = this.solidPicture(c[0], c[1], c[2], c[3]);
|
|
1011
|
-
// spellings are unbounded where solids are not: an app animating a
|
|
1012
|
-
// colour writes a new string every frame
|
|
1013
|
-
if (this._solidByName.size >= 1024) this._solidByName.clear();
|
|
1014
|
-
this._solidByName.set(value, p);
|
|
1187
|
+
// what it paints with, so a drawing can fold `globalAlpha` into it: glyphs
|
|
1188
|
+
// composite straight from their source, with no mask slot to carry one
|
|
1189
|
+
p._rgba = Object.freeze([r, g, b, a]);
|
|
1190
|
+
// and what the style LRU needs of it (styleSolid)
|
|
1191
|
+
p._key = key;
|
|
1192
|
+
p._evicted = false;
|
|
1193
|
+
p._used = 0;
|
|
1015
1194
|
return p;
|
|
1016
1195
|
}
|
|
1017
1196
|
|
|
@@ -1028,13 +1207,16 @@ export default class App {
|
|
|
1028
1207
|
// shadow coverage is cached per connection for the same reason solids
|
|
1029
1208
|
// are: the contexts that drew it are long gone (see lib/shadow.js)
|
|
1030
1209
|
dropShadowSurfaces(this);
|
|
1031
|
-
// shared by every context that ever asked (see solidPicture), so
|
|
1032
|
-
//
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1210
|
+
// shared by every context that ever asked (see solidPicture), so they are
|
|
1211
|
+
// the app's to free, never a context's teardown; whatever is left goes
|
|
1212
|
+
// with the connection, the style solids this job let go of included
|
|
1213
|
+
for (const picture of this._solidPictures.values()) freeSolid(picture);
|
|
1214
|
+
for (const picture of this._styleSolids.values()) freeSolid(picture);
|
|
1215
|
+
for (const picture of this._transientSolids.values()) freeSolid(picture);
|
|
1216
|
+
this._freeEvictedSolids();
|
|
1037
1217
|
this._solidPictures.clear();
|
|
1218
|
+
this._styleSolids.clear();
|
|
1219
|
+
this._transientSolids.clear();
|
|
1038
1220
|
this._solidByName.clear();
|
|
1039
1221
|
// GPU contexts are shared by every direct GL surface on this connection
|
|
1040
1222
|
// (see renderingcontext_gles.js), so they outlive individual contexts and
|
package/lib/precise.js
CHANGED
|
@@ -144,22 +144,27 @@ function edgeInit(edge, yStart, xTop, yTop, xBot, yBot) {
|
|
|
144
144
|
let signdx = 0;
|
|
145
145
|
let rem = 0;
|
|
146
146
|
if (dy) {
|
|
147
|
+
// Plain float divisions truncate exactly here. A quotient of integers
|
|
148
|
+
// whose dividend is below 2^45 sits at least 1/dy from any integer it
|
|
149
|
+
// is not, and a double holds it to within (dividend / dy) · 2^-52, far
|
|
150
|
+
// closer than that. The step from the line's top below, whose dividend
|
|
151
|
+
// can pass 2^53, keeps idiv.
|
|
147
152
|
if (dx >= 0) {
|
|
148
153
|
signdx = 1;
|
|
149
|
-
stepx =
|
|
150
|
-
rem = dx
|
|
154
|
+
stepx = Math.trunc(dx / dy);
|
|
155
|
+
rem = dx - stepx * dy;
|
|
151
156
|
e = -dy;
|
|
152
157
|
} else {
|
|
153
158
|
signdx = -1;
|
|
154
|
-
stepx = -
|
|
155
|
-
rem = -dx
|
|
159
|
+
stepx = -Math.trunc(-dx / dy);
|
|
160
|
+
rem = -dx + stepx * dy;
|
|
156
161
|
}
|
|
157
162
|
// _pixman_edge_multi_init for the small and the big step; n is at most
|
|
158
163
|
// 4370, so every product here is exact in a double
|
|
159
164
|
let ne = STEP_Y_SMALL * rem;
|
|
160
165
|
let sx = (STEP_Y_SMALL * stepx) | 0;
|
|
161
166
|
if (ne > 0) {
|
|
162
|
-
const nx =
|
|
167
|
+
const nx = Math.trunc(ne / dy);
|
|
163
168
|
ne -= nx * dy;
|
|
164
169
|
sx = (sx + nx * signdx) | 0;
|
|
165
170
|
}
|
|
@@ -168,7 +173,7 @@ function edgeInit(edge, yStart, xTop, yTop, xBot, yBot) {
|
|
|
168
173
|
ne = STEP_Y_BIG * rem;
|
|
169
174
|
sx = (STEP_Y_BIG * stepx) | 0;
|
|
170
175
|
if (ne > 0) {
|
|
171
|
-
const nx =
|
|
176
|
+
const nx = Math.trunc(ne / dy);
|
|
172
177
|
ne -= nx * dy;
|
|
173
178
|
sx = (sx + nx * signdx) | 0;
|
|
174
179
|
}
|
|
@@ -219,9 +224,20 @@ function edgeInit(edge, yStart, xTop, yTop, xBot, yBot) {
|
|
|
219
224
|
edge[SIGNDX] = signdx;
|
|
220
225
|
}
|
|
221
226
|
|
|
227
|
+
// RENDER_SAMPLES_X for every fraction of a pixel: how many of a pixel's 17
|
|
228
|
+
// sample columns lie left of an edge that crosses it there
|
|
229
|
+
const SAMPLES_X = new Uint8Array(65536);
|
|
230
|
+
for (let f = 0; f < 65536; f++) SAMPLES_X[f] = ((f + X_FRAC_FIRST) / STEP_X_SMALL) | 0;
|
|
231
|
+
|
|
222
232
|
/**
|
|
223
233
|
* rasterize_edges_8 between `left` and `right`, from sample row `t` to `b`
|
|
224
234
|
* inclusive, into the difference array `acc` (rows `stride` apart).
|
|
235
|
+
*
|
|
236
|
+
* A span adds 17 - ls to the pixel it starts in, 17 to each pixel after, rs
|
|
237
|
+
* to the pixel it ends in and nothing past it: the difference of two steps,
|
|
238
|
+
* each two cells of `acc`. While an edge stays in one pixel from sample row
|
|
239
|
+
* to sample row, as a steep one does for all fifteen, its two cells are
|
|
240
|
+
* summed here and written once.
|
|
225
241
|
*/
|
|
226
242
|
function addSpans(acc, stride, width, t, b) {
|
|
227
243
|
let y = t;
|
|
@@ -245,54 +261,92 @@ function addSpans(acc, stride, width, t, b) {
|
|
|
245
261
|
// pixman clips the span to the image: from 0, and to the last pixel of
|
|
246
262
|
// the row taken whole. Both are what an unclipped span would add there.
|
|
247
263
|
const lastX = (width << 16) - 1;
|
|
264
|
+
// each edge's step not yet written: the pixel it is in (-1 for none), and
|
|
265
|
+
// what it adds to that cell and to the next
|
|
266
|
+
let lPixel = -1;
|
|
267
|
+
let lAt = 0;
|
|
268
|
+
let lPast = 0;
|
|
269
|
+
let rPixel = -1;
|
|
270
|
+
let rAt = 0;
|
|
271
|
+
let rPast = 0;
|
|
272
|
+
// an edge's step carries one more unit of x when its error term passes
|
|
273
|
+
// zero: computed, not branched on, since a slope's carries come in no
|
|
274
|
+
// pattern a branch predictor can learn
|
|
275
|
+
let c = 0;
|
|
248
276
|
for (;;) {
|
|
249
277
|
const l = lx < 0 ? 0 : lx;
|
|
250
278
|
const r = rx >> 16 >= width ? lastX : rx;
|
|
251
279
|
if (r > l) {
|
|
252
|
-
// the span adds 17 - ls to the pixel it starts in, 17 to each pixel
|
|
253
|
-
// after, rs to the pixel it ends in and nothing past it: the
|
|
254
|
-
// difference of two steps, each two cells of `acc`
|
|
255
280
|
const li = l >> 16;
|
|
281
|
+
const ls = SAMPLES_X[l & 0xffff];
|
|
282
|
+
if (li !== lPixel) {
|
|
283
|
+
if (lPixel >= 0) {
|
|
284
|
+
acc[row + lPixel] += lAt;
|
|
285
|
+
acc[row + lPixel + 1] += lPast;
|
|
286
|
+
}
|
|
287
|
+
lPixel = li;
|
|
288
|
+
lAt = 0;
|
|
289
|
+
lPast = 0;
|
|
290
|
+
}
|
|
291
|
+
lAt += N_X_FRAC - ls;
|
|
292
|
+
lPast += ls;
|
|
256
293
|
const ri = r >> 16;
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
294
|
+
const rs = SAMPLES_X[r & 0xffff];
|
|
295
|
+
if (ri !== rPixel) {
|
|
296
|
+
if (rPixel >= 0) {
|
|
297
|
+
acc[row + rPixel] -= rAt;
|
|
298
|
+
acc[row + rPixel + 1] -= rPast;
|
|
299
|
+
}
|
|
300
|
+
rPixel = ri;
|
|
301
|
+
rAt = 0;
|
|
302
|
+
rPast = 0;
|
|
303
|
+
}
|
|
304
|
+
rAt += N_X_FRAC - rs;
|
|
305
|
+
rPast += rs;
|
|
263
306
|
}
|
|
264
|
-
if (y === b)
|
|
307
|
+
if (y === b) break;
|
|
265
308
|
if ((y & 0xffff) !== Y_FRAC_LAST) {
|
|
266
|
-
lx = (lx + lsxs) | 0;
|
|
267
309
|
le = (le + ldxs) | 0;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
rx = (rx + rsxs) | 0;
|
|
310
|
+
c = -le >>> 31;
|
|
311
|
+
le = (le - c * ldy) | 0;
|
|
312
|
+
lx = (lx + lsxs + c * lsign) | 0;
|
|
273
313
|
re = (re + rdxs) | 0;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
314
|
+
c = -re >>> 31;
|
|
315
|
+
re = (re - c * rdy) | 0;
|
|
316
|
+
rx = (rx + rsxs + c * rsign) | 0;
|
|
278
317
|
y = (y + STEP_Y_SMALL) | 0;
|
|
279
318
|
} else {
|
|
280
|
-
lx = (lx + lsxb) | 0;
|
|
281
319
|
le = (le + ldxb) | 0;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
rx = (rx + rsxb) | 0;
|
|
320
|
+
c = -le >>> 31;
|
|
321
|
+
le = (le - c * ldy) | 0;
|
|
322
|
+
lx = (lx + lsxb + c * lsign) | 0;
|
|
287
323
|
re = (re + rdxb) | 0;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
324
|
+
c = -re >>> 31;
|
|
325
|
+
re = (re - c * rdy) | 0;
|
|
326
|
+
rx = (rx + rsxb + c * rsign) | 0;
|
|
292
327
|
y = (y + STEP_Y_BIG) | 0;
|
|
328
|
+
// the pixel row is done: write what it left pending
|
|
329
|
+
if (lPixel >= 0) {
|
|
330
|
+
acc[row + lPixel] += lAt;
|
|
331
|
+
acc[row + lPixel + 1] += lPast;
|
|
332
|
+
lPixel = -1;
|
|
333
|
+
}
|
|
334
|
+
if (rPixel >= 0) {
|
|
335
|
+
acc[row + rPixel] -= rAt;
|
|
336
|
+
acc[row + rPixel + 1] -= rPast;
|
|
337
|
+
rPixel = -1;
|
|
338
|
+
}
|
|
293
339
|
row += stride;
|
|
294
340
|
}
|
|
295
341
|
}
|
|
342
|
+
if (lPixel >= 0) {
|
|
343
|
+
acc[row + lPixel] += lAt;
|
|
344
|
+
acc[row + lPixel + 1] += lPast;
|
|
345
|
+
}
|
|
346
|
+
if (rPixel >= 0) {
|
|
347
|
+
acc[row + rPixel] -= rAt;
|
|
348
|
+
acc[row + rPixel + 1] -= rPast;
|
|
349
|
+
}
|
|
296
350
|
}
|
|
297
351
|
|
|
298
352
|
/** the first sample row of a band from `top`, inside the grid */
|