nodeca / pica

Resize image in browser with high quality and high speed

Home Page:http://nodeca.github.io/pica/demo/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Calling getImageData in Safari raises InvalidStateError

sandstrom opened this issue · comments

When doing resizes with Pica, we sometimes get an error when pica calls getImageData.

The error that Safari throws is:

InvalidStateError · The object is in an invalid state.

It's called on this line:

https://github.com/nodeca/pica/blob/6.1.1/index.js#L362

So it's the canvas we're passing into pica. In other words, this could very well be an issue outside pica, since the canvas in question wasn't created by pica.

In any case, I wanted to open this issue to see if others had the same problem, and to store some of my thoughts around this.

Possible causes

I've done some searching and found these two blog posts:

https://pqina.nl/blog/canvas-area-exceeds-the-maximum-limit/
https://pqina.nl/blog/total-canvas-memory-use-exceeds-the-maximum-limit/

According to them this could be caused by using using a too large canvas (max width/height is 4096 x 4096), however I know pica does tiling, so that shouldn't happen. Another possible cause mentioned in that blog post, is that Safari has trouble with releasing memory allocated to canvas after they're discarded.

They offer a suggestion to remedy this:

function releaseCanvas(canvas) {
    canvas.width = 1;
    canvas.height = 1;
    const ctx = canvas.getContext('2d');
    ctx && ctx.clearRect(0, 0, 1, 1);
}

Quoting from the blog post:

We’re going to have to help out Safari to clean up its mess. Get rid of the things it can’t or won’t throw away.

When we’re done with a element we have to manually “release” it.

Meaning we resize the canvas to a very small size and clear its contents, this tricks Safari in to replacing the canvas in its storage with our compressed version.

The [above] function will do just that.

Safari will still hold on to the for a while, but at least now it won’t blow the roof of its storage depot.

Possible fixes

Applying the releaseCanvas method above may help. I checked the source and pica already sort of does this:

pica/index.js

Lines 526 to 530 in e4e6616

if (tmpCanvas) {
// Safari 12 workaround
// https://github.com/nodeca/pica/issues/199
tmpCanvas.width = tmpCanvas.height = 0;
}

Apparently I'm the one who suggested it 2 years ago (#199). Memory is short 😅

But maybe also calling clearRect would help further.

Basically enhance the existing fix to do what releaseCanvas above does. The author of that blog post has built an image editor based on canvas, so hopefully knows this area pretty well.

Versions

Pica Version: 6.1.1
Safari Version: 15.3.1, 15.4.0, 15.4.1 (we've seen this issue across ~250 users during the past few months)

TODO

  • We're going to try passing in something other than a canvas, and see if that helps.

Having the same issue when trying to downsize the 10208x10208px image on iPhone, any way to resolve this?