-
Notifications
You must be signed in to change notification settings - Fork 5.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
[canvaskit] Decode images using <img> tag decoding #53201
[canvaskit] Decode images using <img> tag decoding #53201
Conversation
This pull request executed golden file tests, but it has not been updated in a while (20 days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold. |
This change causes the CanvasKit renderer to default to decoding using |
} | ||
|
||
final int scaledWidth = scaledSize.width.toInt(); | ||
final int scaledHeight = scaledSize.height.toInt(); |
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.
Maybe scaledImageSize
should switch to BitmapSize
to avoid rounding? There's also the issue that we check for allowUpscaling
before calling toInt
, which may end up upscaling anyway.
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.
Switched to BitmapSize
to avoid ugly .toInt()
and .toDouble()
calls where possible. Checking for allowUpscaling
before calling toInt()
should be okay here since we're only working with int
s for the width and height.
scaledHeight, | ||
); | ||
final DomImageData imageData = | ||
ctx.getImageData(0, 0, scaledWidth, scaledHeight); |
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.
Safari can run out 2D canvas memory easily. To prevent that set the size of the htmlCanvas
to 0x0. That forces Safari to reclaim the underlying image buffer eagerly. Alternatively, maybe the htmlCanvas
could be reused. If the app scaled an image once, it may do it again, but that's speculative.
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.
Done.
height: scaledHeight, | ||
); | ||
final DomCanvasRenderingContext2D ctx = | ||
htmlCanvas.getContext('2d')! as DomCanvasRenderingContext2D; |
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'm guessing that despite the slowness (due to https://bugs.webkit.org/show_bug.cgi?id=267291) a 2d OffscreenCanvas
with transferToImageBitmap
would still be faster than an on-screen 2d canvas getImageData
. The latter is guaranteed to always copy, in addition to other copying that happens in canvasKit.MakeImage
.
However, if use ImageBitmap
as the source, then we'd have to use MakeLazyImageFromTextureSource
, which has been a bit unreliable.
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.
Done.
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.
Unreliable how? I think we should try to avoid explicitly pulling the bytes out of the image. Is there a reason we can't just create an ImageBitmap
and use that, instead of grabbing the image data? I also should mention that createImageBitmap
actually allows you to scale the image while extracting it from the image source: https://developer.mozilla.org/en-US/docs/Web/API/createImageBitmap
/// For images which are decoded via an HTML Image element, this field holds | ||
/// the image element from which this image was created. | ||
/// | ||
/// Skia owns the image element and will close it when it's no longer used. |
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.
How does Skia close the image element? VideoFrame
and ImageBitmap
provide the close()
method, but is there something equivalent for <img>
tag?
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 checked the Skia code and it only automatically disposes the VideoFrame
. We should manually dispose of the ImageBitmap
when dispose
is called. As for the <img>
tag, I think the best we can do is let it be garbage collected with this Image.
return CkImage.cloneOf( | ||
box, | ||
videoFrame: videoFrame?.clone(), | ||
imageElement: imageElement, |
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.
Does this need to do anything with bitmapImage
? If not, it would be good to explain why not.
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.
Yes, it should. I overlooked the clone
call when adding ImageBitmap
here.
…erkelsen/engine into pr/harryterkelsen/53201
0b426fe
to
b207ee7
Compare
final int scaledWidth = scaledSize.width; | ||
final int scaledHeight = scaledSize.height; | ||
|
||
final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas( |
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 think you can avoid creating an offscreen canvas and so on here by just using createImageBitmap
instead. https://developer.mozilla.org/en-US/docs/Web/API/createImageBitmap It has resizeWidth
and resizeHeight
options which will to the rescaling for you.
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.
Harry pointed out that createImageBitmap
is async, which makes this much less clean. So we should leave it as is.
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.
LGTM!
final int scaledWidth = scaledSize.width; | ||
final int scaledHeight = scaledSize.height; | ||
|
||
final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas( |
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.
Harry pointed out that createImageBitmap
is async, which makes this much less clean. So we should leave it as is.
Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change). If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review. |
…152046) flutter/engine@9739d92...6312dfc 2024-07-19 1961493 [email protected] [canvaskit] Decode images using <img> tag decoding (flutter/engine#53201) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/ doc/main/autoroll/README.md
Prefer to decode images using the browser API rather than with CanvasKit to avoid jank when decoding.
Part of deprecating the HTML renderer: flutter/flutter#145954
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.