PageSourceSearch

https://jayreddinjr-jr-odysseus-hf.hf.space/static/js/editor/composite-helpers.js

js jayreddinjr-jr-odysseus-hf.hf.space collected 2026-10-04 17:00:51 UTC 3,004 bytes, 83 lines download raw bytes

1/**
2 * Pure composite helpers — flatten a layer list into a single canvas
3 * for thumbnails / merged-mask use.
4 *
5 * Both helpers are stateless: the caller passes everything they need
6 * (layer list, canvas dimensions, an offsets lookup). The legacy
7 * gallery editor's module-level functions wrap these with their own
8 * state.
9 */
10
11/**
12 * Cheap downscaled preview composited from all visible layers.
13 * Returns a JPEG dataURL, or null when there's nothing to draw.
14 *
15 * @param {Array<{visible: boolean, opacity: number, id: string, canvas: HTMLCanvasElement}>} layers
16 * @param {number} imgW            Document width in canvas pixels.
17 * @param {number} imgH            Document height in canvas pixels.
18 * @param {Map<string,{x:number,y:number}>} offsets  Layer offsets, keyed by id.
19 * @param {number} maxDim          Longest-edge target in CSS pixels.
20 * @param {number} quality         JPEG quality 0..1.
21 * @returns {string|null}
22 */
23export function buildThumbnail(layers, imgW, imgH, offsets, maxDim, quality = 0.6) {
24  if (!imgW || !imgH) return null;
25  try {
26    const scale = Math.min(1, maxDim / Math.max(imgW, imgH));
27    const tw = Math.max(1, Math.round(imgW * scale));
28    const th = Math.max(1, Math.round(imgH * scale));
29    const c = document.createElement('canvas');
30    c.width = tw;
30 c.height = th;
31    const ctx = c.getContext('2d');
32    for (const layer of layers) {
33      if (!layer.visible) continue;
34      ctx.globalAlpha = layer.opacity;
35      const off = offsets.get(layer.id) || { x: 0, y: 0 };
36      ctx.drawImage(
37        layer.canvas,
38        off.x * scale, off.y * scale,
39        layer.canvas.width * scale, layer.canvas.height * scale,
40      );
41    }
42    ctx.globalAlpha = 1;
43    return c.toDataURL('image/jpeg', quality);
44  } catch (_) {
45    return null;
46  }
47}
48
49
50/**
51 * Union of every visible mask sub-layer across `layers`, rendered as a
52 * binary white canvas the size of the document.
53 *
54 * `lighter` composite = additive — overlapping pixels stay clamped at
55 * 255, so wherever any mask painted, the result is solid white.
56 * Returns null when no mask layer contributed any pixels (so the caller
57 * can early-out cleanly).
58 *
59 * @param {Array<{masks?: Array<{visible: boolean, canvas: HTMLCanvasElement}>}>} layers
60 * @param {number} imgW
61 * @param {number} imgH
62 * @returns {HTMLCanvasElement|null}
63 */
64export function buildMergedMaskCanvas(layers, imgW, imgH) {
65  if (!imgW || !imgH) return null;
66  const out = document.createElement('canvas');
67  out.width = imgW;
68  out.height = imgH;
69  const ctx = out.getContext('2d');
70  ctx.globalCompositeOperation = 'lighter';
71  let anyMask = false;
72  for (const ly of layers) {
73    if (!ly.masks || !ly.masks.length) continue;
74    for (const mk of ly.masks) {
75      if (!mk.visible) continue;
76      if (!mk.canvas || !mk.canvas.width || !mk.canvas.height) continue;
77      ctx.drawImage(mk.canvas, 0, 0);
78      anyMask = true;
79    }
80  }
81  ctx.globalCompositeOperation = 'source-over';
82  return anyMask ? out : null;
83}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.