emf-converter 4.8.5 → 4.8.6

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/dist/browser.js CHANGED
@@ -16534,153 +16534,26 @@ function applyTint(src, hue, amount) {
16534
16534
  var TINT_FULL = 254;
16535
16535
  function applyRedEyeCorrection(src, width, height, areas) {
16536
16536
  const out = new Uint8ClampedArray(src);
16537
+ const carry = { x: 0, y: 0 };
16537
16538
  for (const a of areas) {
16538
- correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16539
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height), carry);
16539
16540
  }
16540
16541
  return out;
16541
16542
  }
16542
- var RED_EYE_FALLOFF = new Float64Array(
16543
- [
16544
- 24930,
16545
- 24570,
16546
- 24201,
16547
- 23836,
16548
- 23471,
16549
- 23107,
16550
- 22744,
16551
- 22367,
16552
- 21976,
16553
- 21587,
16554
- 21198,
16555
- 20824,
16556
- 20490,
16557
- 20165,
16558
- 19832,
16559
- 19499,
16560
- 19157,
16561
- 18817,
16562
- 18480,
16563
- 18151,
16564
- 17820,
16565
- 17490,
16566
- 17160,
16567
- 16831,
16568
- 16506,
16569
- 16189,
16570
- 15878,
16571
- 15569,
16572
- 15273,
16573
- 14973,
16574
- 14668,
16575
- 14362,
16576
- 14059,
16577
- 13768,
16578
- 13484,
16579
- 13203,
16580
- 12923,
16581
- 12642,
16582
- 12366,
16583
- 12094,
16584
- 11828,
16585
- 11564,
16586
- 11301,
16587
- 11034,
16588
- 10770,
16589
- 10507,
16590
- 10253,
16591
- 10011,
16592
- 9771,
16593
- 9530,
16594
- 9290,
16595
- 9053,
16596
- 8819,
16597
- 8588,
16598
- 8359,
16599
- 8134,
16600
- 7914,
16601
- 7697,
16602
- 7481,
16603
- 7267,
16604
- 7056,
16605
- 6851,
16606
- 6651,
16607
- 6453,
16608
- 6256,
16609
- 6061,
16610
- 5871,
16611
- 5684,
16612
- 5499,
16613
- 5317,
16614
- 5138,
16615
- 4962,
16616
- 4789,
16617
- 4619,
16618
- 4452,
16619
- 4289,
16620
- 4130,
16621
- 3974,
16622
- 3820,
16623
- 3669,
16624
- 3520,
16625
- 3375,
16626
- 3232,
16627
- 3093,
16628
- 2958,
16629
- 2825,
16630
- 2695,
16631
- 2568,
16632
- 2444,
16633
- 2325,
16634
- 2207,
16635
- 2092,
16636
- 1980,
16637
- 1872,
16638
- 1768,
16639
- 1666,
16640
- 1567,
16641
- 1470,
16642
- 1377,
16643
- 1287,
16644
- 1200,
16645
- 1116,
16646
- 1034,
16647
- 956,
16648
- 881,
16649
- 808,
16650
- 739,
16651
- 674,
16652
- 612,
16653
- 541,
16654
- 462,
16655
- 381,
16656
- 299,
16657
- 220,
16658
- 147,
16659
- 85,
16660
- 36,
16661
- 3,
16662
- 0
16663
- ].map((v) => v / 1e5)
16664
- );
16665
- function redEyeFalloff(u) {
16666
- const t = u * 128;
16667
- const i = Math.floor(t);
16668
- if (i >= RED_EYE_FALLOFF.length - 1) {
16669
- return 0;
16670
- }
16671
- return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16672
- }
16673
16543
  var RED_EYE_SECTORS = 60;
16674
16544
  var RED_EYE_BLUE_WEIGHT = 0.225;
