如何让HTML Canvas自定义滤镜返回可用于putImageData的ImageData对象?
问题分析
你遇到的错误原因很直接:ctx.putImageData() 方法要求第一个参数必须是**ImageData对象**,但你的 filter 方法返回的是 Uint8ClampedArray 类型的像素数据数组(也就是 input.data.slice() 生成的数组),这不符合API的参数类型要求,所以抛出了类型错误。
而你修改为直接修改原 inputData 的方式能正常运行,是因为此时你传入 putImageData 的是原本的 ImageData 对象(data 只是它的一个属性),修改它的 data 数组后,这个对象本身依然是合法的 ImageData 类型。
解决方案
让你的 filter 方法直接返回一个新的 ImageData 对象即可——ImageData 构造函数支持传入像素数组、宽度和高度来创建实例。只需要修改 filter 方法的返回语句:
// 替换原来的 return outputData; return new ImageData(outputData, width, height);
这样调用 filter 方法时,得到的就是合法的 ImageData 对象,直接传给 putImageData 就不会报错了。
修改后的完整代码
////////////////////////////// // Distortion Filter // // Originally by JoelBesada // ////////////////////////////// class FilterDistortion { constructor() { this.name = 'Distortion'; this.defaultValues = { size: 4, density: 0.5, mix: 0.5, }; this.valueRanges = { size: { min: 1, max: 10 }, density: { min: 0.0, max: 1.0 }, mix: { min: 0.0, max: 1.0 }, }; } filter(input, values = this.defaultValues) { const { width, height } = input; const inputData = input.data; const outputData = inputData.slice(); let size = (values.size === undefined) ? this.defaultValues.size : parseInt(values.size, 10); if (size < 1) size = 1; const density = (values.density === undefined) ? this.defaultValues.density : Number(values.density); const mix = (values.mix === undefined) ? this.defaultValues.mix : Number(values.mix); const radius = size + 1; const numShapes = parseInt(((((2 * density) / 30) * width) * height) / 2, 10); for (let i = 0; i < numShapes; i++) { const sx = (Math.random() * (2 ** 32) & 0x7fffffff) % width; const sy = (Math.random() * (2 ** 32) & 0x7fffffff) % height; const rgb2 = [ inputData[(((sy * width) + sx) * 4) + 0], inputData[(((sy * width) + sx) * 4) + 1], inputData[(((sy * width) + sx) * 4) + 2], inputData[(((sy * width) + sx) * 4) + 3], ]; for (let x = sx - radius; x < sx + radius + 1; x++) { for (let y = sy - radius; y < sy + radius + 1; y++) { if (x >= 0 && x < width && y >= 0 && y < height) { const rgb1 = [ outputData[(((y * width) + x) * 4) + 0], outputData[(((y * width) + x) * 4) + 1], outputData[(((y * width) + x) * 4) + 2], outputData[(((y * width) + x) * 4) + 3], ]; const mixedRGB = this.mixColors(mix, rgb1, rgb2); for (let k = 0; k < 3; k++) { outputData[(((y * width) + x) * 4) + k] = mixedRGB[k]; } } } } } // 关键修改:返回新的ImageData对象 return new ImageData(outputData, width, height); } linearInterpolate(t, a, b) { return a + (t * (b - a)); } mixColors(t, rgb1, rgb2) { const r = this.linearInterpolate(t, rgb1[0], rgb2[0]); const g = this.linearInterpolate(t, rgb1[1], rgb2[1]); const b = this.linearInterpolate(t, rgb1[2], rgb2[2]); const a = this.linearInterpolate(t, rgb1[3], rgb2[3]); return [r, g, b, a]; } } ///////////////// // Driver Code // ///////////////// var img = new Image(); img.onload = draw; img.src = "//i.imgur.com/Kzz84cr.png"; function draw() { c.width = this.width; c.height = this.height; var ctx = c.getContext("2d"); // main loop ctx.drawImage(this, 0, 0); // draw video frame var data = ctx.getImageData(0, 0, c.width, c.height); // 现在可以直接传入filter返回的结果 ctx.putImageData(new FilterDistortion().filter(data), 0, 0); ctx.globalCompositeOperation = "source-over"; // "reset" // rinse, repeat }
<canvas id=c></canvas>
内容的提问来源于stack exchange,提问作者Rami Chasygov
相关产品推荐
相关产品推荐

