{"version":3,"file":"Blur.min.mjs","sources":["../../../src/filters/Blur.ts"],"sourcesContent":["import { BaseFilter } from './BaseFilter';\nimport type {\n  TWebGLPipelineState,\n  T2DPipelineState,\n  TWebGLUniformLocationMap,\n} from './typedefs';\nimport { isWebGLPipelineState } from './utils';\nimport { classRegistry } from '../ClassRegistry';\nimport { fragmentSource } from './shaders/blur';\n\ntype BlurOwnProps = {\n  blur: number;\n};\n\nexport const blurDefaultValues: BlurOwnProps = {\n  blur: 0,\n};\n\n/**\n * Blur filter class\n * @example\n * const filter = new Blur({\n *   blur: 0.5\n * });\n * object.filters.push(filter);\n * object.applyFilters();\n * canvas.renderAll();\n */\nexport class Blur extends BaseFilter<'Blur', BlurOwnProps> {\n  /**\n   * blur value, in percentage of image dimensions.\n   * specific to keep the image blur constant at different resolutions\n   * range between 0 and 1.\n   * @type Number\n   */\n  declare blur: BlurOwnProps['blur'];\n\n  declare horizontal: boolean;\n  declare aspectRatio: number;\n\n  static type = 'Blur';\n\n  static defaults = blurDefaultValues;\n\n  static uniformLocations = ['uDelta'];\n\n  getFragmentSource(): string {\n    return fragmentSource;\n  }\n\n  applyTo(options: TWebGLPipelineState | T2DPipelineState) {\n    if (isWebGLPipelineState(options)) {\n      // this aspectRatio is used to give the same blur to vertical and horizontal\n      this.aspectRatio = options.sourceWidth / options.sourceHeight;\n      options.passes++;\n      this._setupFrameBuffer(options);\n      this.horizontal = true;\n      this.applyToWebGL(options);\n      this._swapTextures(options);\n      this._setupFrameBuffer(options);\n      this.horizontal = false;\n      this.applyToWebGL(options);\n      this._swapTextures(options);\n    } else {\n      this.applyTo2d(options);\n    }\n  }\n\n  applyTo2d({ imageData: { data, width, height } }: T2DPipelineState) {\n    // this code mimic the shader for output consistency\n    // it samples 31 pixels across the image over a distance that depends from the blur value.\n    this.aspectRatio = width / height;\n    this.horizontal = true;\n    let blurValue = this.getBlurValue() * width;\n    const imageData = new Uint8ClampedArray(data);\n    const samples = 15;\n    const bytesInRow = 4 * width;\n    for (let i = 0; i < data.length; i += 4) {\n      let r = 0.0,\n        g = 0.0,\n        b = 0.0,\n        a = 0.0,\n        totalA = 0;\n      const minIRow = i - (i % bytesInRow);\n      const maxIRow = minIRow + bytesInRow;\n      // for now let's keep noise out of the way\n      // let pixelOffset = 0;\n      // const offset = Math.random() * 3;\n      // if (offset > 2) {\n      //   pixelOffset = 4;\n      // } else if (offset < 1) {\n      //   pixelOffset = -4;\n      // }\n      for (let j = -samples + 1; j < samples; j++) {\n        const percent = j / samples;\n        const distance = Math.floor(blurValue * percent) * 4;\n        const weight = 1 - Math.abs(percent);\n        let sampledPixel = i + distance; // + pixelOffset;\n        // try to implement edge mirroring\n        if (sampledPixel < minIRow) {\n          sampledPixel = minIRow;\n        } else if (sampledPixel > maxIRow) {\n          sampledPixel = maxIRow;\n        }\n        const localAlpha = data[sampledPixel + 3] * weight;\n        r += data[sampledPixel] * localAlpha;\n        g += data[sampledPixel + 1] * localAlpha;\n        b += data[sampledPixel + 2] * localAlpha;\n        a += localAlpha;\n        totalA += weight;\n      }\n      imageData[i] = r / a;\n      imageData[i + 1] = g / a;\n      imageData[i + 2] = b / a;\n      imageData[i + 3] = a / totalA;\n    }\n    this.horizontal = false;\n    blurValue = this.getBlurValue() * height;\n    for (let i = 0; i < imageData.length; i += 4) {\n      let r = 0.0,\n        g = 0.0,\n        b = 0.0,\n        a = 0.0,\n        totalA = 0;\n      const minIRow = i % bytesInRow;\n      const maxIRow = imageData.length - bytesInRow + minIRow;\n      // for now let's keep noise out of the way\n      // let pixelOffset = 0;\n      // const offset = Math.random() * 3;\n      // if (offset > 2) {\n      //   pixelOffset = bytesInRow;\n      // } else if (offset < 1) {\n      //   pixelOffset = -bytesInRow;\n      // }\n      for (let j = -samples + 1; j < samples; j++) {\n        const percent = j / samples;\n        const distance = Math.floor(blurValue * percent) * bytesInRow;\n        const weight = 1 - Math.abs(percent);\n        let sampledPixel = i + distance; // + pixelOffset;\n        // try to implement edge mirroring\n        if (sampledPixel < minIRow) {\n          sampledPixel = minIRow;\n        } else if (sampledPixel > maxIRow) {\n          sampledPixel = maxIRow;\n        }\n        const localAlpha = imageData[sampledPixel + 3] * weight;\n        r += imageData[sampledPixel] * localAlpha;\n        g += imageData[sampledPixel + 1] * localAlpha;\n        b += imageData[sampledPixel + 2] * localAlpha;\n        a += localAlpha;\n        totalA += weight;\n      }\n      data[i] = r / a;\n      data[i + 1] = g / a;\n      data[i + 2] = b / a;\n      data[i + 3] = a / totalA;\n    }\n  }\n\n  /**\n   * Send data from this filter to its shader program's uniforms.\n   *\n   * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.\n   * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects\n   */\n  sendUniformData(\n    gl: WebGLRenderingContext,\n    uniformLocations: TWebGLUniformLocationMap,\n  ) {\n    const delta = this.chooseRightDelta();\n    gl.uniform2fv(uniformLocations.uDelta, delta);\n  }\n\n  isNeutralState() {\n    return this.blur === 0;\n  }\n\n  getBlurValue(): number {\n    let blurScale = 1;\n    const { horizontal, aspectRatio } = this;\n    if (horizontal) {\n      if (aspectRatio > 1) {\n        // image is wide, i want to shrink radius horizontal\n        blurScale = 1 / aspectRatio;\n      }\n    } else {\n      if (aspectRatio < 1) {\n        // image is tall, i want to shrink radius vertical\n        blurScale = aspectRatio;\n      }\n    }\n    return blurScale * this.blur * 0.12;\n  }\n\n  /**\n   * choose right value of image percentage to blur with\n   * @returns {Array} a numeric array with delta values\n   */\n  chooseRightDelta() {\n    const blur = this.getBlurValue();\n    return this.horizontal ? [blur, 0] : [0, blur];\n  }\n}\n\nclassRegistry.setClass(Blur);\n"],"names":["blurDefaultValues","blur","Blur","BaseFilter","getFragmentSource","fragmentSource","applyTo","options","isWebGLPipelineState","this","aspectRatio","sourceWidth","sourceHeight","passes","_setupFrameBuffer","horizontal","applyToWebGL","_swapTextures","applyTo2d","_ref","imageData","data","width","height","blurValue","getBlurValue","Uint8ClampedArray","samples","bytesInRow","i","length","r","g","b","a","totalA","minIRow","maxIRow","j","percent","distance","Math","floor","weight","abs","sampledPixel","localAlpha","sendUniformData","gl","uniformLocations","delta","chooseRightDelta","uniform2fv","uDelta","isNeutralState","blurScale","_defineProperty","classRegistry","setClass"],"mappings":"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"}