How to clone ImageData?
Asked Answered
F

3

16

This is working, but I feel this code is lengthy. I'm looking for better idea.

var clone = function(imageData) {
  var canvas, context;
  canvas = document.createElement('canvas');
  canvas.width = imageData.width;
  canvas.height = imageData.height;
  context = canvas.getContext('2d');
  context.putImageData(imageData, 0, 0);
  return context.getImageData(0, 0, imageData.width, imageData.height);
};
Fiorin answered 14/6, 2012 at 5:39 Comment(1)
Possible duplicate of Copy imageData by value in JavaScriptAsomatous
M
12

With TypedArray.prototype.set() you can directly copy the data.

var imageDataCopy = new Uint8ClampedArray(originalImageData.data);
imageDataCopy.set(originalImageData.data);

This sets the content of imageDataCopy to be identical to originalImageData.

Mack answered 13/12, 2012 at 15:20 Comment(6)
What is the original object and what is the clone? doens't seem to work for meCarine
This is a bad and unclear code example - additionally, I suspect it is outdated and doesn't work with contemporary browsers.Mink
Hakan, the MDN documentation says this is valid, and it worked for me. I submitted an edit to clarify this.Tying
So each of these lines alone would be enough, or the pair of two lines is required? If you need both lines, what does the first line on its own do?Ammonia
@Ammonia - The new operator is used to create a new instance of a constructor so in theory: 1st line=clone structure, 2nd line=copy data... However this method didn't actually work for me, but the other answer did what I needed, perhaps because the other answer includes the ImageData() function to actually create the ImageData object.Sixteenth
@Ammonia - ...on second thought, after wasting a bunch of time with these methods, I realized I can "clone" canvas image data by simply by assigning it to a new variable. See my answer with demo below.Sixteenth
P
19

The ImageData constructor accepts the image data array.

const imageDataCopy = new ImageData(
  new Uint8ClampedArray(imageData.data),
  imageData.width,
  imageData.height
)
Patras answered 3/1, 2018 at 13:31 Comment(1)
For those wondering, like me, no, ImageData constructor doesn't clone the array argument, and uses it as its data, so new Uint8ClampedArray is necessary. Source.Specification
M
12

With TypedArray.prototype.set() you can directly copy the data.

var imageDataCopy = new Uint8ClampedArray(originalImageData.data);
imageDataCopy.set(originalImageData.data);

This sets the content of imageDataCopy to be identical to originalImageData.

Mack answered 13/12, 2012 at 15:20 Comment(6)
What is the original object and what is the clone? doens't seem to work for meCarine
This is a bad and unclear code example - additionally, I suspect it is outdated and doesn't work with contemporary browsers.Mink
Hakan, the MDN documentation says this is valid, and it worked for me. I submitted an edit to clarify this.Tying
So each of these lines alone would be enough, or the pair of two lines is required? If you need both lines, what does the first line on its own do?Ammonia
@Ammonia - The new operator is used to create a new instance of a constructor so in theory: 1st line=clone structure, 2nd line=copy data... However this method didn't actually work for me, but the other answer did what I needed, perhaps because the other answer includes the ImageData() function to actually create the ImageData object.Sixteenth
@Ammonia - ...on second thought, after wasting a bunch of time with these methods, I realized I can "clone" canvas image data by simply by assigning it to a new variable. See my answer with demo below.Sixteenth
S
-3

Most times it should be sufficient to simply assign the imageData to a new variable, just like:

myImgData=ctx.getImageData(0,0,c.width,c.height); //get image data somewhere
imgDataCopy = myImgData;   // clone myImgData

...now imgDataCopy contains a separate copy of myImgData. 🤷‍♂️


Demo

The snippet below creates 4 "frames" in an array of ImageData's and then loop through them.

const c = document.getElementById('canvas'),
      ctx = c.getContext("2d");
var wh=70, iDatas=[], i=0,
    lines=[[10,10,wh-10,wh-10], [wh/2,5,wh/2,wh-5],  // ⤡,↕
           [wh-10,10,10,wh-10], [5,wh/2,wh-5,wh/2]]; // ⤢,↔
c.width=wh;
c.height=wh;

ctx.strokeStyle='blue'; //setup to draw
ctx.lineWidth=9;
ctx.lineWidth='round';

for(var [x1,y1,x2,y2] of lines){ 
  ctx.beginPath();
  ctx.moveTo(x1,y1); //draw something
  ctx.lineTo(x2,y2);
  ctx.stroke();
  var d=ctx.getImageData(0,0,c.width,c.height); //get imgdata
  iDatas.push( d ); //save imgdata to array
  ctx.clearRect(0, 0, c.width, c.height); //clear canvas
}

ctx.strokeStyle='green'; //❌has no effect: 
  //  ↑ shows that color data comes from the source (can't be changed)
ctx.lineWidth='round'; //❌has no effect: 
  //  ↑ shows that non-color styling does NOT come from source (CAN'T be changed)

//now you can refer to the image data as iData[i] where i= 0 to 3
drawFrame();
function drawFrame(){
  ctx.putImageData( iDatas[i],0,0); //draw imgData from array
  i=(i==3?0:i+1); //set next iteration #
  setTimeout(function(){ drawFrame() }, 100); //schedule next frame
}
canvas{ border:2px dotted salmon; }
<canvas id='canvas'></canvas>
Sixteenth answered 11/9, 2021 at 0:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.