Warm tip: This article is reproduced from stackoverflow.com, please click
bitmap canvas javascript

Difference between ImageBitmap and ImageData

发布于 2020-04-13 09:37:03

What is the difference between ImageBitmap and ImageData in JavaScript? when do you want the one or the other?

Questioner
Endless
Viewed
53
Kaiido 2020-02-03 13:32

An ImageBitmap holds a reference to bitmap data, which can be passed to and stored directly in the GPU.

An ImageData holds a reference to a canvas pixel ArrayBuffer, which itself represents raw pixel values as an Array of un-premultiplied RGBA color channel values, which is only used by the CPU.

The former can be painted directly by the GPU, with no other operations required. The latter needs to be read (often with alpha pre-multiplication) then moved to the GPU before it can be painted.

They will not take the same amount of time to get copied to the canvas bitmap (a.k.a "painted").

var target = document.getElementById('target');
var ctx = target.getContext("2d");

var imageData = ctx.createImageData(300,150);
var imageBitmap = null;

// fill our ImageData with noise
const data = new Uint32Array(imageData.data.buffer);
for(let i=0; i<data.length; i++) {
  data[i] = Math.random()*0xFFFFFF + 0xFF000000;
}
// initial draw
ctx.putImageData(imageData, 0,0);
// we create our ImageBitmap from the current state
// (=> ~ same bitmap as 'imageData')
createImageBitmap(target).then((bmp)=>{
	imageBitmap = bmp;
  btn.disabled = false;
});


// Benchmark.js playground borrowed from 
// https://jsfiddle.net/533hc71h/

var test1_name = 'ImageData';
function test1()
{
	ctx.putImageData(imageData, 0, 0);
}
var test2_name = 'ImageBitmap';
function test2()
{
	ctx.drawImage(imageBitmap, 0, 0);
}
function teardown()
{
	ctx.clearRect(0,0,target.width,target.height);
}

var cycleResults = document.getElementById('cycleResults');
var result = document.getElementById('result');
var btn = document.getElementById('btn');

// BENCHMARK ====================
btn.onclick = function runTests(){

  btn.setAttribute('disable', true);
	cycleResults.innerHTML = '';
  result.textContent = 'Tests running...';
  
  var suite = new Benchmark.Suite;

  // add tests
  suite
  .add(test1_name || 'test1', test1)
  .add(test2_name || 'test2', test2)
  // add listeners
  .on('cycle', function(event) {
    var result = document.createElement('li');
    result.textContent = String(event.target);
    
    document.getElementById('cycleResults')
    	.appendChild(result);
  })
  .on('complete', function() {
    result.textContent = 'Fastest is ' + this.filter('fastest').pluck('name');
    btn.setAttribute('disable', false);
    teardown();
  })
  // run async
  .run({ 'async': true });
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/1.0.0/benchmark.min.js"></script>

<ul id='cycleResults'>

</ul>
<div id="result">

</div>
<br>
<button id="btn" disabled>
Run Tests
</button><br>

<canvas id="target"></canvas>

Running the above snippet I get around 5K OPS (Operations Per Seconds) for drawing the ImageData, and 200K+ for the ImageBitmap on Chrome (44K vs 125K in FF).

However you can't modify an ImageBitmap, nor read its content in any meaningful way.

So,

  • If you need to draw a Bitmap, use an ImageBitmap.
  • If you need to read / manipulate the image's data, then use an ImageData.

And remember that now we also have means to hold canvas contexts directly in a Worker thanks to the OffscreenCanvas API, might suit your needs too.