Canvas Drawimage Slow First Time Another Canvas Is Used As The Source Argument
Solution 1:
I think you just hit a weird optimization quirk here and it might be quite hard to have a definitive answer as to what happens, but I'll try to make an educated guess anyway.
It seems the browser comes back to the CPU thread before the GPU actually has executed the job it was assigned, taking full advantage of tasks parallelism.
So in the first loop, the GPU will start a job asking to draw the <img>
to the <canvas>
, this image's data which even though is decoded in Chrome, still has to be transfered to the GPU and converted to an actual bitmap.
But as we said, this is done in parallel and thus the js script can continue and proceed with the second loop directly while doing this job.
However when it comes at drawing the first target canvas on the second, it will see that there is one running GPU job that will modify it, and will thus block the CPU thread until the first drawing has completed.
The next iterations though will only deal with <canvas>
sources which bitmap buffers are already on the GPU, so they won't take any relevant time.
We can somehow confirm this by simply waiting a few ms between each iterations. Doing so, all canvas-to-canvas operations will take about 0ms.
var url1 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?bar" + Math.random();
var url2 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?foo" + Math.random();
var sourceImage = newImage(); // Original image
sourceImage.crossOrigin = true;
var myImages = []; // Array of image and canvases references
myImages[0] = sourceImage; // Set first myImage to image source// Image onload
sourceImage.onload = asyncfunction() {
console.log("Imageload", newDate() - t0);
myImages[0] = sourceImage;
// create canvases before hand to be sure it's not part of the issuefor (var i = 1; i <= 4; i += 1) {
// Create canvas
myImages[i] = document.createElement("canvas");
// Set canvas dimensions to same as original image
myImages[i].width = myImages[0].width;
myImages[i].height = myImages[0].height;
myImages[i].getContext("2d");
}
// Loop to create and draw on canvasesfor (var i = 1; i <= 4; i += 1) {
// Draw last canvas / image onto this canvas
t0 = newDate();
myImages[i].getContext("2d").drawImage(
myImages[i - 1],
0,
0,
myImages[i].width,
myImages[i].height
);
console.log("drawImage", i, newDate() - t0);
awaitnewPromise(r =>setTimeout(r, 500));
}
// Finished with black.jpg so load white.jpg if (sourceImage.getAttribute("src") == url1) {
sourceImage.src = url2
}
};
// Load image
t0 = newDate();
sourceImage.src = url1;
Similarly, if we do generate ImageBitmaps of each source, we can see that the one taking the most time is as expected the <img>
:
var url1 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?bar" + Math.random();
var url2 = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?foo" + Math.random();
var sourceImage = newImage(); // Original image
sourceImage.crossOrigin = true;
var myImages = []; // Array of image and canvases references
myImages[0] = sourceImage; // Set first myImage to image source// Image onload
sourceImage.onload = asyncfunction() {
console.log("Imageload", newDate() - t0);
myImages[0] = sourceImage;
// create canvases beforehand to be sure it's not part of the issuefor (var i = 1; i <= 4; i += 1) {
// Create canvas
myImages[i] = document.createElement("canvas");
// Set canvas dimensions to same as original image
myImages[i].width = myImages[0].width;
myImages[i].height = myImages[0].height;
myImages[i].getContext("2d");
}
// Loop to create and draw on canvasesfor (var i = 1; i <= 4; i += 1) {
// wait for create ImageBitmap to be created
t0 = newDate();
const img = awaitcreateImageBitmap(myImages[i - 1]);
console.log("createImageBitmap", i, newDate() - t0);
t0 = newDate();
myImages[i].getContext("2d").drawImage(
img,
0,
0,
myImages[i].width,
myImages[i].height
);
console.log("drawImage", i, newDate() - t0);
}
// Finished with black.jpg so load white.jpg if (sourceImage.getAttribute("src") == url1) {
sourceImage.src = url2
}
};
// Load image
t0 = newDate();
sourceImage.src = url1;
Ps: one might be tempted to call getImageData
to force coming back to the CPU thread synchronously, but doing so we also transfer all the canvases bitmaps back and forth between CPU and GPU, creating actually the same slowness at every loops.
Post a Comment for "Canvas Drawimage Slow First Time Another Canvas Is Used As The Source Argument"