Make Layer.toImage() accept bounds rather than size (#17129)

It allows callers to account for things like shadows that paint
outside the bounds of the widget.

https://github.com/flutter/flutter/issues/16859
diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart
index 031b532..aad6951 100644
--- a/packages/flutter/lib/src/rendering/layer.dart
+++ b/packages/flutter/lib/src/rendering/layer.dart
@@ -519,8 +519,9 @@
 
   /// Capture an image of the current state of this layer and its children.
   ///
-  /// The returned [ui.Image] has uncompressed raw RGBA bytes, in the
-  /// dimensions [logicalSize] multiplied by the [pixelRatio].
+  /// The returned [ui.Image] has uncompressed raw RGBA bytes, will be offset
+  /// by the top-left corner of [bounds], and have dimensions equal to the size
+  /// of [bounds] multiplied by [pixelRatio].
   ///
   /// The [pixelRatio] describes the scale between the logical pixels and the
   /// size of the output image. It is independent of the
@@ -532,11 +533,12 @@
   ///
   ///  * [RenderRepaintBoundary.toImage] for a similar API at the render object level.
   ///  * [dart:ui.Scene.toImage] for more information about the image returned.
-  Future<ui.Image> toImage(Size logicalSize, {double pixelRatio: 1.0}) async {
+  Future<ui.Image> toImage(Rect bounds, {double pixelRatio: 1.0}) async {
+    assert(bounds != null);
     assert(pixelRatio != null);
     final ui.SceneBuilder builder = new ui.SceneBuilder();
-    final Matrix4 transform = new Matrix4.diagonal3Values(pixelRatio, pixelRatio, 1.0);
-    transform.translate(-offset.dx, -offset.dy, 0.0);
+    final Matrix4 transform = new Matrix4.translationValues(bounds.left - offset.dx, bounds.top - offset.dy, 0.0);
+    transform.scale(pixelRatio, pixelRatio);
     builder.pushTransform(transform.storage);
     addToScene(builder, Offset.zero);
     final ui.Scene scene = builder.build();
@@ -544,8 +546,8 @@
       // Size is rounded up to the next pixel to make sure we don't clip off
       // anything.
       return await scene.toImage(
-        (pixelRatio * logicalSize.width).ceil(),
-        (pixelRatio * logicalSize.height).ceil(),
+        (pixelRatio * bounds.width).ceil(),
+        (pixelRatio * bounds.height).ceil(),
       );
     } finally {
       scene.dispose();
diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart
index 58bac8a..d67d1e8 100644
--- a/packages/flutter/lib/src/rendering/proxy_box.dart
+++ b/packages/flutter/lib/src/rendering/proxy_box.dart
@@ -2515,7 +2515,7 @@
   ///  * [dart:ui.Scene.toImage] for more information about the image returned.
   Future<ui.Image> toImage({double pixelRatio: 1.0}) {
     assert(!debugNeedsPaint);
-    return layer.toImage(size, pixelRatio: pixelRatio);
+    return layer.toImage(Offset.zero & size, pixelRatio: pixelRatio);
   }
 
 
diff --git a/packages/flutter/test/rendering/view_test.dart b/packages/flutter/test/rendering/view_test.dart
new file mode 100644
index 0000000..7da7bad
--- /dev/null
+++ b/packages/flutter/test/rendering/view_test.dart
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/rendering.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'rendering_tester.dart';
+
+void main() {
+  group('RenderView', () {
+    test('accounts for device pixel ratio in paintBounds', () {
+      layout(new RenderAspectRatio(aspectRatio: 1.0));
+      pumpFrame();
+      final Size logicalSize = renderer.renderView.configuration.size;
+      final double devicePixelRatio = renderer.renderView.configuration.devicePixelRatio;
+      final Size physicalSize = logicalSize * devicePixelRatio;
+      expect(renderer.renderView.paintBounds, Offset.zero & physicalSize);
+    });
+  });
+}
diff --git a/packages/flutter_test/lib/src/matchers.dart b/packages/flutter_test/lib/src/matchers.dart
index d4df942..3c5a085 100644
--- a/packages/flutter_test/lib/src/matchers.dart
+++ b/packages/flutter_test/lib/src/matchers.dart
@@ -1235,7 +1235,7 @@
     }
     assert(!renderObject.debugNeedsPaint);
     final OffsetLayer layer = renderObject.layer;
-    final Future<ui.Image> imageFuture = layer.toImage(element.size);
+    final Future<ui.Image> imageFuture = layer.toImage(renderObject.paintBounds);
 
     final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
     return binding.runAsync<String>(() async {