ntk 3.5.2 → 3.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -552,6 +552,34 @@ class RenderingContext2d {
552
552
  * Core fill: rasterize device-space polys into the scratch a8 mask,
553
553
  * scale by globalAlpha, intersect with the clip, composite the source.
554
554
  */
555
+ /**
556
+ * Device-space bounding box of flattened polys, with a pixel of slack for
557
+ * the antialiased edge, clamped to the surface. Null when nothing lands
558
+ * on it.
559
+ */
560
+ _polysBBox(polys) {
561
+ let minX = Infinity;
562
+ let minY = Infinity;
563
+ let maxX = -Infinity;
564
+ let maxY = -Infinity;
565
+ for (const poly of polys) {
566
+ const pts = poly.pts;
567
+ for (let i = 0; i < pts.length; i += 2) {
568
+ if (pts[i] < minX) minX = pts[i];
569
+ if (pts[i] > maxX) maxX = pts[i];
570
+ if (pts[i + 1] < minY) minY = pts[i + 1];
571
+ if (pts[i + 1] > maxY) maxY = pts[i + 1];
572
+ }
573
+ }
574
+ if (maxX === -Infinity) return null;
575
+ const x = Math.max(0, Math.floor(minX) - 1);
576
+ const y = Math.max(0, Math.floor(minY) - 1);
577
+ const w = Math.min(this.width, Math.ceil(maxX) + 1) - x;
578
+ const h = Math.min(this.height, Math.ceil(maxY) + 1) - y;
579
+ if (w <= 0 || h <= 0) return null;
580
+ return { x, y, w, h };
581
+ }
582
+
555
583
  _fillPolys(polys, rule, { src = null, op = null, alpha = null } = {}) {
556
584
  if (!polys.length) return;
557
585
  src = src ?? this._backgroundPicture;
@@ -559,26 +587,31 @@ class RenderingContext2d {
559
587
  alpha = alpha ?? this.globalAlpha;
560
588
  if (alpha <= 0) return;
561
589
 
590
+ // Everything below is bounded to the shape's bounding box rather than
591
+ // the whole surface. On the wire it makes no difference — a Composite
592
+ // request is the same size either way — but it is the difference
593
+ // between the server touching a 34x34 box and a 400x400 one per fill.
594
+ // Stale mask content outside the box is never composited, so clearing
595
+ // only the box is safe.
596
+ const b = this._polysBBox(polys);
597
+ if (!b) return;
598
+ const R = this.Render;
599
+
562
600
  this._ensureFillMask();
563
- this.Render.FillRectangles(this.Render.PictOp.Src, this.fillMask.id, [0, 0, 0, 0], [0, 0, this.width, this.height]);
601
+ R.FillRectangles(R.PictOp.Src, this.fillMask.id, [0, 0, 0, 0], [b.x, b.y, b.w, b.h]);
564
602
  this._rasterizePolys(this.fillMask, polys, rule);
565
603
 
566
604
  if (alpha < 1) {
567
605
  // In with a constant color scales the a8 coverage by that alpha
568
- this.Render.FillRectangles(this.Render.PictOp.In, this.fillMask.id, [0, 0, 0, alpha], [0, 0, this.width, this.height]);
606
+ R.FillRectangles(R.PictOp.In, this.fillMask.id, [0, 0, 0, alpha], [b.x, b.y, b.w, b.h]);
569
607
  }
570
608
  if (this.clipMask) {
571
- this.Render.Composite(
572
- this.Render.PictOp.In,
573
- this.clipMask.id,
574
- 0,
575
- this.fillMask.id,
576
- 0, 0, 0, 0, 0, 0,
577
- this.width,
578
- this.height
579
- );
609
+ // clipMask is surface-aligned, so it is sampled at the same offset
610
+ R.Composite(R.PictOp.In, this.clipMask.id, 0, this.fillMask.id, b.x, b.y, 0, 0, b.x, b.y, b.w, b.h);
580
611
  }
581
- this.Render.Composite(op, src.id, this.fillMask.id, this.picture.id, 0, 0, 0, 0, 0, 0, this.width, this.height);
612
+ // src is either a 1x1 repeating solid (offset irrelevant) or a
613
+ // surface-aligned gradient, so it is sampled at the same offset too
614
+ R.Composite(op, src.id, this.fillMask.id, this.picture.id, b.x, b.y, b.x, b.y, b.x, b.y, b.w, b.h);
582
615
  this._markDirty();
583
616
  }
584
617
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntk",
3
- "version": "3.5.2",
3
+ "version": "3.5.3",
4
4
  "description": "Desktop UI toolkit for X11 with canvas-like 2d and OpenGL rendering",
5
5
  "author": "Andrey Sidorov <sidorares@yandex.ru>",
6
6
  "license": "MIT",