16675
- var RED_EYE_WEIGHT_CAP = 6;
16676
- var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16677
- var RED_EYE_DARK_STRENGTH = 0.49;
16678
- var RED_EYE_DARK_RATIO = 0.6;
16679
- var RED_EYE_GREEN_SHARE = 80 / 255;
16680
- function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16545
+ var RED_EYE_PURE_WEIGHT = 1 / (1 + RED_EYE_BLUE_WEIGHT);
16546
+ var RED_EYE_CARRY = 0.81;
16547
+ var RED_EYE_GREEN_SHARE = 5 / 16;
16548
+ var RED_EYE_SPREAD_STEPS = [31.5, 50, 98.78];
16549
+ var RED_EYE_STRENGTHS = [0.25, 0.5, 0.661, 0.75];
16550
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1, carry) {
16681
16551
  const w = x1 - x0;
16682
16552
  const h = y1 - y0;
16683
16553
  const count = w * h;
16554
+ if (!(count > 0)) {
16555
+ return;
16556
+ }
16684
16557
  const redness = new Float64Array(count);
16685
16558
  const dark = new Float64Array(count);
16686
16559
  let sx = 0;
@@ -16693,34 +16566,44 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16693
16566
  const g = out[i + 1];
16694
16567
  const b = out[i + 2];
16695
16568
  const k = y * w + x;
16696
- dark[k] = Math.min(g, b);
16569
+ const d = g + RED_EYE_BLUE_WEIGHT * b;
16570
+ dark[k] = d;
16697
16571
  const v = r - Math.max(g, b);
16698
16572
  if (v > 0) {
16699
16573
  redness[k] = v;
16700
- const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16701
- {
16702
- const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16703
- sx += weight * (x + 0.5);
16704
- sy += weight * (y + 0.5);
16705
- sw += weight;
16706
- }
16574
+ const weight = d >= 1 ? v / d : RED_EYE_PURE_WEIGHT;
16575
+ sx += weight * (x + 0.5);
16576
+ sy += weight * (y + 0.5);
16577
+ sw += weight;
16707
16578
  }
16708
16579
  }
16709
16580
  }
16710
16581
  if (sw === 0) {
16711
- if (!redness.some((v) => v > 0)) {
16712
- return;
16713
- }
16714
- sx = w / 2;
16715
- sy = h / 2;
16716
- sw = 1;
16582
+ return;
16717
16583
  }
16718
- const cx = sx / sw;
16719
- const cy = sy / sw;
16720
- const radius = Math.min(cx, cy) - 0.5;
16584
+ let cx = (sx + RED_EYE_CARRY * carry.x) / sw;
16585
+ let cy = (sy + RED_EYE_CARRY * carry.y) / sw;
16586
+ carry.x = x0 + cx;
16587
+ carry.y = y0 + cy;
16588
+ if (Math.hypot(cx - w / 2, cy - h / 2) >= Math.max(Math.min(w, h) / 2, (w + h) / 6)) {
16589
+ cx = w / 2;
16590
+ cy = h / 2;
16591
+ }
16592
+ const ax = x0 + cx;
16593
+ const ay = y0 + cy;
16594
+ const edgeX = Math.round(cx) > w / 2 ? w - cx + 0.5 : cx - 0.5;
16595
+ const edgeY = Math.round(cy) > h / 2 ? h - cy + 0.5 : cy - 0.5;
16596
+ const radius = ax > ay ? Math.min(edgeX, ay - 0.5) : Math.min(edgeY, ax - 0.5);
16721
16597
  if (!(radius > 0)) {
16722
16598
  return;
16723
16599
  }
16600
+ const sorted = Float64Array.from(dark).sort();
16601
+ const spread = sorted[Math.floor(0.7 * count)] - sorted[Math.floor(0.3 * count)];
16602
+ let level = 0;
16603
+ while (level < RED_EYE_SPREAD_STEPS.length && spread >= RED_EYE_SPREAD_STEPS[level]) {
16604
+ level++;
16605
+ }
16606
+ const strength = RED_EYE_STRENGTHS[level];
16724
16607
  const sector = new Uint8Array(count);
16725
16608
  const distance = new Float64Array(count);
16726
16609
  const sectorSum = new Float64Array(RED_EYE_SECTORS);
@@ -16758,7 +16641,8 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16758
16641
  const mean = sectorSum[s] / sectorCount[s];
16759
16642
  const meanDark = sectorDark[s] / sectorCount[s];
16760
16643
  const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16761
- const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16644
+ const u = distance[k] / radius;
16645
+ const falloff = Math.max(strength * (1 - u) * (1 - u), 0.5 * Math.max(0, 1 - ratio) ** 2);
16762
16646
  const rest = (1 - falloff) * mean;
