Fix html version of drawImageNine (#25605)

diff --git a/lib/web_ui/lib/src/engine/html/canvas.dart b/lib/web_ui/lib/src/engine/html/canvas.dart
index 6d275b7..675e730 100644
--- a/lib/web_ui/lib/src/engine/html/canvas.dart
+++ b/lib/web_ui/lib/src/engine/html/canvas.dart
@@ -287,6 +287,47 @@
     _canvas.drawImageRect(image, src, dst, paint as SurfacePaint);
   }
 
+  // Return a list of slice coordinates based on the size of the nine-slice parameters in
+  // one dimension. Each set of slice coordinates contains a begin/end pair for each of the
+  // source (image) and dest (screen) in the order (src0, dst0, src1, dst1).
+  // The area from src0 => src1 of the image is painted on the screen from dst0 => dst1
+  // The slices for each dimension are generated independently.
+  List<double> _initSlices(double img0, double imgC0, double imgC1, double img1, double dst0, double dst1) {
+    final double imageDim = img1 - img0;
+    final double destDim = dst1 - dst0;
+
+    if (imageDim == destDim) {
+      // If the src and dest are the same size then we do not need scaling
+      // We return 4 values for a single slice
+      return <double>[ img0, dst0, img1, dst1 ];
+    }
+
+    final double edge0Dim = imgC0 - img0;
+    final double edge1Dim = img1 - imgC1;
+    final double edgesDim = edge0Dim + edge1Dim;
+
+    if (edgesDim >= destDim) {
+      // the center portion has disappeared, leaving only the edges to scale to a common
+      // center position in the destination
+      // this produces only 2 slices which is 8 values
+      double dstC = dst0 + destDim * edge0Dim / edgesDim;
+      return <double>[
+        img0,  dst0, imgC0, dstC,
+        imgC1, dstC, img1,  dst1,
+      ];
+    }
+
+    // center portion is nonEmpty and only that part is scaled
+    // we need 3 slices which is 12 values
+    final double dstC0 = dst0 + edge0Dim;
+    final double dstC1 = dst1 - edge1Dim;
+    return <double>[
+      img0,  dst0,  imgC0, dstC0,
+      imgC0, dstC0, imgC1, dstC1,
+      imgC1, dstC1, img1,  dst1
+    ];
+  }
+
   @override
   void drawImageNine(
       ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) {
@@ -296,147 +337,44 @@
     assert(rectIsValid(dst));
     assert(paint != null); // ignore: unnecessary_null_comparison
 
-    // Assert you can fit the scaled part of the image (exluding the
-    // center source).
-    assert(image.width - center.width < dst.width);
-    assert(image.height - center.height < dst.height);
+    if (dst.isEmpty)
+      return;
 
-    // The four unscaled corner rectangles in the from the src.
-    final ui.Rect srcTopLeft = ui.Rect.fromLTWH(
-      0,
+    final List<double> hSlices = _initSlices(
       0,
       center.left,
-      center.top,
-    );
-    final ui.Rect srcTopRight = ui.Rect.fromLTWH(
       center.right,
-      0,
-      image.width - center.right,
-      center.top,
-    );
-    final ui.Rect srcBottomLeft = ui.Rect.fromLTWH(
-      0,
-      center.bottom,
-      center.left,
-      image.height - center.bottom,
-    );
-    final ui.Rect srcBottomRight = ui.Rect.fromLTWH(
-      center.right,
-      center.bottom,
-      image.width - center.right,
-      image.height - center.bottom,
-    );
-
-    final ui.Rect dstTopLeft = srcTopLeft.shift(dst.topLeft);
-
-    // The center rectangle in the dst region
-    final ui.Rect dstCenter = ui.Rect.fromLTWH(
-      dstTopLeft.right,
-      dstTopLeft.bottom,
-      dst.width - (srcTopLeft.width + srcTopRight.width),
-      dst.height - (srcTopLeft.height + srcBottomLeft.height),
-    );
-
-    drawImageRect(image, srcTopLeft, dstTopLeft, paint);
-
-    final ui.Rect dstTopRight = ui.Rect.fromLTWH(
-      dstCenter.right,
-      dst.top,
-      srcTopRight.width,
-      srcTopRight.height,
-    );
-    drawImageRect(image, srcTopRight, dstTopRight, paint);
-
-    final ui.Rect dstBottomLeft = ui.Rect.fromLTWH(
+      image.width.toDouble(),
       dst.left,
-      dstCenter.bottom,
-      srcBottomLeft.width,
-      srcBottomLeft.height,
+      dst.right,
     );
-    drawImageRect(image, srcBottomLeft, dstBottomLeft, paint);
-
-    final ui.Rect dstBottomRight = ui.Rect.fromLTWH(
-      dstCenter.right,
-      dstCenter.bottom,
-      srcBottomRight.width,
-      srcBottomRight.height,
-    );
-    drawImageRect(image, srcBottomRight, dstBottomRight, paint);
-
-    // Draw the top center rectangle.
-    drawImageRect(
-      image,
-      ui.Rect.fromLTRB(
-        srcTopLeft.right,
-        srcTopLeft.top,
-        srcTopRight.left,
-        srcTopRight.bottom,
-      ),
-      ui.Rect.fromLTRB(
-        dstTopLeft.right,
-        dstTopLeft.top,
-        dstTopRight.left,
-        dstTopRight.bottom,
-      ),
-      paint,
+    final List<double> vSlices = _initSlices(
+      0,
+      center.top,
+      center.bottom,
+      image.height.toDouble(),
+      dst.top,
+      dst.bottom,
     );
 
-    // Draw the middle left rectangle.
-    drawImageRect(
-      image,
-      ui.Rect.fromLTRB(
-        srcTopLeft.left,
-        srcTopLeft.bottom,
-        srcBottomLeft.right,
-        srcBottomLeft.top,
-      ),
-      ui.Rect.fromLTRB(
-        dstTopLeft.left,
-        dstTopLeft.bottom,
-        dstBottomLeft.right,
-        dstBottomLeft.top,
-      ),
-      paint,
-    );
-
-    // Draw the center rectangle.
-    drawImageRect(image, center, dstCenter, paint);
-
-    // Draw the middle right rectangle.
-    drawImageRect(
-      image,
-      ui.Rect.fromLTRB(
-        srcTopRight.left,
-        srcTopRight.bottom,
-        srcBottomRight.right,
-        srcBottomRight.top,
-      ),
-      ui.Rect.fromLTRB(
-        dstTopRight.left,
-        dstTopRight.bottom,
-        dstBottomRight.right,
-        dstBottomRight.top,
-      ),
-      paint,
-    );
-
-    // Draw the bottom center rectangle.
-    drawImageRect(
-      image,
-      ui.Rect.fromLTRB(
-        srcBottomLeft.right,
-        srcBottomLeft.top,
-        srcBottomRight.left,
-        srcBottomRight.bottom,
-      ),
-      ui.Rect.fromLTRB(
-        dstBottomLeft.right,
-        dstBottomLeft.top,
-        dstBottomRight.left,
-        dstBottomRight.bottom,
-      ),
-      paint,
-    );
+    for (int yi = 0; yi < vSlices.length; yi += 4) {
+      double srcY0 = vSlices[yi];
+      double dstY0 = vSlices[yi + 1];
+      double srcY1 = vSlices[yi + 2];
+      double dstY1 = vSlices[yi + 3];
+      for (int xi = 0; xi < hSlices.length; xi += 4) {
+        double srcX0 = hSlices[xi];
+        double dstX0 = hSlices[xi + 1];
+        double srcX1 = hSlices[xi + 2];
+        double dstX1 = hSlices[xi + 3];
+        drawImageRect(
+          image,
+          ui.Rect.fromLTRB(srcX0, srcY0, srcX1, srcY1),
+          ui.Rect.fromLTRB(dstX0, dstY0, dstX1, dstY1),
+          paint,
+        );
+      }
+    }
   }
 
   @override
diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart
index 2137367..9075089 100644
--- a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart
+++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart
@@ -398,6 +398,39 @@
     }
   });
 
