javascript - 为 HTML Canvas 创建自己的过滤器

标签 javascript html css image canvas

我想为 HTML Canvas 创建自己的过滤器。下面的代码。我得到错误:

Failed to execute 'putImageData' on 'CanvasRenderingContext2D': parameter 1 is not of type 'ImageData'

但是如果我改变return outputData;filter(input, values){} 的末尾与

for (let i = 0; i < outputData.length; i++){ inputData[i] = outputData[i]; }

一切正常,但我想,filter(input, values){}返回值。我该怎么做?

//////////////////////////////
// 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];
            }
          }
        }
      }
    }

    return outputData;
  }

  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);
  ctx.putImageData(new FilterDistortion().filter(data), 0, 0);
  ctx.globalCompositeOperation = "source-over";  // "reset"
  // rinse, repeat
}
<canvas id=c></canvas>

最佳答案

错误消息说明了一切,您需要传递一个 ImageData作为 putImageData 的第一个参数。
这里你传递了一个 UInt8ClampedArray。

所以你要么创建一个 new ImageData从这个 TypedArray(当 ImageData 构造函数可用时),或到 create an empty ImageData从 Canvas 上下文中,然后将其所有 .data 的值设置为新值。

//////////////////////////////
// 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];
            }
          }
        }
      }
    }

    return outputData;
  }

  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.crossOrigin=1; 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);
  var distortedData = new FilterDistortion().filter(data);
  var distortedImg;
  try{
    distortedImg = new window.ImageData(distortedData, c.width, c.height);
  }
  catch(e) {
    distortedImg = ctx.createImageData(c.width, c.height);
    distortedImg.data.set(distortedData);
  }
  ctx.putImageData(distortedImg, 0, 0);
  ctx.globalCompositeOperation = "source-over";  // "reset"
  // rinse, repeat
}
<canvas id=c></canvas>

关于javascript - 为 HTML Canvas 创建自己的过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49311237/

相关文章:

JavaScript 或 JQuery : get innermost element on position

html - 只有嵌套在 anchor 中的 div 的上半部分是可点击的

javascript - 如何在js中生成灵活的宽度/高度/行/列的TABLE

html - CSS/HTML : ul:empty selector won't match an empty list

Jquery/CSS – 将类添加到第一个 :p of multiple div with specified class

html - 如何自动调整框的大小以适应内容——使用 jQuery Mobile

CSS ID 与类

javascript - 哪种单元测试设置是正确的?这两个测试是否都检查功能是否正确并且工作正常?

javascript - jquery数据属性多值

javascript - style.backgroundColor 在 JavaScript 中是一个空字符串