Revert "Revert "[Impeller] Correctly compute UVs in texture fill" (#43087)"

This reverts commit 218fae168360233ce8ad05910e97d78928f55a96.
diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc
index 019a676..b46d270 100644
--- a/impeller/aiks/paint_pass_delegate.cc
+++ b/impeller/aiks/paint_pass_delegate.cc
@@ -4,6 +4,8 @@
 
 #include "impeller/aiks/paint_pass_delegate.h"
 
+#include "impeller/core/formats.h"
+#include "impeller/core/sampler_descriptor.h"
 #include "impeller/entity/contents/contents.h"
 #include "impeller/entity/contents/texture_contents.h"
 #include "impeller/entity/entity_pass.h"
@@ -46,6 +48,13 @@
   contents->SetSourceRect(Rect::MakeSize(target->GetSize()));
   contents->SetOpacity(paint_.color.alpha);
   contents->SetDeferApplyingOpacity(true);
+
+  SamplerDescriptor sampler_desc;
+  sampler_desc.label = "Subpass";
+  sampler_desc.width_address_mode = SamplerAddressMode::kDecal;
+  sampler_desc.height_address_mode = SamplerAddressMode::kDecal;
+  contents->SetSamplerDescriptor(sampler_desc);
+
   return paint_.WithFiltersForSubpassTarget(std::move(contents),
                                             effect_transform);
 }
@@ -140,6 +149,13 @@
   contents->SetSourceRect(Rect::MakeSize(target->GetSize()));
   contents->SetOpacity(paint_.color.alpha);
   contents->SetDeferApplyingOpacity(true);
+
+  SamplerDescriptor sampler_desc;
+  sampler_desc.label = "Subpass";
+  sampler_desc.width_address_mode = SamplerAddressMode::kDecal;
+  sampler_desc.height_address_mode = SamplerAddressMode::kDecal;
+  contents->SetSamplerDescriptor(sampler_desc);
+
   return paint_.WithFiltersForSubpassTarget(std::move(contents),
                                             effect_transform);
 }
diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc
index 90b3e15..553d53c 100644
--- a/impeller/entity/contents/texture_contents.cc
+++ b/impeller/entity/contents/texture_contents.cc
@@ -26,7 +26,7 @@
 
 std::shared_ptr<TextureContents> TextureContents::MakeRect(Rect destination) {
   auto contents = std::make_shared<TextureContents>();
-  contents->rect_ = destination;
+  contents->destination_rect_ = destination;
   return contents;
 }
 
@@ -34,8 +34,8 @@
   label_ = std::move(label);
 }
 
