在JavaScript中通过值复制imageData

14
在我的应用程序中,我有一个处理Canvas ImageData的函数。它看起来像这样:

function processData(imagedata, filters) {
  var data = imagedata.data;
  //manipulate data with filters
  imageData.data = data;
  return imageData;
}

我一直这样使用:

var imageData = processData(imageData, {...});

但是由于imageData对象被按引用传递,因此也可以这样使用:

processData(imageData, {...}); // no assignment

我在项目中遇到了这样的问题,需要在处理图像数据时仍然能够访问原始数据。我的第一次尝试类似于以下代码:

var originalData = imageData;
var processedData = processData(imageData, {...});

当然,这将导致两个相同的imageData。

因此,我的第二个想法是编辑processsData函数,使其以某种方式操作imageData的副本,而不是所传递的imageData。我所有尝试都失败了或者极度低效。只是想知道是否有一种特殊的方法可以做到这一点。提前感谢。


你试过在将其传递给函数之前自己复制它吗? - Pointy
不管我在哪里复制,但我只想找到一种轻松的方法来进行复制。 - user523285
这个“图像数据” - 它是从画布中获取的,对吗?如果是这样,那么它只是一个数组...(编辑 好吧,大多数情况下是一个数组) - Pointy
4个回答

31

根据最新规范ImageData对象的data属性被初始化为一个Uint8ClampedArray对象(而不是之前使用的CanvasPixelArray对象),因此ImageData中的数据可以很容易地使用set方法进行复制:

function copyImageData(ctx, src)
{
    var dst = ctx.createImageData(src.width, src.height);
    dst.data.set(src.data);
    return dst;
}

这需要使用画布上下文作为ctx,对吧?如果您已经有图像数据URL,则不需要画布。您可以以多种方式复制数组。我认为Chris Wininger在下面的答案应该被接受为更简单的答案,它可以普遍地使用Uint8ClampedArray类,这个类通常很少使用,但非常简短和简洁地完成了问题所要求的工作... - OG Sean
这似乎过于复杂了。我认为它可以 - 而且考虑到这似乎将副本连接到原始画布元素,可能应该 - 用return new ImageData(src.data, src.width, src.height)替换。 - 75th Trombone

10

我想在这个帖子中增加一种解决方法,并附上一个链接,指向我找到该解决方案的重复帖子。我在此重新发布它,因为在我的情况下,它提供了一个优势。

他们建议使用以下方法简单地克隆ImageData对象的数据:

var dataCopy = new Uint8ClampedArray(imageData.data);

稍后,如果您想将此数据恢复为ImageObject,则使用以下方法。
imageData.data.set(dataCopy);

imageData 可能是你克隆数据的原始实例或一个新的 ImageData 对象。

在我的情况下,这种方法更好,因为我正在 WorkerThread 内部使用 ImageData 对象。WorkerThreads 无法访问 canvas 对象或其上下文,据我所知,在这里使用的技术对我没有帮助。然而,这个解决方案起作用了。谢谢大家。

重复线程提供其他解决方案。


1

我认为你应该很容易地创建一个副本:

function copyImageData(context, original) {
  var rv = context.createImageData(original.width, original.height);
  // would 
  //   rv.data = Array.prototype.slice.call(original.data, 0);
  // work?
  for (var i = 0; i < original.data.length; ++i)
    rv.data[i] = original.data[i];
  return rv;
}

第一个参数是图形上下文。

然后你可以使用副本调用 "processImageData":

var newImage = processImageData(copyImageData(context, imageData));

编辑——我将尝试使用“slice()”函数来操作“data”数组,我认为它会起作用,但最好还是确认一下。


链接到 Fiddle? - OG Sean

0

ImageData构造函数可用于克隆图像数据。

function cloneImageData(imgData) {
  return new ImageData(imgData.data, imgData.width, imgData.height)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接