UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 8.68 kB
{"version":3,"file":"Blur.min.mjs","names":[],"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"],"mappings":"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"}