UNPKG

fabric

Version:

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

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