-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Add toImage to RenderRepaintBoundary #16758
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
Conversation
13717d2 to
28caa4d
Compare
573b758 to
6ef6eb7
Compare
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.
Don't say "multiplied by the [window.devicePixelRatio]" since it's covered below in the discussion of the [pixelRatio] named parameter?
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.
Yeah, sorry, I guess that's irrelevant now that I added pixelRatio.
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.
the render object must not need layout, painting, or compositing
How does the caller know whether this is the case?
Also, can we assert it below?
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.
Added asserts, and an explanation.
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.
Where is anti-aliasing usually done when the render object is drawn to the screen?
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 it's antialiased at the device level (in the GPU), but since the engine downsamples it with nearest neighbor, anything smaller than the device resolution is not effectively antialiased. At least I think that's the case, and Jason says that it can actually take one of several paths in the engine, so it's not really clear to me when it happens. I decided to take out the advice: they can look at the result and decide for themselves to increase the size or 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.
I don't really understand how we're actually rendering this, but I would presume we're just uploading the whole scene to the GPU, having it render to a texture, then grabbing the texture? In which case I'd expect antialiasing to work fine?
cc @jason-simmons or @chinmaygarde
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.
Yeah, what @Hixie said. We have to render to texture on the GPU first because the scene may reference images that are only resident on the GPU. AA should work just fine.
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 have to be [ui.Scene.toImage]?
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, but I wasn't sure if dartdoc would pick it up correctly or not if I did that.
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.
search for [ui. or [dart:ui. and compare to actual output, I forget which one is the right one.
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.
Is it worth optimizing out this matrix multiply for the case where pixelRatio is 1.0?
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.
No, not really. From what I can tell, calling Matrix4.identity() is slower than the diagonal3Values call.
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.
try/finally with a call to scene.dispose() in the finally?
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.
Good call. Added.
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.
Add a test that expects our assertion error if the render object isn't a repaint boundary?
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.
No, we don't usually test asserts as far as I can tell, and if my call doesn't assert, then the layer getter will.
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.
we do test asserts sometimes (check for takeException), but this point will be moot anyway if you move it to RenderRepaintBoundary.
tvolkert
left a comment
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
15bce3f to
ebf0fb8
Compare
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.
To avoid people depending on particular widgets that happen to be repaint boundaries but might not always be, I recommend putting this on RenderRepaintBoundary.
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.
Yeah, I guess that makes sense. It means that they'll have to cast the result when they get the render object in order to call this function, but that's not horrible.
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.
paintBounds is a bit of a dubious choice because it's just advisory really.
If you move this to RenderRepaintBoundary, you can just use size, which is much more authoritative.
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.
We should replace "dimensions" with the actual property that we use to determine the dimensions (currently [paintBounds], though if you take my feedback below, [size]).
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.
OK, size it 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.
rather than "must not need painting" it might be more positive to say "must have gone through the paint phase" or some such (the parenthetical is fine though)
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.
rather than using multiplied() and allocating a new matrix, you can just use translate.
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.
Good idea. 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.
I wonder if we should change toImage to just take a Size... cc @tvolkert
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.
For the use cases we've heard for this, I don't have a strong opinion 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.
You should add a test that verifies that it works (gives the right pixels) with two layers, since that was a bug.
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.
3675e9a to
d162ccc
Compare
09eeb2b to
681a657
Compare
|
I tried this out in a simple widget test: testWidgets('test', (WidgetTester tester) async {
await tester.pumpWidget(new MyApp());
RenderRepaintBoundary repaintRenderBoundary = tester
.allRenderObjects
.firstWhere((RenderObject object) => object is RenderRepaintBoundary);
ui.Image image = await repaintRenderBoundary.toImage();
ByteData bytes = await image.toByteData(format: ui.ImageByteFormat.png);
});If I run that test on an Android device/emulator, I get a correct PNG capture of the app. However, if I run that on the host in Am I holding it wrong? |
|
aha - indeed I was holding it wrong. I was calling: await new File('/path/fo/foo.png').writeAsBytes(bytes);Which doesn't play nicely with our fake async. When I call |
681a657 to
0699d76
Compare
…tter#16758) This adds a toImage function to RenderRepaintBoundary that returns an uncompressed raw image of the RenderRepaintBoundary and its children. A device pixel ratio different from the physical ratio may be specified for the captured image. A value of 1.0 will give an image in logical pixels.
This adds a toImage function to RenderRepaintBoundary that returns an uncompressed raw image of the RenderRepaintBoundary and its children. A device pixel ratio different from the physical ratio may be specified for the captured image. A value of 1.0 will give an image in logical pixels.