是否可以从画布上下文的getImageData生成Web worker中的图像(blob或data-url)?

时间:2016-01-27 00:27:28

标签: javascript canvas web-worker getimagedata todataurl

后台设置

我有一个Web应用程序,用于处理从一组其他图像创建图像。我选择这样做的方法是通过读取一组图像并将它们放在HTML画布上。然后,我使用toDataURL将每个画布作为jpeg导出到第三方API并将其转换为Blob。我面临的问题是,我有很多这些画布都将数据导出为jpg,并且消耗了大量资源。当每个画布尝试调用toDataURL时,应用程序会变慢并变得无响应。

问题

我发现调用画布的toDataUrl()toBlob()可能会非常慢,特别是对于大画布尺寸。我想利用Web worker的多线程特性。

首先,我尝试传入canvas对象但是抛出了一个错误。事实证明,对象是一个问题,它们似乎要么被转换为字符串,要么在无法克隆时失败。无论哪种方式,我发现传递上下文的图像数据确实有效。数据以原始RGB值的形式传递为画布上下文的方法Uint8ClampedArray中的getImageData()

Main.js

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var worker = new Worker('myWorker.js');
worker.postMessage({
  image: context.getImageData(0, 0, canvas.width, canvas.height)
});

myWorker.js

this.onmessage = function(e) {
  // GOAL: turn e.data.image into an image blob or dataUrl and return it.
  // e.g. this.postMessage(new Blob([e.data.image.data], {type: 'image/jpg'});
}

我认为归结为知道如何将保存RGB信息的Uint8ClampedArray转换为jpg / png数据。

我认为这可能有用的原因是我认为getImageData只是从画布上下文中复制现有数据结构,因此不如toDataUrl那么昂贵。我在调用类似于下面代码块的内容时捕获了cpu配置文件:

var image = context.getImageData(0, 0, canvas.width, canvas.height)
var dataUrl = canvas.toDataURL('image/jpeg');

得到了:

Performance results from getImageData and toDataURL

所以,考虑到这一点,我想把这个过程的主要负担转移到网络工作者身上。我甚至不介意,只要它在另一个进程中发生,它在Web工作者中需要更长的时间。

对此有一些额外的想法:

  • 添加额外的库来进行转换是可以的,但是提供如何添加外部库作为Web工作文件依赖项的奖励点。现在我正在使用browserify来申请。也许为Web worker创建另一个浏览器化的包?
  • 我最终需要一个jpeg(对于第三方API),所以将它转换为png只是转换为jpeg的一个步骤。
  • 我尝试降低encoderOptions toDataURL中的第二个选项,以加快这一过程,但我没有看到太多变化

1 个答案:

答案 0 :(得分:6)

<强> ---- ---- UPDATE

我以为我会以npm库的形式分享我的解决方案: https://www.npmjs.com/package/jpeg-web-worker。它解释了如何利用提供的网络工作者为您做繁重的工作。

<强> ---------------------

我找到了一个适合我的解决方案,可以在生成新图像的同时加快应用程序和页面的响应速度。

以下是应用代码:

应用

var canvas = $('#myCanvas')[0];
var context = canvas.getContext('2d');
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var worker = new Worker('myWorker.js');
worker.postMessage({
  image: imageData
});
worker.onmessage = function(e) {
  var blob = new Blob( [e.data.data], {type: 'image/jpeg'} );
  // use blob
}

这是工人代码:

<强>工人

this.onmessage = function(e) {
  var jpgInfo = encode(e.data.image, 50);
  this.postMessage(jpgInfo);
}

function encode() { ... } // ported from jpeg-js

显然,这个答案的大部分来自encode函数。此功能已从npm模块jpeg-js进行了修改,更具体地说,是文件encoder.js。我通过将整个encoder.js文件复制到myWorker.js中来移植编码功能。它不是很小,但它也非常独立,这使它变得容易。我留下的唯一问题是修改代码,使其在为其构建的node.js环境之外工作。

事实证明这相对容易:

  1. 将“const”变量声明转换为“var”
  2. 删除对Buffer的引用。这是一个两步的过程。首先,删除顶部的atob定义(因为它不需要)。其次,在this.encode函数的末尾返回new Unit8Array。当前版本实际上已将此注释放在缓冲区引用的正上方。只需使用那个并删除下面的所有内容。
  3. 删除对module.export的引用。这就像删除该行一样简单,因为我们在此文件中只需要此功能。
  4. 我没有精确的定时测量,但是当图像生成到滞后时间的第二个时,它从延迟时间的~10秒开始。我在这里使用“滞后时间”意味着在使用页面时性能低下。