-void TextureContents::SetRect(Rect rect) {
-  rect_ = rect;
+void TextureContents::SetDestinationRect(Rect rect) {
+  destination_rect_ = rect;
 }
 
 void TextureContents::SetTexture(std::shared_ptr<Texture> texture) {
@@ -70,7 +70,7 @@
   if (GetOpacity() == 0) {
     return std::nullopt;
   }
-  return rect_.TransformBounds(entity.GetTransformation());
+  return destination_rect_.TransformBounds(entity.GetTransformation());
 };
 
 std::optional<Snapshot> TextureContents::RenderToSnapshot(
@@ -82,7 +82,7 @@
     const std::string& label) const {
   // Passthrough textures that have simple rectangle paths and complete source
   // rects.
-  auto bounds = rect_;
+  auto bounds = destination_rect_;
   auto opacity = GetOpacity();
   if (source_rect_ == Rect::MakeSize(texture_->GetSize()) &&
       (opacity >= 1 - kEhCloseEnough || defer_applying_opacity_)) {
@@ -104,37 +104,30 @@
       label);                                            // label
 }
 
-static TextureFillVertexShader::PerVertexData ComputeVertexData(
-    const Point& position,
-    const Rect& coverage_rect,
-    const ISize& texture_size,
-    const Rect& source_rect) {
-  TextureFillVertexShader::PerVertexData data;
-  data.position = position;
-  auto coverage_coords = (position - coverage_rect.origin) / coverage_rect.size;
-  data.texture_coords =
-      (source_rect.origin + source_rect.size * coverage_coords) / texture_size;
-  return data;
-}
-
 bool TextureContents::Render(const ContentContext& renderer,
                              const Entity& entity,
                              RenderPass& pass) const {
   using VS = TextureFillVertexShader;
   using FS = TextureFillFragmentShader;
 
-  const auto coverage_rect = rect_;
-
-  if (coverage_rect.size.IsEmpty() || source_rect_.IsEmpty() ||
+  if (destination_rect_.size.IsEmpty() || source_rect_.IsEmpty() ||
       texture_ == nullptr || texture_->GetSize().IsEmpty()) {
-    return true;
+    return true;  // Nothing to render.
   }
 
+  // Expand the source rect by half a texel, which aligns sampled texels to the
+  // pixel grid if the source rect is the same size as the destination rect.
+  auto texture_coords =
+      Rect::MakeSize(texture_->GetSize()).Project(source_rect_.Expand(0.5));
+
   VertexBufferBuilder<VS::PerVertexData> vertex_builder;
-  for (const auto position : rect_.GetPoints()) {
-    vertex_builder.AppendVertex(ComputeVertexData(
-        position, coverage_rect, texture_->GetSize(), source_rect_));
-  }
+
+  vertex_builder.AddVertices({
+      {destination_rect_.GetLeftTop(), texture_coords.GetLeftTop()},
+      {destination_rect_.GetRightTop(), texture_coords.GetRightTop()},
+      {destination_rect_.GetLeftBottom(), texture_coords.GetLeftBottom()},
+      {destination_rect_.GetRightBottom(), texture_coords.GetRightBottom()},
+  });
 
   auto& host_buffer = pass.GetTransientsBuffer();
 
diff --git a/impeller/entity/contents/texture_contents.h b/impeller/entity/contents/texture_contents.h
index 567e95a..4da57c1 100644
--- a/impeller/entity/contents/texture_contents.h
+++ b/impeller/entity/contents/texture_contents.h
@@ -30,7 +30,7 @@
 
   void SetLabel(std::string label);
 
-  void SetRect(Rect rect);
+  void SetDestinationRect(Rect rect);
 
   void SetTexture(std::shared_ptr<Texture> texture);
 
@@ -78,7 +78,7 @@
  private:
   std::string label_;
 
-  Rect rect_;
+  Rect destination_rect_;
   bool stencil_enabled_ = true;
 
   std::shared_ptr<Texture> texture_;
diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc
index 65ceaf2..6c88a45 100644
--- a/impeller/entity/entity_unittests.cc
+++ b/impeller/entity/entity_unittests.cc
@@ -1069,7 +1069,7 @@
     if (selected_input_type == 0) {
       auto texture = std::make_shared<TextureContents>();
       texture->SetSourceRect(Rect::MakeSize(boston->GetSize()));
-      texture->SetRect(input_rect);
+      texture->SetDestinationRect(input_rect);
       texture->SetTexture(boston);
       texture->SetOpacity(input_color.alpha);
 
@@ -1192,7 +1192,7 @@
         Rect::MakeXYWH(path_rect[0], path_rect[1], path_rect[2], path_rect[3]);
     auto texture = std::make_shared<TextureContents>();
     texture->SetSourceRect(Rect::MakeSize(boston->GetSize()));
-    texture->SetRect(input_rect);
+    texture->SetDestinationRect(input_rect);
     texture->SetTexture(boston);
     texture->SetOpacity(input_color.alpha);
 
diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc
index 11a5706..3a123c6 100644
--- a/impeller/geometry/geometry_unittests.cc
+++ b/impeller/geometry/geometry_unittests.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "gtest/gtest.h"
 #include "impeller/geometry/geometry_asserts.h"
 
 #include <limits>
@@ -2142,6 +2143,20 @@
   }
 }
 
+TEST(GeometryTest, RectDirections) {
+  auto r = Rect::MakeLTRB(1, 2, 3, 4);
+
+  ASSERT_EQ(r.GetLeft(), 1);
+  ASSERT_EQ(r.GetTop(), 2);
+  ASSERT_EQ(r.GetRight(), 3);
+  ASSERT_EQ(r.GetBottom(), 4);
+
+  ASSERT_POINT_NEAR(r.GetLeftTop(), Point(1, 2));
+  ASSERT_POINT_NEAR(r.GetRightTop(), Point(3, 2));
+  ASSERT_POINT_NEAR(r.GetLeftBottom(), Point(1, 4));
+  ASSERT_POINT_NEAR(r.GetRightBottom(), Point(3, 4));
+}
+
 TEST(GeometryTest, RectProject) {
   {
     auto r = Rect::MakeLTRB(-100, -100, 100, 100);
diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h
index 7a2c18b..0bda614 100644
--- a/impeller/geometry/rect.h
+++ b/impeller/geometry/rect.h
@@ -162,6 +162,16 @@
     return std::max(origin.y, origin.y + size.height);
   }
 
+  constexpr TPoint<T> GetLeftTop() const { return {GetLeft(), GetTop()}; }
+
+  constexpr TPoint<T> GetRightTop() const { return {GetRight(), GetTop()}; }
+
+  constexpr TPoint<T> GetLeftBottom() const { return {GetLeft(), GetBottom()}; }
+
+  constexpr TPoint<T> GetRightBottom() const {
+    return {GetRight(), GetBottom()};
+  }
+
   constexpr std::array<T, 4> GetLTRB() const {
     return {GetLeft(), GetTop(), GetRight(), GetBottom()};
   }