16763
16647
  if (rest >= v) {
16764
16648
  continue;
package/dist/browser.mjs CHANGED
@@ -16532,153 +16532,26 @@ function applyTint(src, hue, amount) {
16532
16532
  var TINT_FULL = 254;
16533
16533
  function applyRedEyeCorrection(src, width, height, areas) {
16534
16534
  const out = new Uint8ClampedArray(src);
16535
+ const carry = { x: 0, y: 0 };
16535
16536
  for (const a of areas) {
16536
- correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16537
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height), carry);
16537
16538
  }
16538
16539
  return out;
16539
16540
  }
16540
- var RED_EYE_FALLOFF = new Float64Array(
16541
- [
16542
- 24930,
16543
- 24570,
16544
- 24201,
16545
- 23836,
16546
- 23471,
16547
- 23107,
16548
- 22744,
16549
- 22367,
16550
- 21976,
16551
- 21587,
16552
- 21198,
16553
- 20824,
16554
- 20490,
16555
- 20165,
16556
- 19832,
16557
- 19499,
16558
- 19157,
16559
- 18817,
16560
- 18480,
16561
- 18151,
16562
- 17820,
16563
- 17490,
16564
- 17160,
16565
- 16831,
16566
- 16506,
16567
- 16189,
16568
- 15878,
16569
- 15569,
16570
- 15273,
16571
- 14973,
16572
- 14668,
16573
- 14362,
16574
- 14059,
16575
- 13768,
16576
- 13484,
16577
- 13203,
16578
- 12923,
16579
- 12642,
16580
- 12366,
16581
- 12094,
16582
- 11828,
16583
- 11564,
16584
- 11301,
16585
- 11034,
16586
- 10770,
16587
- 10507,
16588
- 10253,
16589
- 10011,
16590
- 9771,
16591
- 9530,
16592
- 9290,
16593
- 9053,
16594
- 8819,
16595
- 8588,
16596
- 8359,
16597
- 8134,
16598
- 7914,
16599
- 7697,
16600
- 7481,
16601
- 7267,
16602
- 7056,
16603
- 6851,
16604
- 6651,
16605
- 6453,
16606
- 6256,
16607
- 6061,
16608
- 5871,
16609
- 5684,
16610
- 5499,
16611
- 5317,
16612
- 5138,
16613
- 4962,
16614
- 4789,
16615
- 4619,
16616
- 4452,
16617
- 4289,
16618
- 4130,
16619
- 3974,
16620
- 3820,
16621
- 3669,
16622
- 3520,
16623
- 3375,
16624
- 3232,
16625
- 3093,
16626
- 2958,
16627
- 2825,
16628
- 2695,
16629
- 2568,
16630
- 2444,
16631
- 2325,
16632
- 2207,
16633
- 2092,
16634
- 1980,
16635
- 1872,
16636
- 1768,
16637
- 1666,
16638
- 1567,
16639
- 1470,
16640
- 1377,
16641
- 1287,
16642
- 1200,
16643
- 1116,
16644
- 1034,
16645
- 956,
16646
- 881,
16647
- 808,
16648
- 739,
16649
- 674,
16650
- 612,
16651
- 541,
16652
- 462,
16653
- 381,
16654
- 299,
16655
- 220,
16656
- 147,
16657
- 85,
16658
- 36,
16659
- 3,
16660
- 0
16661
- ].map((v) => v / 1e5)
16662
- );
16663
- function redEyeFalloff(u) {
16664
- const t = u * 128;
16665
- const i = Math.floor(t);
16666
- if (i >= RED_EYE_FALLOFF.length - 1) {
16667
- return 0;
16668
- }
16669
- return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16670
- }
16671
16541
  var RED_EYE_SECTORS = 60;
16672
16542
  var RED_EYE_BLUE_WEIGHT = 0.225;
