function symmetricalNeighbourSharpenNoHaloTwoPass({source, width, height, radius}){
if (!(radius > 0)) return source.slice();
let target = new Uint8ClampedArray(source.length),
buffer = new Float64Array(source.length);
const kernel = bellcurvish1D(radius);
let totalWeight = 0;
const centerWeight = kernel[radius];
totalWeight += centerWeight;
for(let i = radius+1; i < kernel.length; i++) {
const v = kernel[i] * 0.5;
totalWeight += v;
kernel[i] = v;
}
const norm = 1 / totalWeight;
const {min, max, abs, round} = Math;
for(let y = 0; y < height; y++) {
const line = y * width * 4;
for (let x = 0; x < width; x++) {
const cIdx = x*4 + line;
const r0 = source[cIdx];
const g0 = source[cIdx + 1];
const b0 = source[cIdx + 2];
let rf = r0 * centerWeight;
let gf = g0 * centerWeight;
let bf = b0 * centerWeight;
let rn = rf;
let gn = gf;
let bn = bf;
let diffF = 0;
let diffN = 0;
for (let dx = 1; dx <= radius; dx++) {
const {minPick, minDiff, maxPick, maxDiff} = selectDipole(x, y, dx, 0, source, width, height);
diffF += maxDiff;
diffN += minDiff;
const weight = kernel[radius + dx];
rf += source[maxPick] * weight;
gf += source[maxPick + 1] * weight;
bf += source[maxPick + 2] * weight;
rn += source[minPick] * weight;
gn += source[minPick + 1] * weight;
bn += source[minPick + 2] * weight;
}
const diffscale = (diffN > 0 && diffF > 0) ? diffN / diffF : 1;
rf = (rf * norm - r0) * diffscale;
gf = (gf * norm - g0) * diffscale;
bf = (bf * norm - b0) * diffscale;
rn = rn * norm - r0;
gn = gn * norm - g0;
bn = bn * norm - b0;
buffer[cIdx] = rn - rf;
buffer[cIdx+1] = gn - gf;
buffer[cIdx+2] = bn - bf;
}
}
// Vertical pass
for(let y = 0; y < height; y++) {
const line = y * width * 4;
for (let x = 0; x < width; x++) {
const cIdx = x*4 + line;
const r0 = source[cIdx];
const g0 = source[cIdx + 1];
const b0 = source[cIdx + 2];
let rf = buffer[cIdx] * centerWeight;
let gf = buffer[cIdx + 1] * centerWeight;
let bf = buffer[cIdx + 2] * centerWeight;
let rn = rf;
let gn = gf;
let bn = bf;
let diffF = 0;
let diffN = 0;
for (let dy = 1; dy <= radius; dy++) {
const {minPick, minDiff, maxPick, maxDiff} = selectDipole(x, y, dy, 0, source, width, height);
diffF += maxDiff;
diffN += minDiff;
const weight = kernel[radius + dy];
rf += buffer[maxPick] * weight;
gf += buffer[maxPick + 1] * weight;
bf += buffer[maxPick + 2] * weight;
rn += buffer[minPick] * weight;
gn += buffer[minPick + 1] * weight;
bn += buffer[minPick + 2] * weight;
}
const diffscale = (diffN > 0 && diffF > 0) ? diffN / diffF : 1;
target[cIdx] = round(r0 + (rn - rf * diffscale) * norm);
target[cIdx+1] = round(g0 + (gn - gf * diffscale) * norm);
target[cIdx+2] = round(b0 + (bn - bf * diffscale) * norm);
target[cIdx+3] = 255;
}
}
return target;
}