+  // Regression test for https://github.com/flutter/flutter/issues/78068
+  // Tests for correct behavior when using drawImageNine with a destination
+  // size that is too small to render the center portion of the original image.
+  test('Paints nine slice image', () async {
+    Rect region = const Rect.fromLTWH(0, 0, 100, 100);
+    EnginePictureRecorder recorder = EnginePictureRecorder();
+    final Canvas canvas = Canvas(recorder, region);
+    Image testImage = createNineSliceImage();
+    canvas.clipRect(Rect.fromLTWH(0, 0, 100, 100));
+    // The testImage is 60x60 and the center slice is 20x20 so the edges
+    // of the image are 40x40. Drawing into a destination that is smaller
+    // than that will not provide enough room to draw the center portion.
+    canvas.drawImageNine(testImage, Rect.fromLTWH(20, 20, 20, 20),
+        Rect.fromLTWH(20, 20, 36, 36), Paint());
+    Picture picture = recorder.endRecording();
+
+    final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
+    builder.addPicture(Offset(0, 0), picture);
+
+    // Wrap in <flt-scene> so that our CSS selectors kick in.
+    final html.Element sceneElement = html.Element.tag('flt-scene');
+    try {
+      sceneElement.append(builder.build().webOnlyRootElement);
+      html.document.body.append(sceneElement);
+      await matchGoldenFile('draw_nine_slice_empty_center.png',
+          region: region, maxDiffRatePercent: 0);
+    } finally {
+      // The page is reused across tests, so remove the element after taking the
+      // Scuba screenshot.
+      sceneElement.remove();
+    }
+  });
+
   // Regression test for https://github.com/flutter/flutter/issues/61691
   //
   // The bug in bitmap_canvas.dart was that when we transformed and clipped