git-hash-art 0.12.0 → 0.13.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.
- package/CHANGELOG.md +8 -0
- package/dist/browser.js +85 -85
- package/dist/browser.js.map +1 -1
- package/dist/main.js +85 -85
- package/dist/main.js.map +1 -1
- package/dist/module.js +85 -85
- package/dist/module.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/phase-breakdown.test.ts +44 -0
- package/src/lib/render.ts +96 -89
- package/src/types.ts +2 -0
package/dist/main.js
CHANGED
|
@@ -4607,6 +4607,15 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4607
4607
|
...(0, $93cf69256c93baa9$export$c2f8e0cc249a8d8f),
|
|
4608
4608
|
...config
|
|
4609
4609
|
};
|
|
4610
|
+
const _dt = finalConfig._debugTiming;
|
|
4611
|
+
const _t = _dt ? ()=>performance.now() : undefined;
|
|
4612
|
+
let _p = _t ? _t() : 0;
|
|
4613
|
+
function _mark(name) {
|
|
4614
|
+
if (!_dt || !_t) return;
|
|
4615
|
+
const now = _t();
|
|
4616
|
+
_dt.phases[name] = now - _p;
|
|
4617
|
+
_p = now;
|
|
4618
|
+
}
|
|
4610
4619
|
const rng = (0, $e4b03e131ed2a289$export$eaf9227667332084)((0, $e4b03e131ed2a289$export$e9cc707de01b7042)(gitHash));
|
|
4611
4620
|
// ── 0. Select archetype — fundamentally different visual personality ──
|
|
4612
4621
|
const archetype = (0, $f89bc858f7202849$export$f1142fd7da4d6590)(rng);
|
|
@@ -4640,12 +4649,14 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4640
4649
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
4641
4650
|
const cx = width / 2;
|
|
4642
4651
|
const cy = height / 2;
|
|
4652
|
+
_mark("0_setup");
|
|
4643
4653
|
// ── 1. Background ──────────────────────────────────────────────
|
|
4644
4654
|
const bgRadius = Math.hypot(cx, cy);
|
|
4645
4655
|
$4f72c5a314eddf25$var$drawBackground(ctx, archetype.backgroundStyle, bgStart, bgEnd, width, height, cx, cy, bgRadius, rng, colors);
|
|
4646
4656
|
// Gradient mesh overlay — 3-4 color control points for richer backgrounds
|
|
4657
|
+
// Use source-over instead of soft-light for cheaper compositing
|
|
4647
4658
|
const meshPoints = 3 + Math.floor(rng() * 2);
|
|
4648
|
-
ctx.
|
|
4659
|
+
ctx.globalAlpha = 1;
|
|
4649
4660
|
for(let i = 0; i < meshPoints; i++){
|
|
4650
4661
|
const mx = rng() * width;
|
|
4651
4662
|
const my = rng() * height;
|
|
@@ -4654,95 +4665,103 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4654
4665
|
const grad = ctx.createRadialGradient(mx, my, 0, mx, my, mRadius);
|
|
4655
4666
|
grad.addColorStop(0, (0, $d016ad53434219a1$export$f2121afcad3d553f)(mColor, 0.08 + rng() * 0.06));
|
|
4656
4667
|
grad.addColorStop(1, "rgba(0,0,0,0)");
|
|
4657
|
-
ctx.globalAlpha = 1;
|
|
4658
4668
|
ctx.fillStyle = grad;
|
|
4659
|
-
|
|
4669
|
+
// Clip to gradient bounding box — avoids blending transparent pixels
|
|
4670
|
+
const gx = Math.max(0, mx - mRadius);
|
|
4671
|
+
const gy = Math.max(0, my - mRadius);
|
|
4672
|
+
const gw = Math.min(width, mx + mRadius) - gx;
|
|
4673
|
+
const gh = Math.min(height, my + mRadius) - gy;
|
|
4674
|
+
ctx.fillRect(gx, gy, gw, gh);
|
|
4660
4675
|
}
|
|
4661
|
-
ctx.globalCompositeOperation = "source-over";
|
|
4662
4676
|
// Compute average background luminance for contrast enforcement
|
|
4663
4677
|
const bgLum = ((0, $d016ad53434219a1$export$5c6e3c2b59b7fbbe)(bgStart) + (0, $d016ad53434219a1$export$5c6e3c2b59b7fbbe)(bgEnd)) / 2;
|
|
4664
4678
|
// ── 1b. Layered background — archetype-coherent shapes ─────────
|
|
4679
|
+
// Use source-over with pre-multiplied alpha instead of soft-light
|
|
4680
|
+
// for much cheaper compositing (soft-light requires per-pixel blend)
|
|
4665
4681
|
const bgShapeCount = 3 + Math.floor(rng() * 4);
|
|
4666
|
-
ctx.globalCompositeOperation = "soft-light";
|
|
4667
4682
|
for(let i = 0; i < bgShapeCount; i++){
|
|
4668
4683
|
const bx = rng() * width;
|
|
4669
4684
|
const by = rng() * height;
|
|
4670
4685
|
const bSize = width * 0.3 + rng() * width * 0.5;
|
|
4671
4686
|
const bColor = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
4672
|
-
ctx.globalAlpha = 0.03 + rng() * 0.05;
|
|
4687
|
+
ctx.globalAlpha = (0.03 + rng() * 0.05) * 0.5; // halved to compensate for source-over vs soft-light
|
|
4673
4688
|
ctx.fillStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(bColor, 0.15);
|
|
4674
4689
|
ctx.beginPath();
|
|
4675
4690
|
// Use archetype-appropriate background shapes
|
|
4676
|
-
if (archetype.name === "geometric-precision" || archetype.name === "op-art")
|
|
4677
|
-
ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
4691
|
+
if (archetype.name === "geometric-precision" || archetype.name === "op-art") ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
4678
4692
|
else ctx.arc(bx, by, bSize / 2, 0, Math.PI * 2);
|
|
4679
4693
|
ctx.fill();
|
|
4680
4694
|
}
|
|
4681
|
-
// Subtle concentric rings from center
|
|
4695
|
+
// Subtle concentric rings from center — batched into single stroke
|
|
4682
4696
|
const ringCount = 2 + Math.floor(rng() * 3);
|
|
4683
4697
|
ctx.globalAlpha = 0.02 + rng() * 0.03;
|
|
4684
4698
|
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
4685
4699
|
ctx.lineWidth = 1 * scaleFactor;
|
|
4700
|
+
ctx.beginPath();
|
|
4686
4701
|
for(let i = 1; i <= ringCount; i++){
|
|
4687
4702
|
const r = Math.min(width, height) * 0.15 * i;
|
|
4688
|
-
ctx.
|
|
4703
|
+
ctx.moveTo(cx + r, cy);
|
|
4689
4704
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
4690
|
-
ctx.stroke();
|
|
4691
4705
|
}
|
|
4692
|
-
ctx.
|
|
4706
|
+
ctx.stroke();
|
|
4693
4707
|
// ── 1c. Background pattern layer — subtle textured paper ───────
|
|
4694
4708
|
const bgPatternRoll = rng();
|
|
4695
4709
|
if (bgPatternRoll < 0.6) {
|
|
4696
4710
|
ctx.save();
|
|
4697
|
-
ctx.globalCompositeOperation = "soft-light";
|
|
4698
4711
|
const patternOpacity = 0.02 + rng() * 0.04;
|
|
4699
4712
|
const patternColor = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.15);
|
|
4700
4713
|
if (bgPatternRoll < 0.2) {
|
|
4701
|
-
// Dot grid —
|
|
4702
|
-
const dotSpacing = Math.max(
|
|
4703
|
-
const
|
|
4714
|
+
// Dot grid — use fillRect instead of arcs (much cheaper, no path building)
|
|
4715
|
+
const dotSpacing = Math.max(12, Math.min(width, height) * (0.015 + rng() * 0.015));
|
|
4716
|
+
const dotDiam = Math.max(1, Math.round(dotSpacing * 0.16));
|
|
4704
4717
|
ctx.globalAlpha = patternOpacity;
|
|
4705
4718
|
ctx.fillStyle = patternColor;
|
|
4706
|
-
|
|
4707
|
-
for(let px = 0; px < width; px += dotSpacing)for(let py = 0; py < height; py += dotSpacing){
|
|
4708
|
-
ctx.
|
|
4709
|
-
|
|
4719
|
+
let dotCount = 0;
|
|
4720
|
+
for(let px = 0; px < width && dotCount < 2000; px += dotSpacing)for(let py = 0; py < height && dotCount < 2000; py += dotSpacing){
|
|
4721
|
+
ctx.fillRect(px, py, dotDiam, dotDiam);
|
|
4722
|
+
dotCount++;
|
|
4710
4723
|
}
|
|
4711
|
-
ctx.fill();
|
|
4712
4724
|
} else if (bgPatternRoll < 0.4) {
|
|
4713
|
-
// Diagonal lines — batched into a single path
|
|
4714
|
-
const lineSpacing = Math.max(
|
|
4725
|
+
// Diagonal lines — batched into a single path, capped at 300 lines
|
|
4726
|
+
const lineSpacing = Math.max(10, Math.min(width, height) * (0.02 + rng() * 0.02));
|
|
4715
4727
|
ctx.globalAlpha = patternOpacity;
|
|
4716
4728
|
ctx.strokeStyle = patternColor;
|
|
4717
4729
|
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4718
4730
|
const diag = Math.hypot(width, height);
|
|
4719
4731
|
ctx.beginPath();
|
|
4720
|
-
|
|
4732
|
+
let lineCount = 0;
|
|
4733
|
+
for(let d = -diag; d < diag && lineCount < 300; d += lineSpacing){
|
|
4721
4734
|
ctx.moveTo(d, 0);
|
|
4722
4735
|
ctx.lineTo(d + height, height);
|
|
4736
|
+
lineCount++;
|
|
4723
4737
|
}
|
|
4724
4738
|
ctx.stroke();
|
|
4725
4739
|
} else {
|
|
4726
|
-
// Tessellation — hexagonal grid,
|
|
4727
|
-
const tessSize = Math.max(
|
|
4740
|
+
// Tessellation — hexagonal grid, capped at 500 hexagons
|
|
4741
|
+
const tessSize = Math.max(15, Math.min(width, height) * (0.025 + rng() * 0.02));
|
|
4728
4742
|
const tessH = tessSize * Math.sqrt(3);
|
|
4729
4743
|
ctx.globalAlpha = patternOpacity * 0.7;
|
|
4730
4744
|
ctx.strokeStyle = patternColor;
|
|
4731
4745
|
ctx.lineWidth = 0.4 * scaleFactor;
|
|
4746
|
+
// Pre-compute hex vertex offsets (avoid trig per vertex)
|
|
4747
|
+
const hexVx = [];
|
|
4748
|
+
const hexVy = [];
|
|
4749
|
+
for(let s = 0; s < 6; s++){
|
|
4750
|
+
const angle = Math.PI / 3 * s - Math.PI / 6;
|
|
4751
|
+
hexVx.push(Math.cos(angle) * tessSize * 0.5);
|
|
4752
|
+
hexVy.push(Math.sin(angle) * tessSize * 0.5);
|
|
4753
|
+
}
|
|
4732
4754
|
ctx.beginPath();
|
|
4733
|
-
|
|
4755
|
+
let hexCount = 0;
|
|
4756
|
+
for(let row = 0; row * tessH < height + tessH && hexCount < 500; row++){
|
|
4734
4757
|
const offsetX = row % 2 * tessSize * 0.75;
|
|
4735
|
-
for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5; col++){
|
|
4758
|
+
for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5 && hexCount < 500; col++){
|
|
4736
4759
|
const hx = col * tessSize * 1.5 + offsetX;
|
|
4737
4760
|
const hy = row * tessH;
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
const vx = hx + Math.cos(angle) * tessSize * 0.5;
|
|
4741
|
-
const vy = hy + Math.sin(angle) * tessSize * 0.5;
|
|
4742
|
-
if (s === 0) ctx.moveTo(vx, vy);
|
|
4743
|
-
else ctx.lineTo(vx, vy);
|
|
4744
|
-
}
|
|
4761
|
+
ctx.moveTo(hx + hexVx[0], hy + hexVy[0]);
|
|
4762
|
+
for(let s = 1; s < 6; s++)ctx.lineTo(hx + hexVx[s], hy + hexVy[s]);
|
|
4745
4763
|
ctx.closePath();
|
|
4764
|
+
hexCount++;
|
|
4746
4765
|
}
|
|
4747
4766
|
}
|
|
4748
4767
|
ctx.stroke();
|
|
@@ -4750,6 +4769,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4750
4769
|
ctx.restore();
|
|
4751
4770
|
}
|
|
4752
4771
|
ctx.globalCompositeOperation = "source-over";
|
|
4772
|
+
_mark("1_background");
|
|
4753
4773
|
// ── 2. Composition mode — archetype-aware selection ──────────────
|
|
4754
4774
|
const compositionMode = rng() < 0.7 ? archetype.preferredCompositions[Math.floor(rng() * archetype.preferredCompositions.length)] : $4f72c5a314eddf25$var$ALL_COMPOSITION_MODES[Math.floor(rng() * $4f72c5a314eddf25$var$ALL_COMPOSITION_MODES.length)];
|
|
4755
4775
|
const symRoll = rng();
|
|
@@ -4860,6 +4880,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4860
4880
|
}
|
|
4861
4881
|
}
|
|
4862
4882
|
ctx.globalAlpha = 1;
|
|
4883
|
+
_mark("2_3_composition_focal");
|
|
4863
4884
|
// ── 4. Flow field — simplex noise for organic variation ─────────
|
|
4864
4885
|
// Create a seeded simplex noise field (unique per hash)
|
|
4865
4886
|
const noiseFieldRng = (0, $e4b03e131ed2a289$export$eaf9227667332084)((0, $e4b03e131ed2a289$export$e9cc707de01b7042)(gitHash, 333));
|
|
@@ -4936,6 +4957,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4936
4957
|
shape: heroShape
|
|
4937
4958
|
});
|
|
4938
4959
|
}
|
|
4960
|
+
_mark("4_flowfield_hero");
|
|
4939
4961
|
// ── 5. Shape layers ────────────────────────────────────────────
|
|
4940
4962
|
const maxLocalDensity = Math.ceil(shapesPerLayer * 0.15);
|
|
4941
4963
|
// ── Complexity budget — caps total rendering work ──────────────
|
|
@@ -5364,6 +5386,11 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5364
5386
|
}
|
|
5365
5387
|
// Reset blend mode for post-processing passes
|
|
5366
5388
|
ctx.globalCompositeOperation = "source-over";
|
|
5389
|
+
if (_dt) {
|
|
5390
|
+
_dt.shapeCount = shapePositions.length;
|
|
5391
|
+
_dt.extraCount = extrasSpent;
|
|
5392
|
+
}
|
|
5393
|
+
_mark("5_shape_layers");
|
|
5367
5394
|
// ── 5g. Layered masking / cutout portals ───────────────────────
|
|
5368
5395
|
// ~18% of images get 1-3 portal windows that paint over foreground
|
|
5369
5396
|
// with a tinted background wash, creating a "peek through" effect.
|
|
@@ -5422,6 +5449,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5422
5449
|
ctx.restore();
|
|
5423
5450
|
}
|
|
5424
5451
|
}
|
|
5452
|
+
_mark("5g_portals");
|
|
5425
5453
|
// ── 6. Flow-line pass — variable color, branching, pressure ────
|
|
5426
5454
|
// Optimized: collect all segments into width-quantized buckets, then
|
|
5427
5455
|
// render each bucket as a single batched path. This reduces
|
|
@@ -5547,6 +5575,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5547
5575
|
ctx.stroke();
|
|
5548
5576
|
}
|
|
5549
5577
|
}
|
|
5578
|
+
_mark("6_flow_lines");
|
|
5550
5579
|
// ── 6b. Motion/energy lines — short directional bursts ─────────
|
|
5551
5580
|
// Optimized: collect all burst segments, then batch by quantized alpha
|
|
5552
5581
|
const energyArchetypes = [
|
|
@@ -5609,6 +5638,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5609
5638
|
ctx.stroke();
|
|
5610
5639
|
}
|
|
5611
5640
|
}
|
|
5641
|
+
_mark("6b_energy_lines");
|
|
5612
5642
|
// ── 6c. Apply symmetry mirroring ─────────────────────────────────
|
|
5613
5643
|
if (symmetryMode !== "none") {
|
|
5614
5644
|
const canvas = ctx.canvas;
|
|
@@ -5629,60 +5659,25 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5629
5659
|
}
|
|
5630
5660
|
ctx.restore();
|
|
5631
5661
|
}
|
|
5632
|
-
|
|
5633
|
-
//
|
|
5634
|
-
//
|
|
5662
|
+
_mark("6c_symmetry");
|
|
5663
|
+
// ── 7. Noise texture overlay ─────────────────────────────────────
|
|
5664
|
+
// With density capped at 2500 dots, direct fillRect calls are far cheaper
|
|
5665
|
+
// than the getImageData/putImageData round-trip which copies the entire
|
|
5666
|
+
// pixel buffer (4 × width × height bytes) twice.
|
|
5635
5667
|
const noiseRng = (0, $e4b03e131ed2a289$export$eaf9227667332084)((0, $e4b03e131ed2a289$export$e9cc707de01b7042)(gitHash, 777));
|
|
5636
5668
|
const rawNoiseDensity = Math.floor(width * height / 800);
|
|
5637
|
-
// Cap at 2500 dots — beyond this the visual effect is indistinguishable
|
|
5638
|
-
// but getImageData/putImageData cost scales with canvas size
|
|
5639
5669
|
const noiseDensity = Math.min(rawNoiseDensity, 2500);
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
const
|
|
5643
|
-
const
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5650
|
-
// srcA in range [0.01, 0.04] — multiply by 256 for fixed-point
|
|
5651
|
-
const srcA256 = Math.round((0.01 + noiseRng() * 0.03) * 256);
|
|
5652
|
-
const invA256 = 256 - srcA256;
|
|
5653
|
-
const bSrc = brightness * srcA256; // pre-multiply brightness × alpha
|
|
5654
|
-
const idx = ny * width + nx << 2;
|
|
5655
|
-
data[idx] = data[idx] * invA256 + bSrc >> 8;
|
|
5656
|
-
data[idx + 1] = data[idx + 1] * invA256 + bSrc >> 8;
|
|
5657
|
-
data[idx + 2] = data[idx + 2] * invA256 + bSrc >> 8;
|
|
5658
|
-
}
|
|
5659
|
-
else for(let i = 0; i < noiseDensity; i++){
|
|
5660
|
-
const nx = Math.floor(noiseRng() * width);
|
|
5661
|
-
const ny = Math.floor(noiseRng() * height);
|
|
5662
|
-
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5663
|
-
const srcA256 = Math.round((0.01 + noiseRng() * 0.03) * 256);
|
|
5664
|
-
const invA256 = 256 - srcA256;
|
|
5665
|
-
const bSrc = brightness * srcA256;
|
|
5666
|
-
for(let dy = 0; dy < pixelScale && ny + dy < height; dy++)for(let dx = 0; dx < pixelScale && nx + dx < width; dx++){
|
|
5667
|
-
const idx = (ny + dy) * width + (nx + dx) << 2;
|
|
5668
|
-
data[idx] = data[idx] * invA256 + bSrc >> 8;
|
|
5669
|
-
data[idx + 1] = data[idx + 1] * invA256 + bSrc >> 8;
|
|
5670
|
-
data[idx + 2] = data[idx + 2] * invA256 + bSrc >> 8;
|
|
5671
|
-
}
|
|
5672
|
-
}
|
|
5673
|
-
ctx.putImageData(imageData, 0, 0);
|
|
5674
|
-
} catch {
|
|
5675
|
-
// Fallback for environments where getImageData isn't available (e.g. some OffscreenCanvas)
|
|
5676
|
-
for(let i = 0; i < noiseDensity; i++){
|
|
5677
|
-
const nx = noiseRng() * width;
|
|
5678
|
-
const ny = noiseRng() * height;
|
|
5679
|
-
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5680
|
-
const alpha = 0.01 + noiseRng() * 0.03;
|
|
5681
|
-
ctx.globalAlpha = alpha;
|
|
5682
|
-
ctx.fillStyle = `rgba(${brightness},${brightness},${brightness},1)`;
|
|
5683
|
-
ctx.fillRect(nx, ny, 1 * scaleFactor, 1 * scaleFactor);
|
|
5684
|
-
}
|
|
5670
|
+
const pixelScale = Math.max(1, Math.round(scaleFactor));
|
|
5671
|
+
for(let i = 0; i < noiseDensity; i++){
|
|
5672
|
+
const nx = noiseRng() * width;
|
|
5673
|
+
const ny = noiseRng() * height;
|
|
5674
|
+
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5675
|
+
const alpha = 0.01 + noiseRng() * 0.03;
|
|
5676
|
+
ctx.globalAlpha = alpha;
|
|
5677
|
+
ctx.fillStyle = `rgb(${brightness},${brightness},${brightness})`;
|
|
5678
|
+
ctx.fillRect(nx, ny, pixelScale, pixelScale);
|
|
5685
5679
|
}
|
|
5680
|
+
_mark("7_noise_texture");
|
|
5686
5681
|
// ── 8. Vignette — darken edges to draw the eye inward ───────────
|
|
5687
5682
|
ctx.globalAlpha = 1;
|
|
5688
5683
|
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
@@ -5696,6 +5691,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5696
5691
|
vigGrad.addColorStop(1, vignetteColor);
|
|
5697
5692
|
ctx.fillStyle = vigGrad;
|
|
5698
5693
|
ctx.fillRect(0, 0, width, height);
|
|
5694
|
+
_mark("8_vignette");
|
|
5699
5695
|
// ── 9. Organic connecting curves — proximity-aware ───────────────
|
|
5700
5696
|
// Optimized: batch all curves into alpha-quantized groups to reduce
|
|
5701
5697
|
// beginPath/stroke calls from O(numCurves) to O(alphaBuckets).
|
|
@@ -5754,6 +5750,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5754
5750
|
ctx.stroke();
|
|
5755
5751
|
}
|
|
5756
5752
|
}
|
|
5753
|
+
_mark("9_connecting_curves");
|
|
5757
5754
|
// ── 10. Post-processing ────────────────────────────────────────
|
|
5758
5755
|
// 10a. Color grading — unified tone across the whole image
|
|
5759
5756
|
// Apply as a semi-transparent overlay in the grade hue
|
|
@@ -5813,6 +5810,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5813
5810
|
ctx.fillRect(0, 0, width, height);
|
|
5814
5811
|
ctx.globalCompositeOperation = "source-over";
|
|
5815
5812
|
}
|
|
5813
|
+
_mark("10_post_processing");
|
|
5816
5814
|
// ── 10e. Generative borders — archetype-driven decorative frames ──
|
|
5817
5815
|
{
|
|
5818
5816
|
ctx.save();
|
|
@@ -5979,6 +5977,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5979
5977
|
// Other archetypes: no border (intentional — not every image needs one)
|
|
5980
5978
|
ctx.restore();
|
|
5981
5979
|
}
|
|
5980
|
+
_mark("10e_borders");
|
|
5982
5981
|
// ── 11. Signature mark — placed in the least-dense corner ──────
|
|
5983
5982
|
{
|
|
5984
5983
|
const sigRng = (0, $e4b03e131ed2a289$export$eaf9227667332084)((0, $e4b03e131ed2a289$export$e9cc707de01b7042)(gitHash, 42));
|
|
@@ -6046,6 +6045,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
6046
6045
|
ctx.restore();
|
|
6047
6046
|
}
|
|
6048
6047
|
ctx.globalAlpha = 1;
|
|
6048
|
+
_mark("11_signature");
|
|
6049
6049
|
}
|
|
6050
6050
|
|
|
6051
6051
|
|