16673
- var RED_EYE_WEIGHT_CAP = 6;
16674
- var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16675
- var RED_EYE_DARK_STRENGTH = 0.49;
16676
- var RED_EYE_DARK_RATIO = 0.6;
16677
- var RED_EYE_GREEN_SHARE = 80 / 255;
16678
- function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16543
+ var RED_EYE_PURE_WEIGHT = 1 / (1 + RED_EYE_BLUE_WEIGHT);
16544
+ var RED_EYE_CARRY = 0.81;
16545
+ var RED_EYE_GREEN_SHARE = 5 / 16;
16546
+ var RED_EYE_SPREAD_STEPS = [31.5, 50, 98.78];
16547
+ var RED_EYE_STRENGTHS = [0.25, 0.5, 0.661, 0.75];
16548
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1, carry) {
16679
16549
  const w = x1 - x0;
16680
16550
  const h = y1 - y0;
16681
16551
  const count = w * h;
16552
+ if (!(count > 0)) {
16553
+ return;
16554
+ }
16682
16555
  const redness = new Float64Array(count);
16683
16556
  const dark = new Float64Array(count);
16684
16557
  let sx = 0;
@@ -16691,34 +16564,44 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16691
16564
  const g = out[i + 1];
16692
16565
  const b = out[i + 2];
16693
16566
  const k = y * w + x;
16694
- dark[k] = Math.min(g, b);
16567
+ const d = g + RED_EYE_BLUE_WEIGHT * b;
16568
+ dark[k] = d;
16695
16569
  const v = r - Math.max(g, b);
16696
16570
  if (v > 0) {
16697
16571
  redness[k] = v;
16698
- const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16699
- {
16700
- const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16701
- sx += weight * (x + 0.5);
16702
- sy += weight * (y + 0.5);
16703
- sw += weight;
16704
- }
16572
+ const weight = d >= 1 ? v / d : RED_EYE_PURE_WEIGHT;
16573
+ sx += weight * (x + 0.5);
16574
+ sy += weight * (y + 0.5);
16575
+ sw += weight;
16705
16576
  }
16706
16577
  }
16707
16578
  }
16708
16579
  if (sw === 0) {
16709
- if (!redness.some((v) => v > 0)) {
16710
- return;
16711
- }
16712
- sx = w / 2;
16713
- sy = h / 2;
16714
- sw = 1;
16580
+ return;
16715
16581
  }
16716
- const cx = sx / sw;
16717
- const cy = sy / sw;
16718
- const radius = Math.min(cx, cy) - 0.5;
16582
+ let cx = (sx + RED_EYE_CARRY * carry.x) / sw;
16583
+ let cy = (sy + RED_EYE_CARRY * carry.y) / sw;
16584
+ carry.x = x0 + cx;
16585
+ carry.y = y0 + cy;
16586
+ if (Math.hypot(cx - w / 2, cy - h / 2) >= Math.max(Math.min(w, h) / 2, (w + h) / 6)) {
16587
+ cx = w / 2;
16588
+ cy = h / 2;
16589
+ }
16590
+ const ax = x0 + cx;
16591
+ const ay = y0 + cy;
16592
+ const edgeX = Math.round(cx) > w / 2 ? w - cx + 0.5 : cx - 0.5;
16593
+ const edgeY = Math.round(cy) > h / 2 ? h - cy + 0.5 : cy - 0.5;
16594
+ const radius = ax > ay ? Math.min(edgeX, ay - 0.5) : Math.min(edgeY, ax - 0.5);
16719
16595
  if (!(radius > 0)) {
16720
16596
  return;
16721
16597
  }
16598
+ const sorted = Float64Array.from(dark).sort();
16599
+ const spread = sorted[Math.floor(0.7 * count)] - sorted[Math.floor(0.3 * count)];
16600
+ let level = 0;
16601
+ while (level < RED_EYE_SPREAD_STEPS.length && spread >= RED_EYE_SPREAD_STEPS[level]) {
16602
+ level++;
16603
+ }
16604
+ const strength = RED_EYE_STRENGTHS[level];
16722
16605
  const sector = new Uint8Array(count);
16723
16606
  const distance = new Float64Array(count);
16724
16607
  const sectorSum = new Float64Array(RED_EYE_SECTORS);
@@ -16756,7 +16639,8 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16756
16639
  const mean = sectorSum[s] / sectorCount[s];
16757
16640
  const meanDark = sectorDark[s] / sectorCount[s];
16758
16641
  const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16759
- const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16642
+ const u = distance[k] / radius;
16643
+ const falloff = Math.max(strength * (1 - u) * (1 - u), 0.5 * Math.max(0, 1 - ratio) ** 2);
16760
16644
  const rest = (1 - falloff) * mean;
