export function BlurDiffFilter()

in src/canvastools/ts/CanvasTools/CanvasTools.Filter.ts [64:249]


export function BlurDiffFilter(factor: number): FilterFunction {
    // http://blog.ivank.net/fastest-gaussian-blur.html
    function boxesForGauss(sigma: number, n: number): number[] {
        const wIdeal = Math.sqrt((12 * sigma * sigma / n) + 1);  // Ideal averaging filter width
        let wl = Math.floor(wIdeal);
        if (wl % 2 === 0) {
            wl--;
        }
        const wu = wl + 2;

        const mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
        const m = Math.round(mIdeal);
        // var sigmaActual = Math.sqrt( (m*wl*wl + (n-m)*wu*wu - n)/12 );
        const sizes: number[] = [];

        for (let i = 0; i < n; i++) {
            sizes.push(i < m ? wl : wu);
        }
        return sizes;
    }

    function gaussBlur_4(scl: Uint8ClampedArray, tcl: Uint8ClampedArray, w: number, h: number, r: number) {
        const bxs = boxesForGauss(r, 3);
        boxBlur_4 (scl, tcl, w, h, (bxs[0] - 1) / 2);
        boxBlur_4 (tcl, scl, w, h, (bxs[1] - 1) / 2);
        boxBlur_4 (scl, tcl, w, h, (bxs[2] - 1) / 2);
    }
    function boxBlur_4(scl: Uint8ClampedArray, tcl: Uint8ClampedArray, w: number, h: number, r: number) {
        for (let i = 0; i < scl.length; i++) {
            tcl[i] = scl[i];
        }
        boxBlurH_4(tcl, scl, w, h, r);
        boxBlurT_4(scl, tcl, w, h, r);
    }
    function boxBlurH_4(scl: Uint8ClampedArray, tcl: Uint8ClampedArray, w: number, h: number, r: number) {
        const iarr = 1 / (r + r + 1);
        for (let i = 0; i < h; i++) {
            let ti = i * w;
            let li = ti;
            let ri = ti + r;
            const fv = scl[ti];
            const lv = scl[ti + w - 1];
            let val = (r + 1) * fv;
            for (let j = 0; j < r; j++) {
                val += scl[ti + j];
            }
            for (let j = 0  ; j <= r ; j++) {
                val += scl[ri++] - fv;
                tcl[ti++] = Math.round(val * iarr);
            }
            for (let j = r + 1; j < w - r; j++) {
                val += scl[ri++] - scl[li++];
                tcl[ti++] = Math.round(val * iarr);
            }
            for (let j = w - r; j < w; j++) {
                val += lv - scl[li++];
                tcl[ti++] = Math.round(val * iarr);
            }
        }
    }
    function boxBlurT_4(scl: Uint8ClampedArray, tcl: Uint8ClampedArray, w: number, h: number, r: number) {
        const iarr = 1 / (r + r + 1);
        for (let i = 0; i < w; i++) {
            let ti = i;
            let li = ti;
            let ri = ti + r * w;
            const fv = scl[ti];
            const lv = scl[ti + w * (h - 1)];
            let val = (r + 1) * fv;
            for (let j = 0; j < r; j++) {
                val += scl[ti + j * w];
            }
            for (let j = 0; j <= r; j++) {
                val += scl[ri] - fv;
                tcl[ti] = Math.round(val * iarr);
                ri += w;
                ti += w;
            }
            for (let j = r + 1; j < h - r; j++) {
                val += scl[ri] - scl[li];
                tcl[ti] = Math.round(val * iarr);
                li += w;
                ri += w;
                ti += w;
            }
            for (let j = h - r; j < h; j++) {
                val += lv - scl[li];
                tcl[ti] = Math.round(val * iarr);
                li += w;
                ti += w;
            }
        }
    }

    return (canvas: HTMLCanvasElement) => {
        const context = canvas.getContext("2d");
        const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

        const buff = document.createElement("canvas");
        buff.width = canvas.width;
        buff.height = canvas.height;

        const bludData = buff.getContext("2d").createImageData(buff.width, buff.height);
        const idata = imageData.data;
        const bdata = bludData.data;

        const pixelsNumber = canvas.width * canvas.height;
        const dataR = new Uint8ClampedArray(pixelsNumber);
        const dataG = new Uint8ClampedArray(pixelsNumber);
        const dataB = new Uint8ClampedArray(pixelsNumber);
        const dataA = new Uint8ClampedArray(pixelsNumber);

        for (let i = 0; i < pixelsNumber; i++) {
            dataR[i] = idata[4 * i];
            dataG[i] = idata[4 * i + 1];
            dataB[i] = idata[4 * i + 2];
            dataA[i] = idata[4 * i + 3];
        }

        const blurR = new Uint8ClampedArray(pixelsNumber);
        const blurG = new Uint8ClampedArray(pixelsNumber);
        const blurB = new Uint8ClampedArray(pixelsNumber);
        const blurR2 = new Uint8ClampedArray(pixelsNumber);
        const blurG2 = new Uint8ClampedArray(pixelsNumber);
        const blurB2 = new Uint8ClampedArray(pixelsNumber);
        // let blurA = new Uint8ClampedArray(pixelsNumber);

        const halfFactor = factor / 2;
        gaussBlur_4(dataR, blurR, buff.width, buff.height, halfFactor);
        gaussBlur_4(dataG, blurG, buff.width, buff.height, halfFactor);
        gaussBlur_4(dataB, blurB, buff.width, buff.height, halfFactor);
        gaussBlur_4(dataR, blurR2, buff.width, buff.height, factor);
        gaussBlur_4(dataG, blurG2, buff.width, buff.height, factor);
        gaussBlur_4(dataB, blurB2, buff.width, buff.height, factor);

        const alphaStep = 127 / factor;

        for (let i = 0; i < pixelsNumber; i++) {
            const dr = Math.abs(blurR2[i] - blurR[i]);
            const dg = Math.abs(blurG2[i] - blurG[i]);
            const db = Math.abs(blurB2[i] - blurB[i]);
            // const d = 0.2126 * dr + 0.7152 * dg + 0.0722 * db;
            const d = 0.2358 * dr + 0.0700 * dg + 0.6742 * db;

            /* let dr = Math.abs(blurR2[i] - idata[4 * i + 0]);
            let dg = Math.abs(blurG2[i] - idata[4 * i + 1]);
            let db = Math.abs(blurB2[i] - idata[4 * i + 2]); */
            // let d = 255 - Math.min(Math.round(Math.max(dr + dg + db - 16, 0)/8) * 16, 255);

            /* bdata[4 * i + 0] = d;
            bdata[4 * i + 1] = d;
            bdata[4 * i + 2] = d; */

            /* bdata[4 * i + 0] = (d < factor) ? Math.round(idata[4 * i + 0] / factor) * factor : idata[4 * i + 0];
            bdata[4 * i + 1] = (d < factor) ? Math.round(idata[4 * i + 1] / factor) * factor : idata[4 * i + 1];
            bdata[4 * i + 2] = (d < factor) ? Math.round(idata[4 * i + 2] / factor) * factor : idata[4 * i + 2]; */

            /* bdata[4 * i + 0] = (dr >= 0.2126 * factor) ?
                                idata[4 * i + 0] :  Math.round(idata[4 * i + 0] / factor) * factor;
            bdata[4 * i + 1] = (dg >= 0.7152 * factor) ?
                                idata[4 * i + 1] :  Math.round(idata[4 * i + 1] / factor) * factor;
            bdata[4 * i + 2] = (db >= 0.0722 * factor) ?
                                idata[4 * i + 2] :  Math.round(idata[4 * i + 2] / factor) * factor; */

            const g = Math.round(0.2358 * idata[4 * i + 0] + 0.0700 * idata[4 * i + 1] + 0.6742 * idata[4 * i + 2]);

            bdata[4 * i + 0] = (dr >= 0.2358 * halfFactor) ?
                                idata[4 * i + 0] :  Math.round(g / factor) * factor;
            bdata[4 * i + 1] = (dg >= 0.0700 * halfFactor) ?
                                idata[4 * i + 1] :  Math.round(g / factor) * factor;
            bdata[4 * i + 2] = (db >= 0.6742 * halfFactor) ?
                                idata[4 * i + 2] :  Math.round(g / factor) * factor;

            /* bdata[4 * i + 0] = Math.round(idata[4 * i + 0] / 8) * 8;
            bdata[4 * i + 1] = Math.round(idata[4 * i + 1] / 8) * 8;
            bdata[4 * i + 2] = Math.round(idata[4 * i + 2] / 8) * 8; */
            bdata[4 * i + 3] = (d >= factor) ? 255 : 0 + Math.round(d * alphaStep);
        }

        buff.getContext("2d").putImageData(bludData, 0, 0);

        return new Promise<HTMLCanvasElement>((resolve, reject) => {
            return resolve(buff);
        });
    };
}