使用 FabricJS 对大图像应用滤镜

Applying filters to large images using FabricJS

我正在尝试对 FabricJS 中的大图像应用滤镜。

图像大约为 3000 x 4000,超过了默认的 2048 纹理大小。我可以将它增加到 4096,它适用于这张图片。

目前,当应用滤镜时,图像被剪切。我尝试使用以下方法减小图像的大小:

let obj = this.canvas.getActiveObject();
if (Math.max(obj.width, obj.height) > 2048) {
  let scale = 2048 / Math.max(obj.width, obj.height);
  obj.filters.push(
    new fabric.Image.filters.Resize({ scaleX: scale, scaleY: scale })
  );
}
obj.filters.push(new fabric.Image.filters.Grayscale());
obj.applyFilters();
this.canvas.renderAll();

但这对我不起作用。图像仍然被剪切和旋转。

这就是我将图片添加到 canvas:

的方式
//in a parent component
let url = URL.createObjectURL(e.target.files[0]);
fabric.Image.fromURL(url, (image) => {
  this.setState({
    image,
  });
});

//following is in a child component
let canvasWidth = this.canvasWrapperRef.current.clientWidth;
let canvasHeight = this.canvasWrapperRef.current.clientHeight;
//create canvas
this.canvas = new fabric.Canvas("canvas", {
  selection: false,
  backgroundColor: "black",
  hoverCursor: "context-menu",
  height: canvasHeight,
  width: canvasWidth,
});
let image = this.props.image;
image.set({ selectable: false });
this.canvas.centerObject(image);
this.canvas.setActiveObject(image);
this.canvas.add(image);
this.canvas.renderAll();

有什么方法可以减少原始图像的 size/quality,以便对其应用滤镜?

我花了几天时间,但我终于找到了解决我遇到的问题的方法。似乎当您使用 fabric 调整图像大小时,它会保留原始大小。为了解决这个问题,我将图像添加到可以处理过滤器的 canvas 大小,然后将 canvas 作为图像导出并以通常的方式将其添加到结构中。

对我来说,这似乎是一个有点老套的解决方案,它确实有效,但如果有人知道更好的解决方案,请告诉我。

//handle the image uploading process
handleImageUpload = async (e) => {
  if (e.target.files.length < 1) return;
  let objectUrl = URL.createObjectURL(e.target.files[0]);
  //resize if needed
  let imageUrl = await this.resizeImage(this.textureSize, objectUrl);
  fabric.Image.fromURL(imageUrl, (image) => {
    this.setState({
      image,
    });
  });
};

//resize the image if one of the sides of the image exceeds the max texture size
//if the image requires resizing, create a canvas element but don't attach to DOM
//size the canvas with the largest size being equal to the max texture size
//then scale the image down to the correct size when adding to the canvas
resizeImage = (maxSize, imageUrl) => {
  return new Promise((resolve) => {
    let image = new Image();
    image.src = imageUrl;
    image.onload = (img) => {
      //check if resizing is required
      if (Math.max(img.target.width, img.target.height) > maxSize) {
        //create canvas
        let canvas = document.createElement("canvas");
        //scale image
        if (img.target.height >= img.target.width) {
          canvas.height = maxSize;
          canvas.width = (maxSize / img.target.height) * img.target.width;
        } else {
          canvas.width = maxSize;
          canvas.height = (maxSize / img.target.width) * img.target.height;
        }
        //draw to canvas
        let context = canvas.getContext("2d");
        context.drawImage(img.target, 0, 0, canvas.width, canvas.height);
        //assign new image url
        resolve(context.canvas.toDataURL());
      }
      resolve(imageUrl);
    };
  });
};

我使用 2048 作为我的最大纹理尺寸。任何高于此的值和过滤器都会开始剪切和旋转图像。我认为这是由硬件决定的,我使用 2048 作为一个相对安全的限制。请参阅此处了解更多信息:http://fabricjs.com/fabric-filters

当您使用上述方法缩小图像时,您的图像质量可能会下降。更好的解决方案可能是使用 fabric 内置的缩小图像的方法(我没有测试过这个,但我想 fabric 会比上述方法更好地处理重新缩放),然后导出并重新导入类似于上面的方法。然而,由于这只是一个以学习 React 为目的的个人项目,我很高兴能够对图像质量产生影响并显示警告,指出大图像尺寸会被调整大小并且可能会降低质量。