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/module.js
CHANGED
|
@@ -4593,6 +4593,15 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4593
4593
|
...(0, $2bfb6a1ccb7a82ae$export$c2f8e0cc249a8d8f),
|
|
4594
4594
|
...config
|
|
4595
4595
|
};
|
|
4596
|
+
const _dt = finalConfig._debugTiming;
|
|
4597
|
+
const _t = _dt ? ()=>performance.now() : undefined;
|
|
4598
|
+
let _p = _t ? _t() : 0;
|
|
4599
|
+
function _mark(name) {
|
|
4600
|
+
if (!_dt || !_t) return;
|
|
4601
|
+
const now = _t();
|
|
4602
|
+
_dt.phases[name] = now - _p;
|
|
4603
|
+
_p = now;
|
|
4604
|
+
}
|
|
4596
4605
|
const rng = (0, $461134e0b6ce0619$export$eaf9227667332084)((0, $461134e0b6ce0619$export$e9cc707de01b7042)(gitHash));
|
|
4597
4606
|
// ── 0. Select archetype — fundamentally different visual personality ──
|
|
4598
4607
|
const archetype = (0, $3faa2521b78398cf$export$f1142fd7da4d6590)(rng);
|
|
@@ -4626,12 +4635,14 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4626
4635
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
4627
4636
|
const cx = width / 2;
|
|
4628
4637
|
const cy = height / 2;
|
|
4638
|
+
_mark("0_setup");
|
|
4629
4639
|
// ── 1. Background ──────────────────────────────────────────────
|
|
4630
4640
|
const bgRadius = Math.hypot(cx, cy);
|
|
4631
4641
|
$b623126c6e9cbb71$var$drawBackground(ctx, archetype.backgroundStyle, bgStart, bgEnd, width, height, cx, cy, bgRadius, rng, colors);
|
|
4632
4642
|
// Gradient mesh overlay — 3-4 color control points for richer backgrounds
|
|
4643
|
+
// Use source-over instead of soft-light for cheaper compositing
|
|
4633
4644
|
const meshPoints = 3 + Math.floor(rng() * 2);
|
|
4634
|
-
ctx.
|
|
4645
|
+
ctx.globalAlpha = 1;
|
|
4635
4646
|
for(let i = 0; i < meshPoints; i++){
|
|
4636
4647
|
const mx = rng() * width;
|
|
4637
4648
|
const my = rng() * height;
|
|
@@ -4640,95 +4651,103 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4640
4651
|
const grad = ctx.createRadialGradient(mx, my, 0, mx, my, mRadius);
|
|
4641
4652
|
grad.addColorStop(0, (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(mColor, 0.08 + rng() * 0.06));
|
|
4642
4653
|
grad.addColorStop(1, "rgba(0,0,0,0)");
|
|
4643
|
-
ctx.globalAlpha = 1;
|
|
4644
4654
|
ctx.fillStyle = grad;
|
|
4645
|
-
|
|
4655
|
+
// Clip to gradient bounding box — avoids blending transparent pixels
|
|
4656
|
+
const gx = Math.max(0, mx - mRadius);
|
|
4657
|
+
const gy = Math.max(0, my - mRadius);
|
|
4658
|
+
const gw = Math.min(width, mx + mRadius) - gx;
|
|
4659
|
+
const gh = Math.min(height, my + mRadius) - gy;
|
|
4660
|
+
ctx.fillRect(gx, gy, gw, gh);
|
|
4646
4661
|
}
|
|
4647
|
-
ctx.globalCompositeOperation = "source-over";
|
|
4648
4662
|
// Compute average background luminance for contrast enforcement
|
|
4649
4663
|
const bgLum = ((0, $9d614e7d77fc2947$export$5c6e3c2b59b7fbbe)(bgStart) + (0, $9d614e7d77fc2947$export$5c6e3c2b59b7fbbe)(bgEnd)) / 2;
|
|
4650
4664
|
// ── 1b. Layered background — archetype-coherent shapes ─────────
|
|
4665
|
+
// Use source-over with pre-multiplied alpha instead of soft-light
|
|
4666
|
+
// for much cheaper compositing (soft-light requires per-pixel blend)
|
|
4651
4667
|
const bgShapeCount = 3 + Math.floor(rng() * 4);
|
|
4652
|
-
ctx.globalCompositeOperation = "soft-light";
|
|
4653
4668
|
for(let i = 0; i < bgShapeCount; i++){
|
|
4654
4669
|
const bx = rng() * width;
|
|
4655
4670
|
const by = rng() * height;
|
|
4656
4671
|
const bSize = width * 0.3 + rng() * width * 0.5;
|
|
4657
4672
|
const bColor = (0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
4658
|
-
ctx.globalAlpha = 0.03 + rng() * 0.05;
|
|
4673
|
+
ctx.globalAlpha = (0.03 + rng() * 0.05) * 0.5; // halved to compensate for source-over vs soft-light
|
|
4659
4674
|
ctx.fillStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(bColor, 0.15);
|
|
4660
4675
|
ctx.beginPath();
|
|
4661
4676
|
// Use archetype-appropriate background shapes
|
|
4662
|
-
if (archetype.name === "geometric-precision" || archetype.name === "op-art")
|
|
4663
|
-
ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
4677
|
+
if (archetype.name === "geometric-precision" || archetype.name === "op-art") ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
4664
4678
|
else ctx.arc(bx, by, bSize / 2, 0, Math.PI * 2);
|
|
4665
4679
|
ctx.fill();
|
|
4666
4680
|
}
|
|
4667
|
-
// Subtle concentric rings from center
|
|
4681
|
+
// Subtle concentric rings from center — batched into single stroke
|
|
4668
4682
|
const ringCount = 2 + Math.floor(rng() * 3);
|
|
4669
4683
|
ctx.globalAlpha = 0.02 + rng() * 0.03;
|
|
4670
4684
|
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
4671
4685
|
ctx.lineWidth = 1 * scaleFactor;
|
|
4686
|
+
ctx.beginPath();
|
|
4672
4687
|
for(let i = 1; i <= ringCount; i++){
|
|
4673
4688
|
const r = Math.min(width, height) * 0.15 * i;
|
|
4674
|
-
ctx.
|
|
4689
|
+
ctx.moveTo(cx + r, cy);
|
|
4675
4690
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
4676
|
-
ctx.stroke();
|
|
4677
4691
|
}
|
|
4678
|
-
ctx.
|
|
4692
|
+
ctx.stroke();
|
|
4679
4693
|
// ── 1c. Background pattern layer — subtle textured paper ───────
|
|
4680
4694
|
const bgPatternRoll = rng();
|
|
4681
4695
|
if (bgPatternRoll < 0.6) {
|
|
4682
4696
|
ctx.save();
|
|
4683
|
-
ctx.globalCompositeOperation = "soft-light";
|
|
4684
4697
|
const patternOpacity = 0.02 + rng() * 0.04;
|
|
4685
4698
|
const patternColor = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.15);
|
|
4686
4699
|
if (bgPatternRoll < 0.2) {
|
|
4687
|
-
// Dot grid —
|
|
4688
|
-
const dotSpacing = Math.max(
|
|
4689
|
-
const
|
|
4700
|
+
// Dot grid — use fillRect instead of arcs (much cheaper, no path building)
|
|
4701
|
+
const dotSpacing = Math.max(12, Math.min(width, height) * (0.015 + rng() * 0.015));
|
|
4702
|
+
const dotDiam = Math.max(1, Math.round(dotSpacing * 0.16));
|
|
4690
4703
|
ctx.globalAlpha = patternOpacity;
|
|
4691
4704
|
ctx.fillStyle = patternColor;
|
|
4692
|
-
|
|
4693
|
-
for(let px = 0; px < width; px += dotSpacing)for(let py = 0; py < height; py += dotSpacing){
|
|
4694
|
-
ctx.
|
|
4695
|
-
|
|
4705
|
+
let dotCount = 0;
|
|
4706
|
+
for(let px = 0; px < width && dotCount < 2000; px += dotSpacing)for(let py = 0; py < height && dotCount < 2000; py += dotSpacing){
|
|
4707
|
+
ctx.fillRect(px, py, dotDiam, dotDiam);
|
|
4708
|
+
dotCount++;
|
|
4696
4709
|
}
|
|
4697
|
-
ctx.fill();
|
|
4698
4710
|
} else if (bgPatternRoll < 0.4) {
|
|
4699
|
-
// Diagonal lines — batched into a single path
|
|
4700
|
-
const lineSpacing = Math.max(
|
|
4711
|
+
// Diagonal lines — batched into a single path, capped at 300 lines
|
|
4712
|
+
const lineSpacing = Math.max(10, Math.min(width, height) * (0.02 + rng() * 0.02));
|
|
4701
4713
|
ctx.globalAlpha = patternOpacity;
|
|
4702
4714
|
ctx.strokeStyle = patternColor;
|
|
4703
4715
|
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4704
4716
|
const diag = Math.hypot(width, height);
|
|
4705
4717
|
ctx.beginPath();
|
|
4706
|
-
|
|
4718
|
+
let lineCount = 0;
|
|
4719
|
+
for(let d = -diag; d < diag && lineCount < 300; d += lineSpacing){
|
|
4707
4720
|
ctx.moveTo(d, 0);
|
|
4708
4721
|
ctx.lineTo(d + height, height);
|
|
4722
|
+
lineCount++;
|
|
4709
4723
|
}
|
|
4710
4724
|
ctx.stroke();
|
|
4711
4725
|
} else {
|
|
4712
|
-
// Tessellation — hexagonal grid,
|
|
4713
|
-
const tessSize = Math.max(
|
|
4726
|
+
// Tessellation — hexagonal grid, capped at 500 hexagons
|
|
4727
|
+
const tessSize = Math.max(15, Math.min(width, height) * (0.025 + rng() * 0.02));
|
|
4714
4728
|
const tessH = tessSize * Math.sqrt(3);
|
|
4715
4729
|
ctx.globalAlpha = patternOpacity * 0.7;
|
|
4716
4730
|
ctx.strokeStyle = patternColor;
|
|
4717
4731
|
ctx.lineWidth = 0.4 * scaleFactor;
|
|
4732
|
+
// Pre-compute hex vertex offsets (avoid trig per vertex)
|
|
4733
|
+
const hexVx = [];
|
|
4734
|
+
const hexVy = [];
|
|
4735
|
+
for(let s = 0; s < 6; s++){
|
|
4736
|
+
const angle = Math.PI / 3 * s - Math.PI / 6;
|
|
4737
|
+
hexVx.push(Math.cos(angle) * tessSize * 0.5);
|
|
4738
|
+
hexVy.push(Math.sin(angle) * tessSize * 0.5);
|
|
4739
|
+
}
|
|
4718
4740
|
ctx.beginPath();
|
|
4719
|
-
|
|
4741
|
+
let hexCount = 0;
|
|
4742
|
+
for(let row = 0; row * tessH < height + tessH && hexCount < 500; row++){
|
|
4720
4743
|
const offsetX = row % 2 * tessSize * 0.75;
|
|
4721
|
-
for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5; col++){
|
|
4744
|
+
for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5 && hexCount < 500; col++){
|
|
4722
4745
|
const hx = col * tessSize * 1.5 + offsetX;
|
|
4723
4746
|
const hy = row * tessH;
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
const vx = hx + Math.cos(angle) * tessSize * 0.5;
|
|
4727
|
-
const vy = hy + Math.sin(angle) * tessSize * 0.5;
|
|
4728
|
-
if (s === 0) ctx.moveTo(vx, vy);
|
|
4729
|
-
else ctx.lineTo(vx, vy);
|
|
4730
|
-
}
|
|
4747
|
+
ctx.moveTo(hx + hexVx[0], hy + hexVy[0]);
|
|
4748
|
+
for(let s = 1; s < 6; s++)ctx.lineTo(hx + hexVx[s], hy + hexVy[s]);
|
|
4731
4749
|
ctx.closePath();
|
|
4750
|
+
hexCount++;
|
|
4732
4751
|
}
|
|
4733
4752
|
}
|
|
4734
4753
|
ctx.stroke();
|
|
@@ -4736,6 +4755,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4736
4755
|
ctx.restore();
|
|
4737
4756
|
}
|
|
4738
4757
|
ctx.globalCompositeOperation = "source-over";
|
|
4758
|
+
_mark("1_background");
|
|
4739
4759
|
// ── 2. Composition mode — archetype-aware selection ──────────────
|
|
4740
4760
|
const compositionMode = rng() < 0.7 ? archetype.preferredCompositions[Math.floor(rng() * archetype.preferredCompositions.length)] : $b623126c6e9cbb71$var$ALL_COMPOSITION_MODES[Math.floor(rng() * $b623126c6e9cbb71$var$ALL_COMPOSITION_MODES.length)];
|
|
4741
4761
|
const symRoll = rng();
|
|
@@ -4846,6 +4866,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4846
4866
|
}
|
|
4847
4867
|
}
|
|
4848
4868
|
ctx.globalAlpha = 1;
|
|
4869
|
+
_mark("2_3_composition_focal");
|
|
4849
4870
|
// ── 4. Flow field — simplex noise for organic variation ─────────
|
|
4850
4871
|
// Create a seeded simplex noise field (unique per hash)
|
|
4851
4872
|
const noiseFieldRng = (0, $461134e0b6ce0619$export$eaf9227667332084)((0, $461134e0b6ce0619$export$e9cc707de01b7042)(gitHash, 333));
|
|
@@ -4922,6 +4943,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4922
4943
|
shape: heroShape
|
|
4923
4944
|
});
|
|
4924
4945
|
}
|
|
4946
|
+
_mark("4_flowfield_hero");
|
|
4925
4947
|
// ── 5. Shape layers ────────────────────────────────────────────
|
|
4926
4948
|
const maxLocalDensity = Math.ceil(shapesPerLayer * 0.15);
|
|
4927
4949
|
// ── Complexity budget — caps total rendering work ──────────────
|
|
@@ -5350,6 +5372,11 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5350
5372
|
}
|
|
5351
5373
|
// Reset blend mode for post-processing passes
|
|
5352
5374
|
ctx.globalCompositeOperation = "source-over";
|
|
5375
|
+
if (_dt) {
|
|
5376
|
+
_dt.shapeCount = shapePositions.length;
|
|
5377
|
+
_dt.extraCount = extrasSpent;
|
|
5378
|
+
}
|
|
5379
|
+
_mark("5_shape_layers");
|
|
5353
5380
|
// ── 5g. Layered masking / cutout portals ───────────────────────
|
|
5354
5381
|
// ~18% of images get 1-3 portal windows that paint over foreground
|
|
5355
5382
|
// with a tinted background wash, creating a "peek through" effect.
|
|
@@ -5408,6 +5435,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5408
5435
|
ctx.restore();
|
|
5409
5436
|
}
|
|
5410
5437
|
}
|
|
5438
|
+
_mark("5g_portals");
|
|
5411
5439
|
// ── 6. Flow-line pass — variable color, branching, pressure ────
|
|
5412
5440
|
// Optimized: collect all segments into width-quantized buckets, then
|
|
5413
5441
|
// render each bucket as a single batched path. This reduces
|
|
@@ -5533,6 +5561,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5533
5561
|
ctx.stroke();
|
|
5534
5562
|
}
|
|
5535
5563
|
}
|
|
5564
|
+
_mark("6_flow_lines");
|
|
5536
5565
|
// ── 6b. Motion/energy lines — short directional bursts ─────────
|
|
5537
5566
|
// Optimized: collect all burst segments, then batch by quantized alpha
|
|
5538
5567
|
const energyArchetypes = [
|
|
@@ -5595,6 +5624,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5595
5624
|
ctx.stroke();
|
|
5596
5625
|
}
|
|
5597
5626
|
}
|
|
5627
|
+
_mark("6b_energy_lines");
|
|
5598
5628
|
// ── 6c. Apply symmetry mirroring ─────────────────────────────────
|
|
5599
5629
|
if (symmetryMode !== "none") {
|
|
5600
5630
|
const canvas = ctx.canvas;
|
|
@@ -5615,60 +5645,25 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5615
5645
|
}
|
|
5616
5646
|
ctx.restore();
|
|
5617
5647
|
}
|
|
5618
|
-
|
|
5619
|
-
//
|
|
5620
|
-
//
|
|
5648
|
+
_mark("6c_symmetry");
|
|
5649
|
+
// ── 7. Noise texture overlay ─────────────────────────────────────
|
|
5650
|
+
// With density capped at 2500 dots, direct fillRect calls are far cheaper
|
|
5651
|
+
// than the getImageData/putImageData round-trip which copies the entire
|
|
5652
|
+
// pixel buffer (4 × width × height bytes) twice.
|
|
5621
5653
|
const noiseRng = (0, $461134e0b6ce0619$export$eaf9227667332084)((0, $461134e0b6ce0619$export$e9cc707de01b7042)(gitHash, 777));
|
|
5622
5654
|
const rawNoiseDensity = Math.floor(width * height / 800);
|
|
5623
|
-
// Cap at 2500 dots — beyond this the visual effect is indistinguishable
|
|
5624
|
-
// but getImageData/putImageData cost scales with canvas size
|
|
5625
5655
|
const noiseDensity = Math.min(rawNoiseDensity, 2500);
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
const
|
|
5629
|
-
const
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5636
|
-
// srcA in range [0.01, 0.04] — multiply by 256 for fixed-point
|
|
5637
|
-
const srcA256 = Math.round((0.01 + noiseRng() * 0.03) * 256);
|
|
5638
|
-
const invA256 = 256 - srcA256;
|
|
5639
|
-
const bSrc = brightness * srcA256; // pre-multiply brightness × alpha
|
|
5640
|
-
const idx = ny * width + nx << 2;
|
|
5641
|
-
data[idx] = data[idx] * invA256 + bSrc >> 8;
|
|
5642
|
-
data[idx + 1] = data[idx + 1] * invA256 + bSrc >> 8;
|
|
5643
|
-
data[idx + 2] = data[idx + 2] * invA256 + bSrc >> 8;
|
|
5644
|
-
}
|
|
5645
|
-
else for(let i = 0; i < noiseDensity; i++){
|
|
5646
|
-
const nx = Math.floor(noiseRng() * width);
|
|
5647
|
-
const ny = Math.floor(noiseRng() * height);
|
|
5648
|
-
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5649
|
-
const srcA256 = Math.round((0.01 + noiseRng() * 0.03) * 256);
|
|
5650
|
-
const invA256 = 256 - srcA256;
|
|
5651
|
-
const bSrc = brightness * srcA256;
|
|
5652
|
-
for(let dy = 0; dy < pixelScale && ny + dy < height; dy++)for(let dx = 0; dx < pixelScale && nx + dx < width; dx++){
|
|
5653
|
-
const idx = (ny + dy) * width + (nx + dx) << 2;
|
|
5654
|
-
data[idx] = data[idx] * invA256 + bSrc >> 8;
|
|
5655
|
-
data[idx + 1] = data[idx + 1] * invA256 + bSrc >> 8;
|
|
5656
|
-
data[idx + 2] = data[idx + 2] * invA256 + bSrc >> 8;
|
|
5657
|
-
}
|
|
5658
|
-
}
|
|
5659
|
-
ctx.putImageData(imageData, 0, 0);
|
|
5660
|
-
} catch {
|
|
5661
|
-
// Fallback for environments where getImageData isn't available (e.g. some OffscreenCanvas)
|
|
5662
|
-
for(let i = 0; i < noiseDensity; i++){
|
|
5663
|
-
const nx = noiseRng() * width;
|
|
5664
|
-
const ny = noiseRng() * height;
|
|
5665
|
-
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5666
|
-
const alpha = 0.01 + noiseRng() * 0.03;
|
|
5667
|
-
ctx.globalAlpha = alpha;
|
|
5668
|
-
ctx.fillStyle = `rgba(${brightness},${brightness},${brightness},1)`;
|
|
5669
|
-
ctx.fillRect(nx, ny, 1 * scaleFactor, 1 * scaleFactor);
|
|
5670
|
-
}
|
|
5656
|
+
const pixelScale = Math.max(1, Math.round(scaleFactor));
|
|
5657
|
+
for(let i = 0; i < noiseDensity; i++){
|
|
5658
|
+
const nx = noiseRng() * width;
|
|
5659
|
+
const ny = noiseRng() * height;
|
|
5660
|
+
const brightness = noiseRng() > 0.5 ? 255 : 0;
|
|
5661
|
+
const alpha = 0.01 + noiseRng() * 0.03;
|
|
5662
|
+
ctx.globalAlpha = alpha;
|
|
5663
|
+
ctx.fillStyle = `rgb(${brightness},${brightness},${brightness})`;
|
|
5664
|
+
ctx.fillRect(nx, ny, pixelScale, pixelScale);
|
|
5671
5665
|
}
|
|
5666
|
+
_mark("7_noise_texture");
|
|
5672
5667
|
// ── 8. Vignette — darken edges to draw the eye inward ───────────
|
|
5673
5668
|
ctx.globalAlpha = 1;
|
|
5674
5669
|
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
@@ -5682,6 +5677,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5682
5677
|
vigGrad.addColorStop(1, vignetteColor);
|
|
5683
5678
|
ctx.fillStyle = vigGrad;
|
|
5684
5679
|
ctx.fillRect(0, 0, width, height);
|
|
5680
|
+
_mark("8_vignette");
|
|
5685
5681
|
// ── 9. Organic connecting curves — proximity-aware ───────────────
|
|
5686
5682
|
// Optimized: batch all curves into alpha-quantized groups to reduce
|
|
5687
5683
|
// beginPath/stroke calls from O(numCurves) to O(alphaBuckets).
|
|
@@ -5740,6 +5736,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5740
5736
|
ctx.stroke();
|
|
5741
5737
|
}
|
|
5742
5738
|
}
|
|
5739
|
+
_mark("9_connecting_curves");
|
|
5743
5740
|
// ── 10. Post-processing ────────────────────────────────────────
|
|
5744
5741
|
// 10a. Color grading — unified tone across the whole image
|
|
5745
5742
|
// Apply as a semi-transparent overlay in the grade hue
|
|
@@ -5799,6 +5796,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5799
5796
|
ctx.fillRect(0, 0, width, height);
|
|
5800
5797
|
ctx.globalCompositeOperation = "source-over";
|
|
5801
5798
|
}
|
|
5799
|
+
_mark("10_post_processing");
|
|
5802
5800
|
// ── 10e. Generative borders — archetype-driven decorative frames ──
|
|
5803
5801
|
{
|
|
5804
5802
|
ctx.save();
|
|
@@ -5965,6 +5963,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
5965
5963
|
// Other archetypes: no border (intentional — not every image needs one)
|
|
5966
5964
|
ctx.restore();
|
|
5967
5965
|
}
|
|
5966
|
+
_mark("10e_borders");
|
|
5968
5967
|
// ── 11. Signature mark — placed in the least-dense corner ──────
|
|
5969
5968
|
{
|
|
5970
5969
|
const sigRng = (0, $461134e0b6ce0619$export$eaf9227667332084)((0, $461134e0b6ce0619$export$e9cc707de01b7042)(gitHash, 42));
|
|
@@ -6032,6 +6031,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
6032
6031
|
ctx.restore();
|
|
6033
6032
|
}
|
|
6034
6033
|
ctx.globalAlpha = 1;
|
|
6034
|
+
_mark("11_signature");
|
|
6035
6035
|
}
|
|
6036
6036
|
|
|
6037
6037
|
|