react-x11 2.32.0 → 2.34.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.
@@ -0,0 +1,46 @@
1
+ // `objectFit`: where a picture with a size of its own goes in a box that has
2
+ // another — CSS's `object-fit`, for `<image>` and `<video>`, centred the way
3
+ // CSS's default `object-position: 50% 50%` centres it. A leaf: no node, no
4
+ // backend, rects in and a rect out.
5
+
6
+ export const OBJECT_FITS = Object.freeze([
7
+ 'fill',
8
+ 'contain',
9
+ 'cover',
10
+ 'none',
11
+ 'scale-down',
12
+ ]);
13
+
14
+ /** The element's `objectFit`, or its default where the style names none. */
15
+ export function objectFitOf(style, fallback) {
16
+ const fit = style?.objectFit;
17
+ return OBJECT_FITS.includes(fit) ? fit : fallback;
18
+ }
19
+
20
+ /**
21
+ * Where a `width` x `height` picture goes in `box` under `fit`: `fill`
22
+ * stretches it to the box, `contain` scales it to fit inside with bars,
23
+ * `cover` scales it to fill with the overflow cut (by the caller), `none`
24
+ * leaves it its own size, and `scale-down` is `none` or `contain`, whichever
25
+ * is smaller. Its edges are snapped to whole pixels, so a picture drawn
26
+ * there and a layer placed there cover exactly the same ones — except under
27
+ * `fill`, which is the box itself, as an `<image>` has always been drawn.
28
+ */
29
+ export function fitRect(box, width, height, fit) {
30
+ if (fit === 'fill' || !(width > 0 && height > 0)) return { ...box };
31
+ let k;
32
+ if (fit === 'none') k = 1;
33
+ else {
34
+ const sx = box.width / width;
35
+ const sy = box.height / height;
36
+ k = fit === 'cover' ? Math.max(sx, sy) : Math.min(sx, sy);
37
+ if (fit === 'scale-down') k = Math.min(k, 1);
38
+ }
39
+ const w = width * k;
40
+ const h = height * k;
41
+ const x0 = Math.round(box.x + (box.width - w) / 2);
42
+ const y0 = Math.round(box.y + (box.height - h) / 2);
43
+ const x1 = Math.round(box.x + (box.width + w) / 2);
44
+ const y1 = Math.round(box.y + (box.height + h) / 2);
45
+ return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
46
+ }
@@ -18,6 +18,7 @@ import {
18
18
  } from '../imagesource.js';
19
19
  import { loadImageFile } from '../imagedecode.js';
20
20
  import { symbolWeight, symbolsFor, warnOnce } from '../symbols.js';
21
+ import { fitRect, objectFitOf } from './fit.js';
21
22
  import { intrinsicSize } from './layout.js';
22
23
  import { Node } from './node.js';
23
24
  import { DEV } from './util.js';
@@ -317,12 +318,42 @@ export class ImageNode extends Node {
317
318
  }
318
319
  if (!this.image) return;
319
320
  const content = this.contentBox();
320
- ctx.drawImage(
321
- this.image,
322
- content.x,
323
- content.y,
324
- content.width,
325
- content.height,
321
+ const fit = objectFitOf(this.style, 'fill');
322
+ if (fit === 'fill') {
323
+ ctx.drawImage(
324
+ this.image,
325
+ content.x,
326
+ content.y,
327
+ content.width,
328
+ content.height,
329
+ );
330
+ return;
331
+ }
332
+ // the source's size in device pixels, as `measureContent` takes it
333
+ const s = this.scale;
334
+ const rect = fitRect(
335
+ content,
336
+ (this.image.width ?? 0) * s,
337
+ (this.image.height ?? 0) * s,
338
+ fit,
326
339
  );
340
+ if (!(rect.width > 0 && rect.height > 0)) return;
341
+ const inside =
342
+ rect.x >= content.x &&
343
+ rect.y >= content.y &&
344
+ rect.x + rect.width <= content.x + content.width &&
345
+ rect.y + rect.height <= content.y + content.height;
346
+ if (!inside) {
347
+ // `cover`, and `none` larger than the box: cut to the content box
348
+ ctx.save();
349
+ ctx.beginPath();
350
+ ctx.rect(content.x, content.y, content.width, content.height);
351
+ ctx.clip();
352
+ }
353
+ try {
354
+ ctx.drawImage(this.image, rect.x, rect.y, rect.width, rect.height);
355
+ } finally {
356
+ if (!inside) ctx.restore();
357
+ }
327
358
  }
328
359
  }
@@ -13,6 +13,7 @@ export const DRAWN_KINDS = new Set([
13
13
  'box',
14
14
  'text',
15
15
  'image',
16
+ 'video',
16
17
  'canvas',
17
18
  'textinput',
18
19
  'textarea',
@@ -147,6 +147,7 @@ const NO_LAYOUT_KINDS = new Set([
147
147
  'text',
148
148
  'textchunk',
149
149
  'image',
150
+ 'video',
150
151
  'svg',
151
152
  'canvas',
152
153
  'textinput',