16761
16645
  if (rest >= v) {
16762
16646
  continue;
package/dist/index.js CHANGED
@@ -16535,153 +16535,26 @@ function applyTint(src, hue, amount) {
16535
16535
  var TINT_FULL = 254;
16536
16536
  function applyRedEyeCorrection(src, width, height, areas) {
16537
16537
  const out = new Uint8ClampedArray(src);
16538
+ const carry = { x: 0, y: 0 };
16538
16539
  for (const a of areas) {
16539
- correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16540
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height), carry);
16540
16541
  }
16541
16542
  return out;
16542
16543
  }
16543
- var RED_EYE_FALLOFF = new Float64Array(
16544
- [
16545
- 24930,
16546
- 24570,
16547
- 24201,
16548
- 23836,
16549
- 23471,
16550
- 23107,
16551
- 22744,
16552
- 22367,
16553
- 21976,
16554
- 21587,
16555
- 21198,
16556
- 20824,
16557
- 20490,
16558
- 20165,
16559
- 19832,
16560
- 19499,
16561
- 19157,
16562
- 18817,
16563
- 18480,
16564
- 18151,
16565
- 17820,
16566
- 17490,
16567
- 17160,
16568
- 16831,
16569
- 16506,
16570
- 16189,
16571
- 15878,
16572
- 15569,
16573
- 15273,
16574
- 14973,
16575
- 14668,
16576
- 14362,
16577
- 14059,
16578
- 13768,
16579
- 13484,
16580
- 13203,
16581
- 12923,
16582
- 12642,
16583
- 12366,
16584
- 12094,
16585
- 11828,
16586
- 11564,
16587
- 11301,
16588
- 11034,
16589
- 10770,
16590
- 10507,
16591
- 10253,
16592
- 10011,
16593
- 9771,
16594
- 9530,
16595
- 9290,
16596
- 9053,
16597
- 8819,
16598
- 8588,
16599
- 8359,
16600
- 8134,
16601
- 7914,
16602
- 7697,
16603
- 7481,
16604
- 7267,
16605
- 7056,
16606
- 6851,
16607
- 6651,
16608
- 6453,
16609
- 6256,
16610
- 6061,
16611
- 5871,
16612
- 5684,
16613
- 5499,
16614
- 5317,
16615
- 5138,
16616
- 4962,
16617
- 4789,
16618
- 4619,
16619
- 4452,
16620
- 4289,
16621
- 4130,
16622
- 3974,
16623
- 3820,
16624
- 3669,
16625
- 3520,
16626
- 3375,
16627
- 3232,
16628
- 3093,
16629
- 2958,
16630
- 2825,
16631
- 2695,
16632
- 2568,
16633
- 2444,
16634
- 2325,
16635
- 2207,
16636
- 2092,
16637
- 1980,
16638
- 1872,
16639
- 1768,
16640
- 1666,
16641
- 1567,
16642
- 1470,
16643
- 1377,
16644
- 1287,
16645
- 1200,
16646
- 1116,
16647
- 1034,
16648
- 956,
16649
- 881,
16650
- 808,
16651
- 739,
16652
- 674,
16653
- 612,
16654
- 541,
16655
- 462,
16656
- 381,
16657
- 299,
16658
- 220,
16659
- 147,
16660
- 85,
16661
- 36,
16662
- 3,
16663
- 0
16664
- ].map((v) => v / 1e5)
16665
- );
16666
- function redEyeFalloff(u) {
16667
- const t = u * 128;
16668
- const i = Math.floor(t);
16669
- if (i >= RED_EYE_FALLOFF.length - 1) {
16670
- return 0;
16671
- }
16672
- return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16673
- }
16674
16544
  var RED_EYE_SECTORS = 60;
16675
16545
  var RED_EYE_BLUE_WEIGHT = 0.225;
