[web] Fix repaint logic for cullrect,transform changes (#22273)

diff --git a/lib/web_ui/lib/src/engine/html/picture.dart b/lib/web_ui/lib/src/engine/html/picture.dart
index a6d1867..6915010 100644
--- a/lib/web_ui/lib/src/engine/html/picture.dart
+++ b/lib/web_ui/lib/src/engine/html/picture.dart
@@ -91,6 +91,10 @@
   final ui.Rect? localPaintBounds;
   final int hints;
   double _density = 1.0;
+  /// Cull rect changes and density changes due to transforms should
+  /// call applyPaint for picture when retain() or update() is called after
+  /// preroll is complete.
+  bool _requiresRepaint = false;
 
   /// Cache for reusing elements such as images across picture updates.
   CrossFrameCache<html.HtmlElement>? _elementCache =
@@ -114,16 +118,7 @@
         ? 1.0 : _computePixelDensity(_transform, paintWidth, paintHeight);
     if (newDensity != _density) {
       _density = newDensity;
-      if (_canvas != null) {
-        // If cull rect and density hasn't changed, this will only repaint.
-        // If density doesn't match canvas, a new canvas will be created
-        // and paint queued.
-        //
-        // Similar to preroll for transform where transform is updated, for
-        // picture this means we need to repaint so pixelation doesn't occur
-        // due to transform changing overall dpi.
-        applyPaint(_canvas);
-      }
+      _requiresRepaint = true;
     }
     _computeExactCullRects();
   }
@@ -204,13 +199,14 @@
     }
   }
 
-  bool _computeOptimalCullRect(PersistedPicture? oldSurface) {
+  void _computeOptimalCullRect(PersistedPicture? oldSurface) {
     assert(_exactLocalCullRect != null);
 
     if (oldSurface == null || !oldSurface.picture.recordingCanvas!.didDraw) {
       // First useful paint.
       _optimalLocalCullRect = _exactLocalCullRect;
-      return true;
+      _requiresRepaint = true;
+      return;
     }
 
     assert(oldSurface._optimalLocalCullRect != null);
@@ -224,7 +220,10 @@
       // The clip collapsed into a zero-sized rectangle. If it was already zero,
       // no need to signal cull rect change.
       _optimalLocalCullRect = ui.Rect.zero;
-      return oldOptimalLocalCullRect != ui.Rect.zero;
+      if (oldOptimalLocalCullRect != ui.Rect.zero) {
+        _requiresRepaint = true;
+      }
+      return;
     }
 
     if (rectContainsOther(oldOptimalLocalCullRect!, _exactLocalCullRect!)) {
@@ -233,7 +232,7 @@
       // a clip when it is scrolled out of the screen. In this case we do not
       // repaint the picture. We just let it be shrunk by the outer clip.
       _optimalLocalCullRect = oldOptimalLocalCullRect;
-      return false;
+      return;
     }
 
     // The new cull rect contains area not covered by a previous rect. Perhaps
@@ -270,9 +269,8 @@
           _predictTrend(bottomwardDelta, _exactLocalCullRect!.height),
     ).intersect(localPaintBounds!);
 
-    final bool localCullRectChanged = _optimalLocalCullRect != newLocalCullRect;
+    _requiresRepaint = _optimalLocalCullRect != newLocalCullRect;
     _optimalLocalCullRect = newLocalCullRect;
-    return localCullRectChanged;
   }
 
   /// Predicts the delta a particular side of a clip rect will move given the
@@ -309,6 +307,7 @@
 
   void _applyPaint(PersistedPicture? oldSurface) {
     final EngineCanvas? oldCanvas = oldSurface?._canvas;
+    _requiresRepaint = false;
     if (!picture.recordingCanvas!.didDraw || _optimalLocalCullRect!.isEmpty) {
       // The picture is empty, or it has been completely clipped out. Skip
       // painting. This removes all the setup work and scaffolding objects
@@ -540,6 +539,7 @@
   @override
   void build() {
     _computeOptimalCullRect(null);
+    _requiresRepaint = true;
     super.build();
   }
 
@@ -556,15 +556,14 @@
       _applyTranslate();
     }
 
-    final bool cullRectChangeRequiresRepaint =
-        _computeOptimalCullRect(oldSurface);
+    _computeOptimalCullRect(oldSurface);
     if (identical(picture, oldSurface.picture)) {
       bool densityChanged =
           (_canvas is BitmapCanvas &&
               _density != (_canvas as BitmapCanvas)._density);
 
       // The picture is the same. Attempt to avoid repaint.
-      if (cullRectChangeRequiresRepaint || densityChanged) {
+      if (_requiresRepaint || densityChanged) {
         // Cull rect changed such that a repaint is still necessary.
         _applyPaint(oldSurface);
       } else {
@@ -581,8 +580,8 @@
   @override
   void retain() {
     super.retain();
-    final bool cullRectChangeRequiresRepaint = _computeOptimalCullRect(this);
-    if (cullRectChangeRequiresRepaint) {
+    _computeOptimalCullRect(this);
+    if (_requiresRepaint) {
       _applyPaint(this);
     }
   }