Hoist/fold GraphicsContext::DrawTiledImage (background-image version)

Folds the "background-image version" of GraphicsContext::DrawTiledImage
into BoxPainter(Base). Mostly a verbatim move of said method and
Image::DrawTiledBackground (the real "meat") an the associated helpers.

This also introduces the intended replacement (for versions of this
method): GraphicsContext::DrawImageTiled.

Calls to StartAnimation are moved into the implementations of
Image::DrawPattern as needed.

Bug: 614125
Change-Id: Ib04daa5abce9c61c12f4a8297920ea25760557cf
Reviewed-on: https://chromium-review.googlesource.com/c/1448458
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Stephen Chenney <schenney@chromium.org>
Cr-Commit-Position: refs/heads/master@{#628136}
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index f84f3cfc..e503a8b 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -309,6 +309,112 @@
 
 namespace {
 
+// Given the |size| that the whole image should draw at, and the input phase
+// requested by the content, and the space between repeated tiles, return a
+// rectangle with |size| and a location that respects the phase but is no more
+// than one size + space in magnitude. In practice, this means that if there is
+// no repeating the returned rect would contain the destination_offset
+// location. The destination_offset passed here must exactly match the location
+// of the subset in a following call to ComputeSubsetForBackground.
+FloatRect ComputePhaseForBackground(const FloatPoint& destination_offset,
+                                    const FloatSize& size,
+                                    const FloatPoint& phase,
+                                    const FloatSize& spacing) {
+  const FloatSize step_per_tile(size + spacing);
+  return FloatRect(
+      FloatPoint(
+          destination_offset.X() + fmodf(-phase.X(), step_per_tile.Width()),
+          destination_offset.Y() + fmodf(-phase.Y(), step_per_tile.Height())),
+      size);
+}
+
+// Compute the image subset, in intrinsic image coordinates, that gets mapped
+// onto the |subset|, when the whole image would be drawn with phase and size
+// given by |phase_and_size|. Assumes |phase_and_size| contains |subset|. The
+// location of the requested subset should be the painting snapped location, or
+// whatever was used as a destination_offset in ComputePhaseForBackground.
+//
+// It is used to undo the offset added in ComputePhaseForBackground. The size
+// of requested subset should be the unsnapped size so that the computed
+// scale and location in the source image can be correctly determined.
+FloatRect ComputeSubsetForBackground(const FloatRect& phase_and_size,
+                                     const FloatRect& subset,
+                                     const FloatSize& intrinsic_size) {
+  // TODO(schenney): Re-enable this after determining why it fails for
+  // CAP, and maybe other cases.
+  // DCHECK(phase_and_size.Contains(subset));
+
+  const FloatSize scale(phase_and_size.Width() / intrinsic_size.Width(),
+                        phase_and_size.Height() / intrinsic_size.Height());
+  return FloatRect((subset.X() - phase_and_size.X()) / scale.Width(),
+                   (subset.Y() - phase_and_size.Y()) / scale.Height(),
+                   subset.Width() / scale.Width(),
+                   subset.Height() / scale.Height());
+}
+
+// The unsnapped_subset_size should be the target painting area implied by the
+//   content, without any snapping applied. It is necessary to correctly
+//   compute the subset of the source image to paint into the destination.
+// The snapped_paint_rect should be the target destination for painting into.
+// The phase is never snapped.
+// The tile_size is the total image size. The mapping from this size
+//   to the unsnapped_dest_rect size defines the scaling of the image for
+//   sprite computation.
+void DrawTiledBackground(GraphicsContext& context,
+                         Image* image,
+                         const FloatSize& unsnapped_subset_size,
+                         const FloatRect& snapped_paint_rect,
+                         const FloatPoint& phase,
+                         const FloatSize& tile_size,
+                         SkBlendMode op,
+                         const FloatSize& repeat_spacing) {
+  DCHECK(!tile_size.IsEmpty());
+
+  // Use the intrinsic size of the image if it has one, otherwise force the
+  // generated image to be the tile size.
+  FloatSize intrinsic_tile_size(image->Size());
+  FloatSize scale(1, 1);
+  if (image->HasRelativeSize()) {
+    intrinsic_tile_size = tile_size;
+  } else {
+    scale = FloatSize(tile_size.Width() / intrinsic_tile_size.Width(),
+                      tile_size.Height() / intrinsic_tile_size.Height());
+  }
+
+  const FloatRect one_tile_rect = ComputePhaseForBackground(
+      snapped_paint_rect.Location(), tile_size, phase, repeat_spacing);
+
+  // Check and see if a single draw of the image can cover the entire area we
+  // are supposed to tile. The dest_rect_for_subset must use the same
+  // location that was used in ComputePhaseForBackground and the unsnapped
+  // destination rect in order to correctly evaluate the subset size and
+  // location in the presence of border snapping and zoom.
+  FloatRect dest_rect_for_subset(snapped_paint_rect.Location(),
+                                 unsnapped_subset_size);
+  if (one_tile_rect.Contains(dest_rect_for_subset)) {
+    FloatRect visible_src_rect = ComputeSubsetForBackground(
+        one_tile_rect, dest_rect_for_subset, intrinsic_tile_size);
+    // Round to avoid filtering pulling in neighboring pixels, for the
+    // common case of sprite maps.
+    // TODO(schenney): Snapping at this level is a problem for cases where we
+    // might be animating background-position to pan over an image. Ideally we
+    // would either snap only if close to integral, or move snapping
+    // calculations up the stack.
+    visible_src_rect = FloatRect(RoundedIntRect(visible_src_rect));
+    context.DrawImage(image, Image::kSyncDecode, snapped_paint_rect,
+                      &visible_src_rect, op, kDoNotRespectImageOrientation);
+    return;
+  }
+
+  // Note that this tile rect the image's pre-scaled size.
+  FloatRect tile_rect(FloatPoint(), intrinsic_tile_size);
+  // This call takes the unscaled image, applies the given scale, and paints
+  // it into the snapped_dest_rect using phase from one_tile_rect and the
+  // given repeat spacing. Note the phase is already scaled.
+  context.DrawImageTiled(image, snapped_paint_rect, tile_rect, scale,
+                         one_tile_rect.Location(), repeat_spacing, op);
+}
+
 inline bool PaintFastBottomLayer(Node* node,
                                  const PaintInfo& paint_info,
                                  const BoxPainterBase::FillLayerInfo& info,
@@ -359,7 +465,7 @@
 
       // Phase calculation uses the actual painted location, given by the
       // border-snapped destination rect.
-      image_tile = Image::ComputePhaseForBackground(
+      image_tile = ComputePhaseForBackground(
           FloatPoint(geometry.SnappedDestRect().Location()),
           FloatSize(geometry.TileSize()), geometry.Phase(),
           FloatSize(geometry.SpaceSize()));
@@ -417,7 +523,7 @@
   // so snap to integers. This is particuarly important for sprite maps.
   // Calculation up to this point, in LayoutUnits, can lead to small variations
   // from integer size, so it is safe to round without introducing major issues.
-  const FloatRect unrounded_subset = Image::ComputeSubsetForBackground(
+  const FloatRect unrounded_subset = ComputeSubsetForBackground(
       image_tile, dest_rect_for_subset, intrinsic_tile_size);
   FloatRect src_rect = FloatRect(RoundedIntRect(unrounded_subset));
 
@@ -541,17 +647,17 @@
   // NOTE: This method can be called with no image in situations when a bad
   // resource locator is given such as "//:0", so still check for image.
   if (info.should_paint_image && !geometry.SnappedDestRect().IsEmpty() &&
-      image) {
+      !geometry.TileSize().IsEmpty() && image) {
     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
                  "data",
                  inspector_paint_image_event::Data(
                      node, *info.image, FloatRect(image->Rect()),
                      FloatRect(scrolled_paint_rect)));
-    context.DrawTiledImage(image,
-                           FloatSize(geometry.UnsnappedDestRect().Size()),
-                           FloatRect(geometry.SnappedDestRect()),
-                           geometry.Phase(), FloatSize(geometry.TileSize()),
-                           composite_op, FloatSize(geometry.SpaceSize()));
+    DrawTiledBackground(context, image,
+                        FloatSize(geometry.UnsnappedDestRect().Size()),
+                        FloatRect(geometry.SnappedDestRect()), geometry.Phase(),
+                        FloatSize(geometry.TileSize()), composite_op,
+                        FloatSize(geometry.SpaceSize()));
   }
 }
 
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 2eecda79..2293a21 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -384,6 +384,8 @@
   flags.setBlendMode(composite_op);
   flags.setColorFilter(sk_ref_sp(context.GetColorFilter()));
   context.DrawRect(dst_rect, flags);
+
+  StartAnimation();
 }
 
 sk_sp<PaintRecord> SVGImage::PaintRecordForContainer(
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index cc494d2..197174a0 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -995,17 +995,17 @@
       std::min(resampling, ImageInterpolationQuality()));
 }
 
-void GraphicsContext::DrawTiledImage(Image* image,
-                                     const FloatSize& unsnapped_subset_size,
-                                     const FloatRect& snapped_paint_rect,
-                                     const FloatPoint& unsnapped_phase,
-                                     const FloatSize& tile_size,
-                                     SkBlendMode op,
-                                     const FloatSize& repeat_spacing) {
+void GraphicsContext::DrawImageTiled(Image* image,
+                                     const FloatRect& dest_rect,
+                                     const FloatRect& src_rect,
+                                     const FloatSize& scale_src_to_dest,
+                                     const FloatPoint& phase,
+                                     const FloatSize& repeat_spacing,
+                                     SkBlendMode op) {
   if (ContextDisabled() || !image)
     return;
-  image->DrawTiledBackground(*this, unsnapped_subset_size, snapped_paint_rect,
-                             unsnapped_phase, tile_size, op, repeat_spacing);
+  image->DrawPattern(*this, src_rect, scale_src_to_dest, phase, op, dest_rect,
+                     repeat_spacing);
   paint_controller_.SetImagePainted();
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index f46176a4..a8dcb080 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -223,14 +223,13 @@
       const FloatRect& src_rect,
       SkBlendMode = SkBlendMode::kSrcOver,
       RespectImageOrientationEnum = kDoNotRespectImageOrientation);
-  // Used for background image
-  void DrawTiledImage(Image*,
-                      const FloatSize& unsnapped_subset_size,
-                      const FloatRect& snapped_paint_rect,
-                      const FloatPoint& unsnapped_phase,
-                      const FloatSize& tile_size,
-                      SkBlendMode = SkBlendMode::kSrcOver,
-                      const FloatSize& repeat_spacing = FloatSize());
+  void DrawImageTiled(Image* image,
+                      const FloatRect& dest_rect,
+                      const FloatRect& src_rect,
+                      const FloatSize& scale_src_to_dest,
+                      const FloatPoint& phase,
+                      const FloatSize& repeat_spacing,
+                      SkBlendMode = SkBlendMode::kSrcOver);
   // Used for border image
   void DrawTiledImage(Image*,
                       const FloatRect& dest_rect,
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index efa5fa09..dba1d08 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -141,66 +141,6 @@
 }
 
 // TODO(schenney): Lift this code, with the calculations for subsetting the
-// image and the like, up the stack into a BackgroundPainter.
-void Image::DrawTiledBackground(GraphicsContext& ctxt,
-                                const FloatSize& unsnapped_subset_size,
-                                const FloatRect& snapped_paint_rect,
-                                const FloatPoint& phase,
-                                const FloatSize& tile_size,
-                                SkBlendMode op,
-                                const FloatSize& repeat_spacing) {
-  if (tile_size.IsEmpty())
-    return;
-
-  // Use the intrinsic size of the image if it has one, otherwise force the
-  // generated image to be the tile size.
-  FloatSize intrinsic_tile_size(Size());
-  FloatSize scale(1, 1);
-  if (HasRelativeSize()) {
-    intrinsic_tile_size.SetWidth(tile_size.Width());
-    intrinsic_tile_size.SetHeight(tile_size.Height());
-  } else {
-    scale = FloatSize(tile_size.Width() / intrinsic_tile_size.Width(),
-                      tile_size.Height() / intrinsic_tile_size.Height());
-  }
-
-  const FloatRect one_tile_rect = ComputePhaseForBackground(
-      snapped_paint_rect.Location(), tile_size, phase, repeat_spacing);
-
-  // Check and see if a single draw of the image can cover the entire area we
-  // are supposed to tile. The dest_rect_for_subset must use the same
-  // location that was used in ComputePhaseForBackground and the unsnapped
-  // destination rect in order to correctly evaluate the subset size and
-  // location in the presence of border snapping and zoom.
-  FloatRect dest_rect_for_subset(snapped_paint_rect.Location(),
-                                 unsnapped_subset_size);
-  if (one_tile_rect.Contains(dest_rect_for_subset)) {
-    FloatRect visible_src_rect = ComputeSubsetForBackground(
-        one_tile_rect, dest_rect_for_subset, intrinsic_tile_size);
-    // Round to avoid filtering pulling in neighboring pixels, for the
-    // common case of sprite maps.
-    // TODO(schenney): Snapping at this level is a problem for cases where we
-    // might be animating background-position to pan over an image. Ideally we
-    // would either snap only if close to integral, or move snapping
-    // calculations up the stack.
-    visible_src_rect = FloatRect(RoundedIntRect(visible_src_rect));
-    ctxt.DrawImage(this, kSyncDecode, snapped_paint_rect, &visible_src_rect, op,
-                   kDoNotRespectImageOrientation);
-    return;
-  }
-
-  // Note that this tile rect the image's pre-scaled size.
-  FloatRect tile_rect(FloatPoint(), intrinsic_tile_size);
-  // This call takes the unscaled image, applies the given scale, and paints
-  // it into the snapped_dest_rect using phase from one_tile_rect and the
-  // given repeat spacing. Note the phase is already scaled.
-  DrawPattern(ctxt, tile_rect, scale, one_tile_rect.Location(), op,
-              snapped_paint_rect, repeat_spacing);
-
-  StartAnimation();
-}
-
-// TODO(schenney): Lift this code, with the calculations for subsetting the
 // image and the like, up the stack into a border painting class.
 void Image::DrawTiledBorder(GraphicsContext& ctxt,
                             const FloatRect& dst_rect,
@@ -295,8 +235,6 @@
     DrawPattern(ctxt, src_rect, tile_scale_factor, pattern_phase, op, dst_rect,
                 spacing);
   }
-
-  StartAnimation();
 }
 
 namespace {
@@ -412,6 +350,8 @@
 
   context.DrawRect(dest_rect, flags);
 
+  StartAnimation();
+
   if (CurrentFrameIsLazyDecoded()) {
     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                          "Draw LazyPixelRef", TRACE_EVENT_SCOPE_THREAD,
@@ -454,33 +394,6 @@
   return true;
 }
 
-FloatRect Image::ComputePhaseForBackground(const FloatPoint& destination_offset,
-                                           const FloatSize& size,
-                                           const FloatPoint& phase,
-                                           const FloatSize& spacing) {
-  const FloatSize step_per_tile(size + spacing);
-  return FloatRect(
-      FloatPoint(
-          destination_offset.X() + fmodf(-phase.X(), step_per_tile.Width()),
-          destination_offset.Y() + fmodf(-phase.Y(), step_per_tile.Height())),
-      size);
-}
-
-FloatRect Image::ComputeSubsetForBackground(const FloatRect& phase_and_size,
-                                            const FloatRect& subset,
-                                            const FloatSize& intrinsic_size) {
-  // TODO(schenney): Re-enable this after determining why it fails for
-  // CAP, and maybe other cases.
-  // DCHECK(phase_and_size.Contains(subset));
-
-  const FloatSize scale(phase_and_size.Width() / intrinsic_size.Width(),
-                        phase_and_size.Height() / intrinsic_size.Height());
-  return FloatRect((subset.X() - phase_and_size.X()) / scale.Width(),
-                   (subset.Y() - phase_and_size.Y()) / scale.Height(),
-                   subset.Width() / scale.Width(),
-                   subset.Height() / scale.Height());
-}
-
 SkBitmap Image::AsSkBitmapForCurrentFrame(
     RespectImageOrientationEnum should_respect_image_orientation) {
   PaintImage paint_image = PaintImageForCurrentFrame();
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index 490c63f..a6e42e8 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -223,33 +223,6 @@
     return nullptr;
   }
 
-  // Given the |size| that the whole image should draw at, and the
-  // input phase requested by the content, and the space between repeated tiles,
-  // return a rectangle with |size| and a location that respects
-  // the phase but is no more than one size + space in magnitude. In practice,
-  // this means that if there is no repeating the returned rect would contain
-  // the destination_offset location. The destination_offset passed here must
-  // exactly match the location of the subset in a following call to
-  // ComputeSubsetForBackground.
-  static FloatRect ComputePhaseForBackground(
-      const FloatPoint& destination_offset,
-      const FloatSize& size,
-      const FloatPoint& phase,
-      const FloatSize& spacing);
-
-  // Compute the image subset, in intrinsic image coordinates, that gets mapped
-  // onto the |subset|, when the whole image would be drawn with phase
-  // and size given by |phase_and_size|. Assumes
-  // |phase_and_size| contains |subset|. The location
-  // of the requested subset should be the painting snapped location, or
-  // whatever was used as a destination_offset in ComputePhaseForBackground.
-  // It is used to undo the offset added in ComputePhaseForBackground. The size
-  // of requested subset should be the unsnapped size so that the computed
-  // scale and location in the source image can be correctly determined.
-  static FloatRect ComputeSubsetForBackground(const FloatRect& phase_and_size,
-                                              const FloatRect& subset,
-                                              const FloatSize& intrinsic_size);
-
   virtual sk_sp<PaintRecord> PaintRecordForContainer(
       const KURL& url,
       const IntSize& container_size,
@@ -278,22 +251,6 @@
  protected:
   Image(ImageObserver* = nullptr, bool is_multipart = false);
 
-  // The unsnapped_subset_size should be the target painting area implied by the
-  //   content, without any snapping applied. It is necessary to correctly
-  //   compute the subset of the source image to paint into the destination.
-  // The snapped_paint_rect should be the target destination for painting into.
-  // The phase is never snapped.
-  // The tile_size is the total image size. The mapping from this size
-  //   to the unsnapped_dest_rect size defines the scaling of the image for
-  //   sprite computation.
-  void DrawTiledBackground(GraphicsContext&,
-                           const FloatSize& unsnapped_subset_size,
-                           const FloatRect& snapped_paint_rect,
-                           const FloatPoint& phase,
-                           const FloatSize& tile_size,
-                           SkBlendMode,
-                           const FloatSize& repeat_spacing);
-
   void DrawTiledBorder(GraphicsContext&,
                        const FloatRect& dst_rect,
                        const FloatRect& src_rect,