ntk 8.23.1 → 8.23.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.
- package/lib/renderingcontext_2d.js +71 -12
- package/package.json +1 -1
|
@@ -805,6 +805,10 @@ class RenderingContext2d {
|
|
|
805
805
|
this.fillMaskDrawable = null;
|
|
806
806
|
this.clipMask = null;
|
|
807
807
|
this.clipMaskDrawable = null;
|
|
808
|
+
// a clip mask a restore let go of, for the next path clip (`_rebuildClipMask`)
|
|
809
|
+
this._keptClipMask = null;
|
|
810
|
+
// where the clip mask may hold coverage, [x, y, w, h]; null for nowhere
|
|
811
|
+
this._clipMaskInk = null;
|
|
808
812
|
this._textStyle = null;
|
|
809
813
|
this._lastFontString = null;
|
|
810
814
|
this.textAlign = "start";
|
|
@@ -1224,6 +1228,8 @@ class RenderingContext2d {
|
|
|
1224
1228
|
this.clipMaskDrawable.destroy();
|
|
1225
1229
|
this.clipMask = this.clipMaskDrawable = null;
|
|
1226
1230
|
}
|
|
1231
|
+
// one kept is the size the surface was
|
|
1232
|
+
this._dropKeptClipMask();
|
|
1227
1233
|
// clip paths are device-space and survive the resize; rebuild the mask
|
|
1228
1234
|
if (this._clips && this._clips.length) this._rebuildClipMask();
|
|
1229
1235
|
}
|
|
@@ -2361,6 +2367,16 @@ class RenderingContext2d {
|
|
|
2361
2367
|
|
|
2362
2368
|
_ensureClipMask() {
|
|
2363
2369
|
if (this.clipMask) return;
|
|
2370
|
+
const kept = this._keptClipMask;
|
|
2371
|
+
if (kept) {
|
|
2372
|
+
// the one a restore let go of: what it holds is cleared where it was
|
|
2373
|
+
// written, by `_materializeClipMask`
|
|
2374
|
+
this._keptClipMask = null;
|
|
2375
|
+
this.clipMask = kept.mask;
|
|
2376
|
+
this.clipMaskDrawable = kept.drawable;
|
|
2377
|
+
this._clipMaskInk = kept.ink;
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2364
2380
|
this.clipMaskDrawable = new Pixmap(this.window.app, {
|
|
2365
2381
|
depth: 8,
|
|
2366
2382
|
width: this.width,
|
|
@@ -2370,6 +2386,8 @@ class RenderingContext2d {
|
|
|
2370
2386
|
drawable: this.clipMaskDrawable,
|
|
2371
2387
|
format: this.Render.a8,
|
|
2372
2388
|
});
|
|
2389
|
+
// a fresh pixmap's contents are undefined, all of them
|
|
2390
|
+
this._clipMaskInk = [0, 0, this.width, this.height];
|
|
2373
2391
|
}
|
|
2374
2392
|
|
|
2375
2393
|
/**
|
|
@@ -4900,12 +4918,16 @@ class RenderingContext2d {
|
|
|
4900
4918
|
_materializeClipMask() {
|
|
4901
4919
|
const R = this.Render;
|
|
4902
4920
|
this._ensureClipMask();
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4921
|
+
// Cleared where it may hold coverage, not all over: the mask is the size
|
|
4922
|
+
// of the surface, and a clip a rounded row of a list puts on a window
|
|
4923
|
+
// cleared the whole window's worth of it for each row of each frame.
|
|
4924
|
+
// What it holds is never outside its first shape's box, since what is
|
|
4925
|
+
// intersected with it only takes coverage away (`_clipMaskInk`).
|
|
4926
|
+
const ink = this._clipMaskInk;
|
|
4927
|
+
if (ink && ink[2] > 0 && ink[3] > 0) {
|
|
4928
|
+
R.FillRectangles(R.PictOp.Src, this.clipMask.id, [0, 0, 0, 0], ink);
|
|
4929
|
+
}
|
|
4930
|
+
this._clipMaskInk = null;
|
|
4909
4931
|
// region entries are the picture's clip, not the mask's business
|
|
4910
4932
|
const shapes = this._clips.filter((entry) => !entry.region);
|
|
4911
4933
|
if (!shapes.length) {
|
|
@@ -4915,6 +4937,7 @@ class RenderingContext2d {
|
|
|
4915
4937
|
[0, 0, 0, 1],
|
|
4916
4938
|
[0, 0, this.width, this.height],
|
|
4917
4939
|
);
|
|
4940
|
+
this._clipMaskInk = [0, 0, this.width, this.height];
|
|
4918
4941
|
return;
|
|
4919
4942
|
}
|
|
4920
4943
|
const first = shapes[0];
|
|
@@ -4931,9 +4954,20 @@ class RenderingContext2d {
|
|
|
4931
4954
|
[0, 0, 0, 1],
|
|
4932
4955
|
[x, y, right - x, bottom - y],
|
|
4933
4956
|
);
|
|
4957
|
+
this._clipMaskInk = [x, y, right - x, bottom - y];
|
|
4934
4958
|
}
|
|
4935
4959
|
} else {
|
|
4936
4960
|
this._applyPolyClip(first, R.PictOp.Src);
|
|
4961
|
+
const bb = this._polysBBox(first.polys);
|
|
4962
|
+
if (bb && bb.w > 0 && bb.h > 0) {
|
|
4963
|
+
const x = Math.max(0, Math.floor(bb.x));
|
|
4964
|
+
const y = Math.max(0, Math.floor(bb.y));
|
|
4965
|
+
const right = Math.min(this.width, Math.ceil(bb.x + bb.w));
|
|
4966
|
+
const bottom = Math.min(this.height, Math.ceil(bb.y + bb.h));
|
|
4967
|
+
if (right > x && bottom > y) {
|
|
4968
|
+
this._clipMaskInk = [x, y, right - x, bottom - y];
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4937
4971
|
}
|
|
4938
4972
|
for (let i = 1; i < shapes.length; i++) this._intersectClip(shapes[i]);
|
|
4939
4973
|
}
|
|
@@ -5084,9 +5118,18 @@ class RenderingContext2d {
|
|
|
5084
5118
|
if (!rasterizer) return false;
|
|
5085
5119
|
const { shapes, edges } = fillableShapes(entry.polys);
|
|
5086
5120
|
if (!shapes.length) return false;
|
|
5087
|
-
//
|
|
5088
|
-
//
|
|
5089
|
-
|
|
5121
|
+
// A clip's coverage is rasterized here up to the policy's byte bound,
|
|
5122
|
+
// whatever its edges, where a fill is routed by its cost (`_routeFor`).
|
|
5123
|
+
// The server route is AddTraps into the pixmap the coverage lands on,
|
|
5124
|
+
// and AddTraps adds into a picture that exists: glamor — Xwayland's,
|
|
5125
|
+
// and Xorg's on most GPUs — has no GPU path for it, and reads the
|
|
5126
|
+
// pixmap back, rasterizes in software and uploads it again, a GPU sync
|
|
5127
|
+
// for every clip. A rounded row clipped that way took 5.6 ms on a virgl
|
|
5128
|
+
// Xwayland, where its raster here and a PutImage took 0.14; pixman,
|
|
5129
|
+
// which adds the traps itself, takes 0.03 ms less for it.
|
|
5130
|
+
const extent = this._polysExtent(entry.polys) ?? bb;
|
|
5131
|
+
const area = Math.min(extent.w, this.width) * Math.min(extent.h, this.height);
|
|
5132
|
+
if (!(area > 0 && area <= this.window.app.rasterPolicy.maxBytes) || !edges)
|
|
5090
5133
|
return false;
|
|
5091
5134
|
|
|
5092
5135
|
const coverage = rasterizer.rasterize({
|
|
@@ -5148,17 +5191,33 @@ class RenderingContext2d {
|
|
|
5148
5191
|
}
|
|
5149
5192
|
|
|
5150
5193
|
_rebuildClipMask() {
|
|
5151
|
-
// The stack changed shape (restore, resize):
|
|
5194
|
+
// The stack changed shape (restore, resize): let the mask go rather than
|
|
5152
5195
|
// rebuild it eagerly. A stack that went back to rectangles never needs
|
|
5153
5196
|
// one again, and one that still holds a poly rebuilds on first demand
|
|
5154
5197
|
// through _requireClipMask — same work, paid only if something draws.
|
|
5198
|
+
// It is kept, not destroyed, for the next path clip to fill again: a
|
|
5199
|
+
// pixmap the size of the surface was made and freed for every rounded
|
|
5200
|
+
// clip, sixteen a frame for a list of eight rounded rows, where the fill
|
|
5201
|
+
// mask beside it is kept for the context's life.
|
|
5155
5202
|
if (this.clipMask) {
|
|
5156
|
-
this.
|
|
5157
|
-
this.
|
|
5203
|
+
this._dropKeptClipMask();
|
|
5204
|
+
this._keptClipMask = {
|
|
5205
|
+
mask: this.clipMask,
|
|
5206
|
+
drawable: this.clipMaskDrawable,
|
|
5207
|
+
ink: this._clipMaskInk,
|
|
5208
|
+
};
|
|
5158
5209
|
this.clipMask = this.clipMaskDrawable = null;
|
|
5159
5210
|
}
|
|
5160
5211
|
}
|
|
5161
5212
|
|
|
5213
|
+
_dropKeptClipMask() {
|
|
5214
|
+
const kept = this._keptClipMask;
|
|
5215
|
+
if (!kept) return;
|
|
5216
|
+
this._keptClipMask = null;
|
|
5217
|
+
kept.mask.destroy();
|
|
5218
|
+
kept.drawable.destroy();
|
|
5219
|
+
}
|
|
5220
|
+
|
|
5162
5221
|
isPointInPath(...args) {
|
|
5163
5222
|
let path = null;
|
|
5164
5223
|
let rest = args;
|