Split tiling pattern rendering code into its own file.

Similar to the split for shading pattern rendering code in
pdfium-review.googlesource.com/61030, for another 12% reduction in
cpdf_renderstatus.cpp line count.

Change-Id: I9156c746b22b06e0053ca1a60ef33e9ea302bbee
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/70530
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/render/BUILD.gn b/core/fpdfapi/render/BUILD.gn
index 8fc9e53..515d300 100644
--- a/core/fpdfapi/render/BUILD.gn
+++ b/core/fpdfapi/render/BUILD.gn
@@ -33,6 +33,8 @@
     "cpdf_rendershading.h",
     "cpdf_renderstatus.cpp",
     "cpdf_renderstatus.h",
+    "cpdf_rendertiling.cpp",
+    "cpdf_rendertiling.h",
     "cpdf_scaledrenderbuffer.cpp",
     "cpdf_scaledrenderbuffer.h",
     "cpdf_textrenderer.cpp",
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 3375924..136e93e 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <cmath>
-#include <limits>
 #include <memory>
 #include <numeric>
 #include <set>
@@ -47,6 +46,7 @@
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_rendershading.h"
+#include "core/fpdfapi/render/cpdf_rendertiling.h"
 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
 #include "core/fpdfapi/render/cpdf_textrenderer.h"
 #include "core/fpdfapi/render/cpdf_type3cache.h"
@@ -61,7 +61,6 @@
 #include "core/fxge/renderdevicedriver_iface.h"
 #include "core/fxge/text_char_pos.h"
 #include "core/fxge/text_glyph_pos.h"
-#include "third_party/base/compiler_specific.h"
 #include "third_party/base/logging.h"
 #include "third_party/base/stl_util.h"
 
@@ -74,48 +73,6 @@
 constexpr int kRenderMaxRecursionDepth = 64;
 int g_CurrentRecursionDepth = 0;
 
-RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
-    CPDF_Document* pDoc,
-    CPDF_PageRenderCache* pCache,
-    CPDF_TilingPattern* pPattern,
-    CPDF_Form* pPatternForm,
-    const CFX_Matrix& mtObject2Device,
-    int width,
-    int height,
-    const CPDF_RenderOptions::Options& draw_options) {
-  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pBitmap->Create(width, height,
-                       pPattern->colored() ? FXDIB_Argb : FXDIB_8bppMask)) {
-    return nullptr;
-  }
-  CFX_DefaultRenderDevice bitmap_device;
-  bitmap_device.Attach(pBitmap, false, nullptr, false);
-  pBitmap->Clear(0);
-  CFX_FloatRect cell_bbox =
-      pPattern->pattern_to_form().TransformRect(pPattern->bbox());
-  cell_bbox = mtObject2Device.TransformRect(cell_bbox);
-  CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
-  CFX_Matrix mtAdjust;
-  mtAdjust.MatchRect(bitmap_rect, cell_bbox);
-
-  CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
-  CPDF_RenderOptions options;
-  if (!pPattern->colored())
-    options.SetColorMode(CPDF_RenderOptions::kAlpha);
-
-  options.GetOptions() = draw_options;
-  options.GetOptions().bForceHalftone = true;
-
-  CPDF_RenderContext context(pDoc, nullptr, pCache);
-  context.AppendLayer(pPatternForm, &mtPattern2Bitmap);
-  context.Render(&bitmap_device, &options, nullptr);
-#if defined _SKIA_SUPPORT_PATHS_
-  bitmap_device.Flush(true);
-  pBitmap->UnPreMultiply();
-#endif
-  return pBitmap;
-}
-
 int GetFillRenderOptionsHelper(const CPDF_RenderOptions::Options& options,
                                const CPDF_PathObject* path_obj,
                                int fill_type,
@@ -1211,175 +1168,12 @@
   if (clip_box.IsEmpty())
     return;
 
-  const CFX_Matrix mtPattern2Device =
-      pPattern->pattern_to_form() * mtObj2Device;
-
-  CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
-
-  float ceil_height = std::ceil(cell_bbox.Height());
-  float ceil_width = std::ceil(cell_bbox.Width());
-
-  // Validate the float will fit into the int when the conversion is done.
-  if (!pdfium::base::IsValueInRangeForNumericType<int>(ceil_height) ||
-      !pdfium::base::IsValueInRangeForNumericType<int>(ceil_width)) {
-    return;
-  }
-
-  int width = static_cast<int>(ceil_width);
-  int height = static_cast<int>(ceil_height);
-  if (width <= 0)
-    width = 1;
-  if (height <= 0)
-    height = 1;
-
-  CFX_FloatRect clip_box_p =
-      mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
-  int min_col = static_cast<int>(
-      ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
-  int max_col = static_cast<int>(
-      floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
-  int min_row = static_cast<int>(
-      ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
-  int max_row = static_cast<int>(
-      floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
-
-  // Make sure we can fit the needed width * height into an int.
-  if (height > std::numeric_limits<int>::max() / width)
+  RetainPtr<CFX_DIBitmap> pScreen =
+      CPDF_RenderTiling::Draw(this, pPageObj, pPattern, pPatternForm.get(),
+                              mtObj2Device, clip_box, bStroke);
+  if (!pScreen)
     return;
 
-  if (width > clip_box.Width() || height > clip_box.Height() ||
-      width * height > clip_box.Width() * clip_box.Height()) {
-    std::unique_ptr<CPDF_GraphicStates> pStates;
-    if (!pPattern->colored())
-      pStates = CloneObjStates(pPageObj, bStroke);
-
-    const CPDF_Dictionary* pFormDict = pPatternForm->GetDict();
-    const CPDF_Dictionary* pFormResource =
-        pFormDict ? pFormDict->GetDictFor("Resources") : nullptr;
-    for (int col = min_col; col <= max_col; col++) {
-      for (int row = min_row; row <= max_row; row++) {
-        CFX_PointF original = mtPattern2Device.Transform(
-            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
-        CFX_Matrix matrix = mtObj2Device;
-        matrix.Translate(original.x - mtPattern2Device.e,
-                         original.y - mtPattern2Device.f);
-        CFX_RenderDevice::StateRestorer restorer2(m_pDevice);
-        CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
-        status.SetOptions(m_Options);
-        status.SetTransparency(pPatternForm->GetTransparency());
-        status.SetFormResource(pFormResource);
-        status.SetDropObjects(m_bDropObjects);
-        status.Initialize(this, pStates.get());
-        status.RenderObjectList(pPatternForm.get(), matrix);
-      }
-    }
-    return;
-  }
-
-  bool bAligned =
-      pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
-      pPattern->bbox().right == pPattern->x_step() &&
-      pPattern->bbox().top == pPattern->y_step() &&
-      (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
-  if (bAligned) {
-    int orig_x = FXSYS_roundf(mtPattern2Device.e);
-    int orig_y = FXSYS_roundf(mtPattern2Device.f);
-    min_col = (clip_box.left - orig_x) / width;
-    if (clip_box.left < orig_x)
-      min_col--;
-
-    max_col = (clip_box.right - orig_x) / width;
-    if (clip_box.right <= orig_x)
-      max_col--;
-
-    min_row = (clip_box.top - orig_y) / height;
-    if (clip_box.top < orig_y)
-      min_row--;
-
-    max_row = (clip_box.bottom - orig_y) / height;
-    if (clip_box.bottom <= orig_y)
-      max_row--;
-  }
-  float left_offset = cell_bbox.left - mtPattern2Device.e;
-  float top_offset = cell_bbox.bottom - mtPattern2Device.f;
-  RetainPtr<CFX_DIBitmap> pPatternBitmap;
-  if (width * height < 16) {
-    RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
-        m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
-        pPatternForm.get(), mtObj2Device, 8, 8, m_Options.GetOptions());
-    pPatternBitmap = pEnlargedBitmap->StretchTo(
-        width, height, FXDIB_ResampleOptions(), nullptr);
-  } else {
-    pPatternBitmap =
-        DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(),
-                          pPattern, pPatternForm.get(), mtObj2Device, width,
-                          height, m_Options.GetOptions());
-  }
-  if (!pPatternBitmap)
-    return;
-
-  if (m_Options.ColorModeIs(CPDF_RenderOptions::kGray))
-    pPatternBitmap->ConvertColorScale(0, 0xffffff);
-
-  FX_ARGB fill_argb = GetFillArgb(pPageObj);
-  int clip_width = clip_box.right - clip_box.left;
-  int clip_height = clip_box.bottom - clip_box.top;
-  auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pScreen->Create(clip_width, clip_height, FXDIB_Argb))
-    return;
-
-  pScreen->Clear(0);
-  const uint8_t* const src_buf = pPatternBitmap->GetBuffer();
-  for (int col = min_col; col <= max_col; col++) {
-    for (int row = min_row; row <= max_row; row++) {
-      int start_x;
-      int start_y;
-      if (bAligned) {
-        start_x =
-            FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
-        start_y =
-            FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
-      } else {
-        CFX_PointF original = mtPattern2Device.Transform(
-            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
-
-        FX_SAFE_INT32 safeStartX = FXSYS_roundf(original.x + left_offset);
-        FX_SAFE_INT32 safeStartY = FXSYS_roundf(original.y + top_offset);
-
-        safeStartX -= clip_box.left;
-        safeStartY -= clip_box.top;
-        if (!safeStartX.IsValid() || !safeStartY.IsValid())
-          return;
-
-        start_x = safeStartX.ValueOrDie();
-        start_y = safeStartY.ValueOrDie();
-      }
-      if (width == 1 && height == 1) {
-        if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
-            start_y >= clip_box.Height()) {
-          continue;
-        }
-        uint32_t* dest_buf = reinterpret_cast<uint32_t*>(
-            pScreen->GetBuffer() + pScreen->GetPitch() * start_y + start_x * 4);
-        if (pPattern->colored()) {
-          const auto* src_buf32 = reinterpret_cast<const uint32_t*>(src_buf);
-          *dest_buf = *src_buf32;
-        } else {
-          *dest_buf = (*src_buf << 24) | (fill_argb & 0xffffff);
-        }
-      } else {
-        if (pPattern->colored()) {
-          pScreen->CompositeBitmap(start_x, start_y, width, height,
-                                   pPatternBitmap, 0, 0, BlendMode::kNormal,
-                                   nullptr, false);
-        } else {
-          pScreen->CompositeMask(start_x, start_y, width, height,
-                                 pPatternBitmap, fill_argb, 0, 0,
-                                 BlendMode::kNormal, nullptr, false);
-        }
-      }
-    }
-  }
   CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, 0, 255,
                     BlendMode::kNormal, CPDF_Transparency());
 }
diff --git a/core/fpdfapi/render/cpdf_renderstatus.h b/core/fpdfapi/render/cpdf_renderstatus.h
index f5ed945..d5f4931 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.h
+++ b/core/fpdfapi/render/cpdf_renderstatus.h
@@ -122,11 +122,11 @@
                          BlendMode blend_mode,
                          const CPDF_Transparency& transparency);
 
- private:
   static std::unique_ptr<CPDF_GraphicStates> CloneObjStates(
       const CPDF_GraphicStates* pSrcStates,
       bool bStroke);
 
+ private:
   FX_ARGB GetFillArgbInternal(CPDF_PageObject* pObj, bool bType3) const;
   bool ProcessTransparency(CPDF_PageObject* PageObj,
                            const CFX_Matrix& mtObj2Device);
diff --git a/core/fpdfapi/render/cpdf_rendertiling.cpp b/core/fpdfapi/render/cpdf_rendertiling.cpp
new file mode 100644
index 0000000..97f537a
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendertiling.cpp
@@ -0,0 +1,249 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/render/cpdf_rendertiling.h"
+
+#include <limits>
+#include <memory>
+
+#include "core/fpdfapi/page/cpdf_form.h"
+#include "core/fpdfapi/page/cpdf_tilingpattern.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/render/cpdf_pagerendercache.h"
+#include "core/fpdfapi/render/cpdf_rendercontext.h"
+#include "core/fpdfapi/render/cpdf_renderoptions.h"
+#include "core/fpdfapi/render/cpdf_renderstatus.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+
+namespace {
+
+RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
+    CPDF_Document* pDoc,
+    CPDF_PageRenderCache* pCache,
+    CPDF_TilingPattern* pPattern,
+    CPDF_Form* pPatternForm,
+    const CFX_Matrix& mtObject2Device,
+    int width,
+    int height,
+    const CPDF_RenderOptions::Options& draw_options) {
+  auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pBitmap->Create(width, height,
+                       pPattern->colored() ? FXDIB_Argb : FXDIB_8bppMask)) {
+    return nullptr;
+  }
+  CFX_DefaultRenderDevice bitmap_device;
+  bitmap_device.Attach(pBitmap, false, nullptr, false);
+  pBitmap->Clear(0);
+  CFX_FloatRect cell_bbox =
+      pPattern->pattern_to_form().TransformRect(pPattern->bbox());
+  cell_bbox = mtObject2Device.TransformRect(cell_bbox);
+  CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
+  CFX_Matrix mtAdjust;
+  mtAdjust.MatchRect(bitmap_rect, cell_bbox);
+
+  CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
+  CPDF_RenderOptions options;
+  if (!pPattern->colored())
+    options.SetColorMode(CPDF_RenderOptions::kAlpha);
+
+  options.GetOptions() = draw_options;
+  options.GetOptions().bForceHalftone = true;
+
+  CPDF_RenderContext context(pDoc, nullptr, pCache);
+  context.AppendLayer(pPatternForm, &mtPattern2Bitmap);
+  context.Render(&bitmap_device, &options, nullptr);
+#if defined _SKIA_SUPPORT_PATHS_
+  bitmap_device.Flush(true);
+  pBitmap->UnPreMultiply();
+#endif
+  return pBitmap;
+}
+
+}  // namespace
+
+// static
+RetainPtr<CFX_DIBitmap> CPDF_RenderTiling::Draw(
+    CPDF_RenderStatus* pRenderStatus,
+    CPDF_PageObject* pPageObj,
+    CPDF_TilingPattern* pPattern,
+    CPDF_Form* pPatternForm,
+    const CFX_Matrix& mtObj2Device,
+    const FX_RECT& clip_box,
+    bool bStroke) {
+  const CFX_Matrix mtPattern2Device =
+      pPattern->pattern_to_form() * mtObj2Device;
+
+  CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
+
+  float ceil_height = std::ceil(cell_bbox.Height());
+  float ceil_width = std::ceil(cell_bbox.Width());
+
+  // Validate the float will fit into the int when the conversion is done.
+  if (!pdfium::base::IsValueInRangeForNumericType<int>(ceil_height) ||
+      !pdfium::base::IsValueInRangeForNumericType<int>(ceil_width)) {
+    return nullptr;
+  }
+
+  int width = static_cast<int>(ceil_width);
+  int height = static_cast<int>(ceil_height);
+  if (width <= 0)
+    width = 1;
+  if (height <= 0)
+    height = 1;
+
+  CFX_FloatRect clip_box_p =
+      mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
+  int min_col = static_cast<int>(
+      ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
+  int max_col = static_cast<int>(
+      floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
+  int min_row = static_cast<int>(
+      ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
+  int max_row = static_cast<int>(
+      floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
+
+  // Make sure we can fit the needed width * height into an int.
+  if (height > std::numeric_limits<int>::max() / width)
+    return nullptr;
+
+  CFX_RenderDevice* pDevice = pRenderStatus->GetRenderDevice();
+  CPDF_RenderContext* pContext = pRenderStatus->GetContext();
+  const CPDF_RenderOptions& options = pRenderStatus->GetRenderOptions();
+  if (width > clip_box.Width() || height > clip_box.Height() ||
+      width * height > clip_box.Width() * clip_box.Height()) {
+    std::unique_ptr<CPDF_GraphicStates> pStates;
+    if (!pPattern->colored())
+      pStates = CPDF_RenderStatus::CloneObjStates(pPageObj, bStroke);
+
+    const CPDF_Dictionary* pFormDict = pPatternForm->GetDict();
+    const CPDF_Dictionary* pFormResource =
+        pFormDict ? pFormDict->GetDictFor("Resources") : nullptr;
+    for (int col = min_col; col <= max_col; col++) {
+      for (int row = min_row; row <= max_row; row++) {
+        CFX_PointF original = mtPattern2Device.Transform(
+            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
+        CFX_Matrix matrix = mtObj2Device;
+        matrix.Translate(original.x - mtPattern2Device.e,
+                         original.y - mtPattern2Device.f);
+        CFX_RenderDevice::StateRestorer restorer2(pDevice);
+        CPDF_RenderStatus status(pContext, pDevice);
+        status.SetOptions(options);
+        status.SetTransparency(pPatternForm->GetTransparency());
+        status.SetFormResource(pFormResource);
+        status.SetDropObjects(pRenderStatus->GetDropObjects());
+        status.Initialize(pRenderStatus, pStates.get());
+        status.RenderObjectList(pPatternForm, matrix);
+      }
+    }
+    return nullptr;
+  }
+
+  bool bAligned =
+      pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
+      pPattern->bbox().right == pPattern->x_step() &&
+      pPattern->bbox().top == pPattern->y_step() &&
+      (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
+  if (bAligned) {
+    int orig_x = FXSYS_roundf(mtPattern2Device.e);
+    int orig_y = FXSYS_roundf(mtPattern2Device.f);
+    min_col = (clip_box.left - orig_x) / width;
+    if (clip_box.left < orig_x)
+      min_col--;
+
+    max_col = (clip_box.right - orig_x) / width;
+    if (clip_box.right <= orig_x)
+      max_col--;
+
+    min_row = (clip_box.top - orig_y) / height;
+    if (clip_box.top < orig_y)
+      min_row--;
+
+    max_row = (clip_box.bottom - orig_y) / height;
+    if (clip_box.bottom <= orig_y)
+      max_row--;
+  }
+  float left_offset = cell_bbox.left - mtPattern2Device.e;
+  float top_offset = cell_bbox.bottom - mtPattern2Device.f;
+  RetainPtr<CFX_DIBitmap> pPatternBitmap;
+  if (width * height < 16) {
+    RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
+        pContext->GetDocument(), pContext->GetPageCache(), pPattern,
+        pPatternForm, mtObj2Device, 8, 8, options.GetOptions());
+    pPatternBitmap = pEnlargedBitmap->StretchTo(
+        width, height, FXDIB_ResampleOptions(), nullptr);
+  } else {
+    pPatternBitmap = DrawPatternBitmap(
+        pContext->GetDocument(), pContext->GetPageCache(), pPattern,
+        pPatternForm, mtObj2Device, width, height, options.GetOptions());
+  }
+  if (!pPatternBitmap)
+    return nullptr;
+
+  if (options.ColorModeIs(CPDF_RenderOptions::kGray))
+    pPatternBitmap->ConvertColorScale(0, 0xffffff);
+
+  FX_ARGB fill_argb = pRenderStatus->GetFillArgb(pPageObj);
+  int clip_width = clip_box.right - clip_box.left;
+  int clip_height = clip_box.bottom - clip_box.top;
+  auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
+  if (!pScreen->Create(clip_width, clip_height, FXDIB_Argb))
+    return nullptr;
+
+  pScreen->Clear(0);
+  const uint8_t* const src_buf = pPatternBitmap->GetBuffer();
+  for (int col = min_col; col <= max_col; col++) {
+    for (int row = min_row; row <= max_row; row++) {
+      int start_x;
+      int start_y;
+      if (bAligned) {
+        start_x =
+            FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
+        start_y =
+            FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
+      } else {
+        CFX_PointF original = mtPattern2Device.Transform(
+            CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
+
+        FX_SAFE_INT32 safeStartX = FXSYS_roundf(original.x + left_offset);
+        FX_SAFE_INT32 safeStartY = FXSYS_roundf(original.y + top_offset);
+
+        safeStartX -= clip_box.left;
+        safeStartY -= clip_box.top;
+        if (!safeStartX.IsValid() || !safeStartY.IsValid())
+          return nullptr;
+
+        start_x = safeStartX.ValueOrDie();
+        start_y = safeStartY.ValueOrDie();
+      }
+      if (width == 1 && height == 1) {
+        if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
+            start_y >= clip_box.Height()) {
+          continue;
+        }
+        uint32_t* dest_buf = reinterpret_cast<uint32_t*>(
+            pScreen->GetBuffer() + pScreen->GetPitch() * start_y + start_x * 4);
+        if (pPattern->colored()) {
+          const auto* src_buf32 = reinterpret_cast<const uint32_t*>(src_buf);
+          *dest_buf = *src_buf32;
+        } else {
+          *dest_buf = (*src_buf << 24) | (fill_argb & 0xffffff);
+        }
+      } else {
+        if (pPattern->colored()) {
+          pScreen->CompositeBitmap(start_x, start_y, width, height,
+                                   pPatternBitmap, 0, 0, BlendMode::kNormal,
+                                   nullptr, false);
+        } else {
+          pScreen->CompositeMask(start_x, start_y, width, height,
+                                 pPatternBitmap, fill_argb, 0, 0,
+                                 BlendMode::kNormal, nullptr, false);
+        }
+      }
+    }
+  }
+  return pScreen;
+}
diff --git a/core/fpdfapi/render/cpdf_rendertiling.h b/core/fpdfapi/render/cpdf_rendertiling.h
new file mode 100644
index 0000000..36d37c0
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendertiling.h
@@ -0,0 +1,34 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
+#define CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
+
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+class CFX_Matrix;
+class CPDF_Form;
+class CPDF_PageObject;
+class CPDF_RenderStatus;
+class CPDF_TilingPattern;
+struct FX_RECT;
+
+class CPDF_RenderTiling {
+ public:
+  static RetainPtr<CFX_DIBitmap> Draw(CPDF_RenderStatus* pRenderStatus,
+                                      CPDF_PageObject* pPageObj,
+                                      CPDF_TilingPattern* pPattern,
+                                      CPDF_Form* pPatternForm,
+                                      const CFX_Matrix& mtObj2Device,
+                                      const FX_RECT& clip_box,
+                                      bool bStroke);
+
+  CPDF_RenderTiling() = delete;
+  CPDF_RenderTiling(const CPDF_RenderTiling&) = delete;
+  CPDF_RenderTiling& operator=(const CPDF_RenderTiling&) = delete;
+};
+
+#endif  // CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_