PageSourceSearch

https://rootevidence.com/assets/holo/holo-masks.js?v=397c56b8

js rootevidence.com collected 2026-10-09 22:46:59 UTC 8,177 bytes, 214 lines download raw bytes

1/* holo-masks - Evidence HoloHDR engine: mask rasterizers.
2
3   Every function returns a plain 2D canvas the backends upload as the mask
4   texture (alpha = foil coverage; RED channel = steel glyphs). Two-canvas
5   convention from the reference implementation: the shader texture is a FULL
6   COVERAGE fill (black, or red for steel), while the SHAPE (rounded pill /
7   glyphs) is applied as a CSS mask-image data-URL on the injected canvas -
8   CSS masks clip AFTER the blur filter, so any softening affects only the
9   effect while silhouettes and letter edges stay crisp.
10
11   Premultiply caveat (load-bearing): the canvas-to-GPU upload premultiplies,
12   so steel glyphs are ALWAYS drawn '#f00' at alpha 1 - NEVER partial alpha,
13   the premultiply round trip would corrupt the red value. */
14(function(){
15  'use strict';
16  var H=window.EVD_HOLO;
17  if(!H||H._masks) return;
18
19  var CAP=4096;   /* texture edge cap - shared with both backends */
20
21  function mk(W,H2){
22    var c=document.createElement('canvas');
23    c.width=Math.max(1,Math.min(Math.round(W),CAP));
24    c.height=Math.max(1,Math.min(Math.round(H2),CAP));
25    return c;
26  }
27
28  /* full-coverage fill: the shader shades the whole rect; shape comes from
29     CSS masks (ours below, or the element's own .hl--pat rules) */
30  function fullMask(W,H2,steel){
31    var oc=mk(W,H2);
32    var c=oc.getContext('2d');
33    c.fillStyle=steel?'#f00':'#000';
34    c.fillRect(0,0,oc.width,oc.height);
35    return oc;
36  }
37
38  /* rounded-rect at the element's own computed border radius */
39  function roundedRect(el,W,H2,DPR){
40    var oc=mk(W,H2);
41    var c=oc.getContext('2d');
42    var rad=parseFloat(getComputedStyle(el).borderTopLeftRadius)||0;
43    /* clamp to min(w,h)/2: pill radii (9999px, .hl--btn) rasterize as true
44       stadium caps instead of a corrupt path */
45    var r=Math.min(rad*DPR,oc.width/2,oc.height/2);
46    var w=oc.width,h=oc.height;
47    /* black fill: the shape read is ALPHA only, red stays 0 for steel */
48    c.fillStyle='#000';
49    c.beginPath();
50    c.moveTo(r,0); c.lineTo(w-r,0); c.arcTo(w,0,w,r,r);
51    c.lineTo(w,h-r); c.arcTo(w,h,w-r,h,r);
52    c.lineTo(r,h); c.arcTo(0,h,0,h-r,r);
53    c.lineTo(0,r); c.arcTo(0,0,r,0,r);
54    c.closePath(); c.fill();
55    return oc;
56  }
57
58  /* the element's own text rasterised as glyphs (steel masks). Pass the
59     previous raster as `reuse` to redraw in place (EVD_HOLO.refresh's cheap
60     path for live steel counters - no per-tick canvas allocation). */
61  function glyphs(el,W,H2,DPR,reuse){
62    var oc=reuse||mk(W,H2);
63    if(reuse){
64      var rw=Math.max(1,Math.min(Math.round(W),CAP));
65      var rh=Math.max(1,Math.min(Math.round(H2),CAP));
66      if(oc.width!==rw) oc.width=rw;
67      if(oc.height!==rh) oc.height=rh;
68    }
69    var c=oc.getContext('2d');
70    c.clearRect(0,0,oc.width,oc.height);
71    var style=getComputedStyle(el);
72    var fpx=parseFloat(style.fontSize)*DPR;
73    c.fillStyle='#f00';
74    c.font=style.fontStyle+' '+style.fontWeight+' '+fpx+'px '+style.fontFamily;
75    var ls=parseFloat(style.letterSpacing);
76    if(!isNaN(ls)) c.letterSpacing=(ls*DPR)+'px';
77    c.textBaseline='middle';
78    var text=el.textContent;
79    c.fillText(text,
80      (oc.width-c.measureText(text).width)/2,
81      oc.height*0.5+fpx*0.04);   /* small optical drop */
82    return oc;
83  }
84
85  /* fit an image into the box: 'cover' anchors bottom-right, 'left'/'right'
86     scale to width; capped at 4096 by mk() */
87  function svgFit(img,W,H2,fit){
88    var oc=mk(W,H2);
89    var c=oc.getContext('2d');
90    var iw=img.naturalWidth||img.width,ih=img.naturalHeight||img.height;
91    if(!iw||!ih) return oc;
92    var s,dw,dh;
93    if(fit==='cover'){
94      s=Math.max(oc.width/iw,oc.height/ih); dw=iw*s; dh=ih*s;
95      c.drawImage(img,oc.width-dw,oc.height-dh,dw,dh);   /* anchor bottom right */
96    }else{
97      s=oc.width/iw; dw=oc.width; dh=ih*s;
98      c.drawImage(img,0,0,dw,dh);
99    }
100    return oc;
101  }
102
103  /* vertical band: fill the band's width and TILE down the full height
104     (the artworks are repeating columns) */
105  function svgTile(img,W,H2){
106    var oc=mk(W,H2);
107    var c=oc.getContext('2d');
108    var iw=img.naturalWidth||img.width,ih=img.naturalHeight||img.height;
109    if(!iw||!ih) return oc;
110    var s=oc.width/iw, dw=oc.width, dh=ih*s;
111    for(var ty=0;ty<oc.height;ty+=dh){ c.drawImage(img,0,ty,dw,dh); }
112    return oc;
113  }
114
115  /* composite: rounded ring stroke (tier ring frame) with an optional shape
116     drawn inside. The tier card covers the centre, so the plain plate works
117     too - the stroke variant keeps the alpha honest for translucent pages. */
118  function composite(el,W,H2,DPR,opts){
119    opts=opts||{};
120    var oc=roundedRect(el,W,H2,DPR);
121    if(opts.ring){
122      var c=oc.getContext('2d');
123      var t=Math.max(2,(opts.ringWidth||8)*DPR);
124      c.globalCompositeOperation='destination-out';
125      var rad=parseFloat(getComputedStyle(el).borderTopLeftRadius)||0;
126      var r=Math.max(0,Math.min(rad*DPR,oc.height/2)-t);
127      var x=t,y=t,w=oc.width-2*t,h=oc.height-2*t;
128      c.beginPath();
129      c.moveTo(x+r,y); c.lineTo(x+w-r,y); c.arcTo(x+w,y,x+w,y+r,r);
130      c.lineTo(x+w,y+h-r); c.arcTo(x+w,y+h,x+w-r,y+h,r);
131      c.lineTo(x+r,y+h); c.arcTo(x,y+h,x,y+h-r,r);
132      c.lineTo(x,y+r); c.arcTo(x,y,x+r,y,r);
133      c.closePath(); c.fill();
134      c.globalCompositeOperation='source-over';
135    }
136    return oc;
137  }
138
139  /* the live globe mask contract: the backend re-uploads the registered
140     canvas whenever its version advances (published by holo-globe.js) */
141  function liveGlobe(){
142    return window.EVD_HOLO_GLOBE||null;
143  }
144
145  /* the SHAPE canvas applied as a CSS mask on the injected canvas: clips
146     after blur, silhouettes stay crisp */
147  function applyCssMask(canvas,shape){
148    try{
149      var du=shape.toDataURL();
150      canvas.style.webkitMaskImage='url('+du+')';
151      canvas.style.maskImage='url('+du+')';
152      canvas.style.webkitMaskSize='100% 100%';
153      canvas.style.maskSize='100% 100%';
154      /* reset any leftover intersect layering (surface re-classified) */
155      canvas.style.webkitMaskPosition='';
156      canvas.style.maskPosition='';
157      canvas.style.webkitMaskRepeat='';
158      canvas.style.maskRepeat='';
159      canvas.style.webkitMaskComposite='';
160      canvas.style.maskComposite='';
161    }catch(e){}
162  }
163
164  /* glyph ∩ pattern: two comma-layered mask images - the rastered glyph
165     shape first (stretched over the box, like applyCssMask), the pattern
166     tile second (center-anchored, .hl--pat-* conventions) - composited with
167     mask-composite:intersect (standard) / -webkit-mask-composite:source-in
168     (legacy alias of the same op), so the foil only shows where BOTH the
169     glyphs and the tile have alpha. tileSize defaults to 'cover' to mirror
170     the .hl--pat-* rules; pass e.g. 'auto 100%' for repeating strips. */
171  function applyCssMaskIntersect(canvas,shape,tileUrl,tileSize){
172    try{
173      var du=shape.toDataURL();
174      var layers='url('+du+'), url("'+tileUrl+'")';
175      var size='100% 100%, '+(tileSize||'cover');
176      canvas.style.webkitMaskImage=layers;
177      canvas.style.maskImage=layers;
178      canvas.style.webkitMaskSize=size;
179      canvas.style.maskSize=size;
180      canvas.style.webkitMaskPosition='0 0, center';
181      canvas.style.maskPosition='0 0, center';
182      canvas.style.webkitMaskRepeat='no-repeat, no-repeat';
183      canvas.style.maskRepeat='no-repeat, no-repeat';
184      canvas.style.webkitMaskComposite='source-in';
185      canvas.style.maskComposite='intersect';
186    }catch(e){}
187  }
188  function clearCssMask(canvas){
189    canvas.style.webkitMaskImage='';
190    canvas.style.maskImage='';
191    canvas.style.webkitMaskSize='';
192    canvas.style.maskSize='';
193    canvas.style.webkitMaskPosition='';
194    canvas.style.maskPosition='';
195    canvas.style.webkitMaskRepeat='';
196    canvas.style.maskRepeat='';
197    canvas.style.webkitMaskComposite='';
198    canvas.style.maskComposite='';
199  }
200
201  H._masks={
202    CAP:CAP,
203    fullMask:fullMask,
204    roundedRect:roundedRect,
205    glyphs:glyphs,
206    svgFit:svgFit,
207    svgTile:svgTile,
208    composite:composite,
209    liveGlobe:liveGlobe,
210    applyCssMask:applyCssMask,
211    applyCssMaskIntersect:applyCssMaskIntersect,
212    clearCssMask:clearCssMask
213  };
214})();

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.