-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Process data uri images synchronously #4105
Conversation
This causes the image source to be updated at the same time as the image location.
This commit is the indentation that was omitted from previous commit for clarity
I'm not sure what's going on with the tests.
And another timeout. I already tried restarting them once with no change. Any ideas? |
Tests are passing now. |
src/components/images/draw.js
Outdated
var canvas = document.createElement('canvas'); | ||
canvas.width = this.width; | ||
canvas.height = this.height; | ||
if(d.source && d.source.slice(0, 5) === 'data:') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d.source
should always be a string at this stage, so you could use:
if(d.source.indexOf('data:') === 0) {
// ...
}
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d.source
can be large though, isn't slice(0, 5)
going to be a lot faster than indexOf
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point @alexcjohnson. @etpinard, anything you don't like about slice
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not obvious to me why slice
would be faster than indexOf() === 0
. Has anyone tested that out on jsperf before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking was that If the string starts with 'data:'
, then indexOf()
would terminate right away. But if not, indexOf()
would search the full string and not bail out until it reaches the end, whereas slice never needs to look past the first 5 characters. If the string doesn't start with data:
, then I guess it should be a URL which hopefully isn't terribly long. So it might not matter either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess in as far as the two options we care about are a short string starting http
and a long string starting data:
, indexOf
should be fine. The case to worry about would be a long string that doesn't start data:
... but we don't expect that and wouldn't care if it's slow.
jsperf shows a win for indexOf
https://jsperf.com/slice-vs-indexof-acj but I don't trust it, there's no way it could actually do indexOf
in 2 clock cycles, compiler must have turned it into a constant. This isn't happening in a loop so either option is fast enough to be not worth worrying about.
Thanks @jonmmease - this works! Before merging this, it would be cool to add one test checking that layout images with data uri as plotly.js/test/jasmine/tests/heatmap_test.js Lines 735 to 741 in ffa6e34
|
Oh and @jonmmease , I put this under the |
Thanks @etpinard, yeah that test approach make sense. I'll give it a go! Not urgent, 1.50.0 is fine. Thanks for the quick review! |
- Stash prior input image src string as the this._imgSrc property - Pull datauti code outside of the promise, so that we skip creating the promise and skip creating an Image() object, and skip running the onload function.
Ok, in c310c92 I pulled the data uri image handling outside of the promise. So in this case we don't create the promise, construct an In dd585b2, I added a test to make sure that we don't construct any Note: I tried to avoid the |
Very strong PR @jonmmease ! Thanks very much for your help! We haven't started merging things for |
Thanks for the reviews @etpinard, nice to dip my toe into the code base again! |
We've started merging things for 1.50.0, so @jonmmease merge away 💃 💃 |
This PR is the result of some time spent debugging an odd behavior I was seeing when using plotly.js layout images to display interactive results generated by Datashader.
The basic idea is that every time the viewport of a plot changes (pan or zoom), a new image is generated by datashader to be displayed for that viewport. By encoding these images a data uris, I've been able to display them pretty successfully using
layout.image
objects.But one problem has been that sometimes the images appear to jump right before they are updated. Here's what that looks like.
After playing with this for a while, I realized that what's happening is that the image position (with the old image) is getting updated a moment before the new image is displayed. Here's a pure JavaScript example that shows the problem a little more clearly. (gist: https://gist.github.com/jonmmease/d2793251fdb1f370e483d54ca6561690). In this snippet, each time the viewport changes,
Plotly.react
is used to update the image with a newsource
and a new position, where the position is always the bottom right corner of the viewport.Notice how the old image is moved to the bottom right corner before the new image is displayed.
After digging into plotly.js a bit, I realized that the reason this is happening is that the displayed image is updated in an
img.onload
callback that is executed after the image has loaded. This callback then builds and saves the data uri form of the image using a canvas element.This PR makes a small adjustment to bypass the
onload
callback for images that are specified as data uri's already. In this case we can use thelayout.image.source
string directly. With this change, images specified as data uris will update at the same time that their position is updated. This change is in commit 536c90e. Commit 0a397c4 contains the indentation that I omitted from 536c90e in order to make a cleaner diff of the logic changes.Here is what the example above looks like in this PR:
Let me know if there are any specific tests you'd like to see for this change. I did notice that there is already a data uri image being tested by the
layout_image
mock.