16676
- var RED_EYE_WEIGHT_CAP = 6;
16677
- var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16678
- var RED_EYE_DARK_STRENGTH = 0.49;
16679
- var RED_EYE_DARK_RATIO = 0.6;
16680
- var RED_EYE_GREEN_SHARE = 80 / 255;
16681
- function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16546
+ var RED_EYE_PURE_WEIGHT = 1 / (1 + RED_EYE_BLUE_WEIGHT);
16547
+ var RED_EYE_CARRY = 0.81;
16548
+ var RED_EYE_GREEN_SHARE = 5 / 16;
16549
+ var RED_EYE_SPREAD_STEPS = [31.5, 50, 98.78];
16550
+ var RED_EYE_STRENGTHS = [0.25, 0.5, 0.661, 0.75];
16551
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1, carry) {
16682
16552
  const w = x1 - x0;
16683
16553
  const h = y1 - y0;
16684
16554
  const count = w * h;
16555
+ if (!(count > 0)) {
16556
+ return;
16557
+ }
16685
16558
  const redness = new Float64Array(count);
16686
16559
  const dark = new Float64Array(count);
16687
16560
  let sx = 0;
@@ -16694,34 +16567,44 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16694
16567
  const g = out[i + 1];
16695
16568
  const b = out[i + 2];
16696
16569
  const k = y * w + x;
16697
- dark[k] = Math.min(g, b);
16570
+ const d = g + RED_EYE_BLUE_WEIGHT * b;
16571
+ dark[k] = d;
16698
16572
  const v = r - Math.max(g, b);
16699
16573
  if (v > 0) {
16700
16574
  redness[k] = v;
16701
- const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16702
- {
16703
- const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16704
- sx += weight * (x + 0.5);
16705
- sy += weight * (y + 0.5);
16706
- sw += weight;
16707
- }
16575
+ const weight = d >= 1 ? v / d : RED_EYE_PURE_WEIGHT;
16576
+ sx += weight * (x + 0.5);
16577
+ sy += weight * (y + 0.5);
16578
+ sw += weight;
16708
16579
  }
16709
16580
  }
16710
16581
  }
16711
16582
  if (sw === 0) {
16712
- if (!redness.some((v) => v > 0)) {
16713
- return;
16714
- }
16715
- sx = w / 2;
16716
- sy = h / 2;
16717
- sw = 1;
16583
+ return;
16718
16584
  }
16719
- const cx = sx / sw;
16720
- const cy = sy / sw;
16721
- const radius = Math.min(cx, cy) - 0.5;
16585
+ let cx = (sx + RED_EYE_CARRY * carry.x) / sw;
16586
+ let cy = (sy + RED_EYE_CARRY * carry.y) / sw;
16587
+ carry.x = x0 + cx;
16588
+ carry.y = y0 + cy;
16589
+ if (Math.hypot(cx - w / 2, cy - h / 2) >= Math.max(Math.min(w, h) / 2, (w + h) / 6)) {
16590
+ cx = w / 2;
16591
+ cy = h / 2;
16592
+ }
16593
+ const ax = x0 + cx;
16594
+ const ay = y0 + cy;
16595
+ const edgeX = Math.round(cx) > w / 2 ? w - cx + 0.5 : cx - 0.5;
16596
+ const edgeY = Math.round(cy) > h / 2 ? h - cy + 0.5 : cy - 0.5;
16597
+ const radius = ax > ay ? Math.min(edgeX, ay - 0.5) : Math.min(edgeY, ax - 0.5);
16722
16598
  if (!(radius > 0)) {
16723
16599
  return;
16724
16600
  }
16601
+ const sorted = Float64Array.from(dark).sort();
16602
+ const spread = sorted[Math.floor(0.7 * count)] - sorted[Math.floor(0.3 * count)];
16603
+ let level = 0;
16604
+ while (level < RED_EYE_SPREAD_STEPS.length && spread >= RED_EYE_SPREAD_STEPS[level]) {
16605
+ level++;
16606
+ }
16607
+ const strength = RED_EYE_STRENGTHS[level];
16725
16608
  const sector = new Uint8Array(count);
16726
16609
  const distance = new Float64Array(count);
16727
16610
  const sectorSum = new Float64Array(RED_EYE_SECTORS);
@@ -16759,7 +16642,8 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16759
16642
  const mean = sectorSum[s] / sectorCount[s];
16760
16643
  const meanDark = sectorDark[s] / sectorCount[s];
16761
16644
  const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16762
- const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16645
+ const u = distance[k] / radius;
16646
+ const falloff = Math.max(strength * (1 - u) * (1 - u), 0.5 * Math.max(0, 1 - ratio) ** 2);
16763
16647
  const rest = (1 - falloff) * mean;
