简体   繁体   English

如何将画布图像数据复制到其他变量?

[英]How to copy canvas image data to some other variable?

I am working on a small app, that loads user image onto a server, lets him choose one of the filters and gives image back. 我正在开发一个小型应用程序,该应用程序将用户图像加载到服务器上,让他选择其中一个过滤器并返回图像。

I need to somehow save the initial image data with no filters applied. 我需要以某种方式保存未应用过滤器的初始图像数据。

But as i found out, in JS there is no natural way to copy vars. 但是,正如我发现的那样,在JS中没有复制var的自然方法

I tried using LoDash _.clone() and one of the jQuery functions to do this, but they didn't work. 我尝试使用LoDash _.clone()和jQuery函数之一来执行此操作,但是它们不起作用。

When I applied a cloned data to image, function putImageData couldn't get the cloned data because of the wrong type. 当我将克隆的数据应用于图像时,函数putImageData由于类型错误而无法获取克隆的数据。

It seems, that clone functions somehow ignore object types. 克隆函数似乎以某种方式忽略了对象类型。

Code: 码:

var img = document.getElementById("image");
var canvas = document.getElementById("imageCanvas");
var downloadLink = document.getElementById("download");
canvas.width = img.width;
canvas.height = img.height;

var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, img.width, img.height);
document.getElementById("image").remove();

initialImageData = context.getImageData(0, 0, canvas.width, canvas.height); //initialImageData stores a reference to data, but I need a copy

///////////////////////
normalBtn.onclick = function(){
        if(!(currentState == converterStates.normal)){
             currentState = converterStates.normal;
             //here I need to apply cloned normal data
        }
};

So, what can I do here??? 那么,我在这里可以做什么???

Thanks!!! 谢谢!!!

Use : 采用 :

var image = …;
var data = JSON.parse(JSON.stringify(image).data);
var arr = new Uint8ClampedArray(data);
var copy = new ImageData(arr, image.width, image.height);

An ImageData object holds an Uint8ClampedArray which itself holds an ArrayBuffer . 一个ImageData对象包含一个Uint8ClampedArray ,它本身包含一个ArrayBuffer

To clone this ArrayBuffer , you can use its slice method, or the one from the TypedArray View you get : 要克隆此ArrayBuffer ,可以使用其slice方法,也可以使用TypedArray视图中的方法:

 var ctx = canvas.getContext('2d'); ctx.fillStyle = 'orange'; ctx.fillRect(0,0,300,150); var original = ctx.getImageData(0,0,300,150); var copiedData = original.data.slice(); var copied = new ImageData(copiedData, original.width, original.height); // now both hold the same values console.log(original.data[25], copied.data[25]); // but can be modified independently copied.data[25] = 0; console.log(original.data[25], copied.data[25]); 
 <canvas id="canvas"></canvas> 

But in your case, an easier solution, is to call twice ctx.getImageData . 但是在您的情况下,一个更简单的解决方案是调用两次ctx.getImageData

 var ctx = canvas.getContext('2d'); ctx.fillStyle = 'orange'; ctx.fillRect(0,0,300,150); var original = ctx.getImageData(0,0,300,150); var copied = ctx.getImageData(0,0,300,150); // both hold the same values console.log(original.data[25], copied.data[25]); // and can be modified independently copied.data[25] = 0; console.log(original.data[25], copied.data[25]); 
 <canvas id="canvas"></canvas> 

And an complete example : 还有一个完整的例子:

 var ctx = canvas.getContext('2d'); var img = new Image(); // keep these variables globally accessible to our script var initialImageData, filterImageData; var current = 0; // just to be able to switch easily img.onload = function(){ // prepare our initial state canvas.width = img.width/2; canvas.height = img.height/2; ctx.drawImage(img, 0,0, canvas.width, canvas.height); // this is the state we want to save initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height); // get an other, independent, copy of the current state filterImageData = ctx.getImageData(0,0,canvas.width,canvas.height); // now we can modify one of these copies applyFilter(filterImageData); button.onclick = switchImageData; switchImageData(); } // remove red channel function applyFilter(image){ var d = image.data; for(var i = 0; i < d.byteLength; i+=4){ d[i] = 0; } } function switchImageData(){ // use either the original one or the filtered one var currentImageData = (current = +!current) ? filterImageData : initialImageData; ctx.putImageData(currentImageData, 0, 0); log.textContent = current ? 'filtered' : 'original'; } img.crossOrigin = 'anonymous'; img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg'; 
 <button id="button">switch imageData</button> <code id="log"></code><br> <canvas id="canvas"></canvas> 

The same with slice : slice相同:

 var ctx = canvas.getContext('2d'); var img = new Image(); // keep these variables globally accessible to our script var initialImageData, filterImageData; var current = 0; // just to be able to switch easily img.onload = function(){ // prepare our initial state canvas.width = img.width/2; canvas.height = img.height/2; ctx.drawImage(img, 0,0, canvas.width, canvas.height); // this is the state we want to save initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height); // get an other, independent, copy of the current state filterImageData = new ImageData(initialImageData.data.slice(), initialImageData.width, initialImageData.height); // now we can modify one of these copies applyFilter(filterImageData); button.onclick = switchImageData; switchImageData(); } // remove red channel function applyFilter(image){ var d = image.data; for(var i = 0; i < d.byteLength; i+=4){ d[i] = 0; } } function switchImageData(){ // use either the original one or the filtered one var currentImageData = (current = +!current) ? filterImageData : initialImageData; ctx.putImageData(currentImageData, 0, 0); log.textContent = current ? 'filtered' : 'original'; } img.crossOrigin = 'anonymous'; img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg'; 
 <button id="button">switch imageData</button> <code id="log"></code><br> <canvas id="canvas"></canvas> 

The correct way to copy a typed array is via the static function from 复制类型数组的正确方法是通过静态函数from

eg 例如

var imageData = ctx.getImageData(0,0,100,100);
var copyOfData = Uint8ClampedArray.from(imageData.data); // create a Uint8ClampedArray copy of imageData.data

It will also allow you to convert the type 它还将允许您转换类型

var copyAs16Bit = Uint16Array.from(imageData.data); // Adds high byte. 0xff becomes 0x00ff

Note that when converting to a smaller type the extra bits are truncated for integers. 请注意,转换为较小的类型时,多余的位将被截断为整数。 When converting from floats the value not the bits are copied. 从浮点数转换时,不会复制值。 When copying between signed and unsigned ints the bits are copied eg Uint8Array to Int8Array will convert 255 to -1. 当符号和无符号整数之间复制位被复制例如Uint8ArrayInt8Array将转换255 -1。 When converting from small int to larger uint eg Int8Array to Uint32Array will add on bits -1 becomes 0xffff 当从小int转换为更大的uint时,例如将Int8ArrayUint32Array将在位-1上加上0xffff

You can also add optional map function 您还可以添加可选的地图功能

// make a copy with aplha set to half.
var copyTrans = Uint8ClampedArray.from(imageData.data, (d, i) => i % 4 === 3 ? d >> 1 : d);

typedArray.from will create a copy of any array like or iterable objects. typedArray.from将创建类似或可迭代对象的任何数组的副本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM