[Impeller] blur: removed ability to request out of bounds mip_counts (#50290)

b/323402168

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc
index ca5ba3c..c8c5a47 100644
--- a/impeller/aiks/aiks_unittests.cc
+++ b/impeller/aiks/aiks_unittests.cc
@@ -3968,5 +3968,61 @@
   }
 }
 
+// This addresses a bug where tiny blurs could result in mip maps that beyond
+// the limits for the textures used for blurring.
+// See also: b/323402168
+TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
+  for (int32_t i = 1; i < 5; ++i) {
+    Canvas canvas;
+    Scalar fi = i;
+    canvas.DrawPath(
+        PathBuilder{}
+            .MoveTo({100, 100})
+            .LineTo({100.f + fi, 100.f + fi})
+            .TakePath(),
+        {.color = Color::Chartreuse(),
+         .image_filter = ImageFilter::MakeBlur(
+             Sigma(0.1), Sigma(0.1), FilterContents::BlurStyle::kNormal,
+             Entity::TileMode::kClamp)});
+
+    Picture picture = canvas.EndRecordingAsPicture();
+    std::shared_ptr<RenderTargetCache> cache =
+        std::make_shared<RenderTargetCache>(
+            GetContext()->GetResourceAllocator());
+    AiksContext aiks_context(GetContext(), nullptr, cache);
+    std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
+    EXPECT_TRUE(image) << " length " << i;
+  }
+}
+
+// This addresses a bug where tiny blurs could result in mip maps that beyond
+// the limits for the textures used for blurring.
+// See also: b/323402168
+TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
+  for (int32_t i = 0; i < 5; ++i) {
+    Canvas canvas;
+    ISize clip_size = ISize(i, i);
+    canvas.ClipRect(
+        Rect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
+    canvas.DrawCircle(
+        {400, 400}, 200,
+        {
+            .color = Color::Green(),
+            .image_filter = ImageFilter::MakeBlur(
+                Sigma(0.1), Sigma(0.1), FilterContents::BlurStyle::kNormal,
+                Entity::TileMode::kDecal),
+        });
+    canvas.Restore();
+
+    Picture picture = canvas.EndRecordingAsPicture();
+    std::shared_ptr<RenderTargetCache> cache =
+        std::make_shared<RenderTargetCache>(
+            GetContext()->GetResourceAllocator());
+    AiksContext aiks_context(GetContext(), nullptr, cache);
+    std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
+    EXPECT_TRUE(image) << " clip rect " << i;
+  }
+}
+
 }  // namespace testing
 }  // namespace impeller
diff --git a/impeller/core/allocator.cc b/impeller/core/allocator.cc
index d9cedbe..19314c5 100644
--- a/impeller/core/allocator.cc
+++ b/impeller/core/allocator.cc
@@ -55,6 +55,14 @@
     return nullptr;
   }
 
+  if (desc.mip_count > desc.size.MipCount()) {
+    VALIDATION_LOG << "Requested mip_count " << desc.mip_count
+                   << " exceeds maximum supported for size " << desc.size;
+    TextureDescriptor corrected_desc = desc;
+    corrected_desc.mip_count = desc.size.MipCount();
+    return OnCreateTexture(corrected_desc);
+  }
+
   return OnCreateTexture(desc);
 }
 
diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc
index bbef829..b8bfcc3 100644
--- a/impeller/entity/contents/contents.cc
+++ b/impeller/entity/contents/contents.cc
@@ -80,8 +80,9 @@
     }
   }
 
+  ISize subpass_size = ISize::Ceil(coverage->GetSize());
   fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
-      label, ISize::Ceil(coverage->GetSize()),
+      label, subpass_size,
       [&contents = *this, &entity, &coverage](const ContentContext& renderer,
                                               RenderPass& pass) -> bool {
         Entity sub_entity;
@@ -91,7 +92,8 @@
             entity.GetTransform());
         return contents.Render(renderer, sub_entity, pass);
       },
-      msaa_enabled, mip_count);
+      msaa_enabled,
+      std::min(mip_count, static_cast<int32_t>(subpass_size.MipCount())));
 
   if (!render_target.ok()) {
     return std::nullopt;