16764
16648
  if (rest >= v) {
16765
16649
  continue;
package/dist/index.mjs CHANGED
@@ -16533,153 +16533,26 @@ function applyTint(src, hue, amount) {
16533
16533
  var TINT_FULL = 254;
16534
16534
  function applyRedEyeCorrection(src, width, height, areas) {
16535
16535
  const out = new Uint8ClampedArray(src);
16536
+ const carry = { x: 0, y: 0 };
16536
16537
  for (const a of areas) {
16537
- correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16538
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height), carry);
16538
16539
  }
16539
16540
  return out;
16540
16541
  }
16541
- var RED_EYE_FALLOFF = new Float64Array(
16542
- [
16543
- 24930,
16544
- 24570,
16545
- 24201,
16546
- 23836,
16547
- 23471,
16548
- 23107,
16549
- 22744,
16550
- 22367,
16551
- 21976,
16552
- 21587,
16553
- 21198,
16554
- 20824,
16555
- 20490,
16556
- 20165,
16557
- 19832,
16558
- 19499,
16559
- 19157,
16560
- 18817,
16561
- 18480,
16562
- 18151,
16563
- 17820,
16564
- 17490,
16565
- 17160,
16566
- 16831,
16567
- 16506,
16568
- 16189,
16569
- 15878,
16570
- 15569,
16571
- 15273,
16572
- 14973,
16573
- 14668,
16574
- 14362,
16575
- 14059,
16576
- 13768,
16577
- 13484,
16578
- 13203,
16579
- 12923,
16580
- 12642,
16581
- 12366,
16582
- 12094,
16583
- 11828,
16584
- 11564,
16585
- 11301,
16586
- 11034,
16587
- 10770,
16588
- 10507,
16589
- 10253,
16590
- 10011,
16591
- 9771,
16592
- 9530,
16593
- 9290,
16594
- 9053,
16595
- 8819,
16596
- 8588,
16597
- 8359,
16598
- 8134,
16599
- 7914,
16600
- 7697,
16601
- 7481,
16602
- 7267,
16603
- 7056,
16604
- 6851,
16605
- 6651,
16606
- 6453,
16607
- 6256,
16608
- 6061,
16609
- 5871,
16610
- 5684,
16611
- 5499,
16612
- 5317,
16613
- 5138,
16614
- 4962,
16615
- 4789,
16616
- 4619,
16617
- 4452,
16618
- 4289,
16619
- 4130,
16620
- 3974,
16621
- 3820,
16622
- 3669,
16623
- 3520,
16624
- 3375,
16625
- 3232,
16626
- 3093,
16627
- 2958,
16628
- 2825,
16629
- 2695,
16630
- 2568,
16631
- 2444,
16632
- 2325,
16633
- 2207,
16634
- 2092,
16635
- 1980,
16636
- 1872,
16637
- 1768,
16638
- 1666,
16639
- 1567,
16640
- 1470,
16641
- 1377,
16642
- 1287,
16643
- 1200,
16644
- 1116,
16645
- 1034,
16646
- 956,
16647
- 881,
16648
- 808,
16649
- 739,
16650
- 674,
16651
- 612,
16652
- 541,
16653
- 462,
16654
- 381,
16655
- 299,
16656
- 220,
16657
- 147,
16658
- 85,
16659
- 36,
16660
- 3,
16661
- 0
16662
- ].map((v) => v / 1e5)
16663
- );
16664
- function redEyeFalloff(u) {
16665
- const t = u * 128;
16666
- const i = Math.floor(t);
16667
- if (i >= RED_EYE_FALLOFF.length - 1) {
16668
- return 0;
16669
- }
16670
- return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16671
- }
16672
16542
  var RED_EYE_SECTORS = 60;
16673
16543
  var RED_EYE_BLUE_WEIGHT = 0.225;
