ntk 8.12.1 → 8.12.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.
@@ -2716,7 +2716,14 @@ class RenderingContext2d {
2716
2716
  if (rect.w === 0 || rect.h === 0) return;
2717
2717
  this._setPictureClip(rect);
2718
2718
  }
2719
- drawGlyphRuns(app, op, src.id, this._dst(), positioned);
2719
+ drawGlyphRuns(
2720
+ app,
2721
+ op,
2722
+ src.id,
2723
+ this._dst(),
2724
+ positioned,
2725
+ rect ?? { x: 0, y: 0, w: this.width, h: this.height },
2726
+ );
2720
2727
  if (rect) this._invalidatePictureClip();
2721
2728
  this._markDirty();
2722
2729
  return;
@@ -2737,6 +2744,7 @@ class RenderingContext2d {
2737
2744
  this._glyphSource.id,
2738
2745
  this.fillMask.id,
2739
2746
  positioned,
2747
+ { x: 0, y: 0, w: this.width, h: this.height },
2740
2748
  );
2741
2749
  R.Composite(
2742
2750
  R.PictOp.In,
@@ -571,8 +571,11 @@ export function routeGlyphSize(app, font, size, policy = policyOf(app), textRend
571
571
  * @param {number} srcId source picture (fill color/gradient)
572
572
  * @param {number} dstId destination picture
573
573
  * @param {Array<{run, x, y}>} positioned visual-order runs with baseline origins
574
+ * @param {{x: number, y: number, w: number, h: number}} [bounds] the part of
575
+ * the destination that can show anything — the clip, or the surface. A
576
+ * glyph wholly outside it is not sent (`visibleGlyph`).
574
577
  */
575
- export function drawGlyphRuns(app, op, srcId, dstId, positioned) {
578
+ export function drawGlyphRuns(app, op, srcId, dstId, positioned, bounds = null) {
576
579
  const policy = policyOf(app);
577
580
  let bitmap = positioned;
578
581
  let vector = null;
@@ -590,11 +593,35 @@ export function drawGlyphRuns(app, op, srcId, dstId, positioned) {
590
593
  bitmap.push(positioned[i]);
591
594
  }
592
595
  }
593
- if (bitmap.length) drawBitmapGlyphRuns(app, op, srcId, dstId, bitmap, policy);
594
- if (vector) drawVectorGlyphRuns(app, op, srcId, dstId, vector);
596
+ if (bitmap.length) drawBitmapGlyphRuns(app, op, srcId, dstId, bitmap, policy, bounds);
597
+ if (vector) drawVectorGlyphRuns(app, op, srcId, dstId, vector, bounds);
595
598
  }
596
599
 
597
- function drawBitmapGlyphRuns(app, op, srcId, dstId, positioned, policy) {
600
+ /**
601
+ * Whether a glyph whose origin is at (x, y) can put any ink inside `bounds`,
602
+ * give or take two ems — no glyph reaches further from its origin than that,
603
+ * swashes and all.
604
+ *
605
+ * Not only a saving: the glyphs of a line wider than 32767 device pixels —
606
+ * a minified file in a code editor, a long label in a narrow cell — have
607
+ * origins no request can carry. CompositeGlyphs places its first glyph and
608
+ * every element after it in 16-bit coordinates, and the vector path's trap
609
+ * mask would span the whole line, so drawing such a line threw a RangeError
610
+ * out of the paint however little of it was on screen. What is left after
611
+ * the cull sits inside a surface, and every coordinate in it fits.
612
+ */
613
+ function visibleGlyph(bounds, x, y, size) {
614
+ if (bounds === null) return true;
615
+ const reach = size * 2;
616
+ return (
617
+ x + reach > bounds.x &&
618
+ x - reach < bounds.x + bounds.w &&
619
+ y + reach > bounds.y &&
620
+ y - reach < bounds.y + bounds.h
621
+ );
622
+ }
623
+
624
+ function drawBitmapGlyphRuns(app, op, srcId, dstId, positioned, policy, bounds) {
598
625
  const Render = app.display.Render;
599
626
  const items = [];
600
627
  let bits = 8;
@@ -608,6 +635,7 @@ function drawBitmapGlyphRuns(app, op, srcId, dstId, positioned, policy) {
608
635
  if (page.bits > bits) bits = page.bits;
609
636
  }
610
637
  for (const pos of positionGlyphs(positioned)) {
638
+ if (!visibleGlyph(bounds, pos.x, pos.y, pos.run.size)) continue;
611
639
  const page = pages.get(pos.run);
612
640
  const e = page.entry(pos.glyph.id);
613
641
  items.push({ gs: e.gs, lid: e.lid, adv: e.adv, x: pos.x, y: pos.y });
@@ -646,7 +674,7 @@ const MAX_TRAPS_PER_REQUEST = 6000;
646
674
  * Positions are intentionally NOT rounded to whole pixels — fractional
647
675
  * advances and origins keep zoom animations smooth.
648
676
  */
649
- function drawVectorGlyphRuns(app, op, srcId, dstId, positioned) {
677
+ function drawVectorGlyphRuns(app, op, srcId, dstId, positioned, bounds = null) {
650
678
  // unrounded glyph origins + ink bounding box
651
679
  const placed = [];
652
680
  let minX = Infinity;
@@ -657,6 +685,7 @@ function drawVectorGlyphRuns(app, op, srcId, dstId, positioned) {
657
685
  const gx = cursor + g.dx;
658
686
  const gy = y - g.dy;
659
687
  cursor += g.ax;
688
+ if (!visibleGlyph(bounds, gx, gy, run.size)) continue;
660
689
  const e = run.font.glyphExtents(g.id, run.size);
661
690
  if (!Number.isFinite(e.minX) || e.maxX <= e.minX) continue; // blank glyph
662
691
  placed.push({ run, glyph: g, x: gx, y: gy });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntk",
3
- "version": "8.12.1",
3
+ "version": "8.12.2",
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",