16674
- var RED_EYE_WEIGHT_CAP = 6;
16675
- var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16676
- var RED_EYE_DARK_STRENGTH = 0.49;
16677
- var RED_EYE_DARK_RATIO = 0.6;
16678
- var RED_EYE_GREEN_SHARE = 80 / 255;
16679
- function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16544
+ var RED_EYE_PURE_WEIGHT = 1 / (1 + RED_EYE_BLUE_WEIGHT);
16545
+ var RED_EYE_CARRY = 0.81;
16546
+ var RED_EYE_GREEN_SHARE = 5 / 16;
16547
+ var RED_EYE_SPREAD_STEPS = [31.5, 50, 98.78];
16548
+ var RED_EYE_STRENGTHS = [0.25, 0.5, 0.661, 0.75];
16549
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1, carry) {
16680
16550
  const w = x1 - x0;
16681
16551
  const h = y1 - y0;
16682
16552
  const count = w * h;
16553
+ if (!(count > 0)) {
16554
+ return;
16555
+ }
16683
16556
  const redness = new Float64Array(count);
16684
16557
  const dark = new Float64Array(count);
16685
16558
  let sx = 0;
@@ -16692,34 +16565,44 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16692
16565
  const g = out[i + 1];
16693
16566
  const b = out[i + 2];
16694
16567
  const k = y * w + x;
16695
- dark[k] = Math.min(g, b);
16568
+ const d = g + RED_EYE_BLUE_WEIGHT * b;
16569
+ dark[k] = d;
16696
16570
  const v = r - Math.max(g, b);
16697
16571
  if (v > 0) {
16698
16572
  redness[k] = v;
16699
- const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16700
- {
16701
- const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16702
- sx += weight * (x + 0.5);
16703
- sy += weight * (y + 0.5);
16704
- sw += weight;
16705
- }
16573
+ const weight = d >= 1 ? v / d : RED_EYE_PURE_WEIGHT;
16574
+ sx += weight * (x + 0.5);
16575
+ sy += weight * (y + 0.5);
16576
+ sw += weight;
16706
16577
  }
16707
16578
  }
16708
16579
  }
16709
16580
  if (sw === 0) {
16710
- if (!redness.some((v) => v > 0)) {
16711
- return;
16712
- }
16713
- sx = w / 2;
16714
- sy = h / 2;
16715
- sw = 1;
16581
+ return;
16716
16582
  }
16717
- const cx = sx / sw;
16718
- const cy = sy / sw;
16719
- const radius = Math.min(cx, cy) - 0.5;
16583
+ let cx = (sx + RED_EYE_CARRY * carry.x) / sw;
16584
+ let cy = (sy + RED_EYE_CARRY * carry.y) / sw;
16585
+ carry.x = x0 + cx;
16586
+ carry.y = y0 + cy;
16587
+ if (Math.hypot(cx - w / 2, cy - h / 2) >= Math.max(Math.min(w, h) / 2, (w + h) / 6)) {
16588
+ cx = w / 2;
16589
+ cy = h / 2;
16590
+ }
16591
+ const ax = x0 + cx;
16592
+ const ay = y0 + cy;
16593
+ const edgeX = Math.round(cx) > w / 2 ? w - cx + 0.5 : cx - 0.5;
16594
+ const edgeY = Math.round(cy) > h / 2 ? h - cy + 0.5 : cy - 0.5;
16595
+ const radius = ax > ay ? Math.min(edgeX, ay - 0.5) : Math.min(edgeY, ax - 0.5);
16720
16596
  if (!(radius > 0)) {
16721
16597
  return;
16722
16598
  }
16599
+ const sorted = Float64Array.from(dark).sort();
16600
+ const spread = sorted[Math.floor(0.7 * count)] - sorted[Math.floor(0.3 * count)];
16601
+ let level = 0;
16602
+ while (level < RED_EYE_SPREAD_STEPS.length && spread >= RED_EYE_SPREAD_STEPS[level]) {
16603
+ level++;
16604
+ }
16605
+ const strength = RED_EYE_STRENGTHS[level];
16723
16606
  const sector = new Uint8Array(count);
16724
16607
  const distance = new Float64Array(count);
16725
16608
  const sectorSum = new Float64Array(RED_EYE_SECTORS);
@@ -16757,7 +16640,8 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16757
16640
  const mean = sectorSum[s] / sectorCount[s];
16758
16641
  const meanDark = sectorDark[s] / sectorCount[s];
16759
16642
  const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16760
- const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16643
+ const u = distance[k] / radius;
16644
+ const falloff = Math.max(strength * (1 - u) * (1 - u), 0.5 * Math.max(0, 1 - ratio) ** 2);
16761
16645
  const rest = (1 - falloff) * mean;
16762
16646
  if (rest >= v) {
16763
16647
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.8.5",
3
+ "version": "4.8.6",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",