Improve SkPictureRecorder ownership

Uses std::unique_ptr to improve the ownership handling for
SkPictureRecorder. This fixes an issue with CFX_SkiaDeviceDriver
creating, and then leaking, the SkPictureRecorder.

Bug: pdfium:1929
Change-Id: Idd4ae80d6bb9b1c5ff08298748f99743c606aaa2
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/100750
Auto-Submit: K. Moon <kmoon@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxge/cfx_defaultrenderdevice.h b/core/fxge/cfx_defaultrenderdevice.h
index 028612e..0dcd4c2 100644
--- a/core/fxge/cfx_defaultrenderdevice.h
+++ b/core/fxge/cfx_defaultrenderdevice.h
@@ -7,11 +7,14 @@
 #ifndef CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_
 #define CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_
 
+#include <memory>
+
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/fx_dib.h"
 
 class SkPictureRecorder;
+struct SkRect;
 
 class CFX_DefaultRenderDevice final : public CFX_RenderDevice {
  public:
@@ -33,7 +36,7 @@
 #if defined(_SKIA_SUPPORT_)
   bool AttachRecorder(SkPictureRecorder* recorder);
   void Clear(uint32_t color);
-  SkPictureRecorder* CreateRecorder(int size_x, int size_y);
+  std::unique_ptr<SkPictureRecorder> CreateRecorder(const SkRect& bounds);
   void DebugVerifyBitmapIsPreMultiplied() const override;
   bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
                        const RetainPtr<CFX_DIBBase>& pMask,
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index 57eb781..772de30 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -1740,16 +1740,6 @@
 }
 
 #if defined(_SKIA_SUPPORT_)
-CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(int size_x, int size_y)
-    : m_pBitmap(nullptr),
-      m_pBackdropBitmap(nullptr),
-      m_pRecorder(new SkPictureRecorder),
-      m_pCache(std::make_unique<SkiaState>(this)),
-      m_bGroupKnockout(false) {
-  m_pRecorder->beginRecording(SkIntToScalar(size_x), SkIntToScalar(size_y));
-  m_pCanvas = m_pRecorder->getRecordingCanvas();
-}
-
 CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder)
     : m_pBitmap(nullptr),
       m_pBackdropBitmap(nullptr),
@@ -2880,11 +2870,12 @@
   skDriver->Clear(color);
 }
 
-SkPictureRecorder* CFX_DefaultRenderDevice::CreateRecorder(int size_x,
-                                                           int size_y) {
-  auto driver = std::make_unique<CFX_SkiaDeviceDriver>(size_x, size_y);
-  SkPictureRecorder* recorder = driver->GetRecorder();
-  SetDeviceDriver(std::move(driver));
+std::unique_ptr<SkPictureRecorder> CFX_DefaultRenderDevice::CreateRecorder(
+    const SkRect& bounds) {
+  auto recorder = std::make_unique<SkPictureRecorder>();
+  recorder->beginRecording(bounds);
+
+  SetDeviceDriver(std::make_unique<CFX_SkiaDeviceDriver>(recorder.get()));
   return recorder;
 }
 #endif  // defined(_SKIA_SUPPORT_)
diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h
index 449927c..3a8fca6 100644
--- a/core/fxge/skia/fx_skia_device.h
+++ b/core/fxge/skia/fx_skia_device.h
@@ -32,7 +32,6 @@
                        bool bGroupKnockout);
 #if defined(_SKIA_SUPPORT_)
   explicit CFX_SkiaDeviceDriver(SkPictureRecorder* recorder);
-  CFX_SkiaDeviceDriver(int size_x, int size_y);
 #endif
   ~CFX_SkiaDeviceDriver() override;
 
@@ -156,7 +155,6 @@
                    const SkMatrix& matrix);
   void Clear(uint32_t color);
   void Flush() override;
-  SkPictureRecorder* GetRecorder() const { return m_pRecorder; }
   void PreMultiply();
   static void PreMultiply(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
   SkCanvas* SkiaCanvas() { return m_pCanvas; }
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index 20eebb9..a1fc16b 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -97,6 +97,10 @@
     ]
     allow_circular_includes_from += [ "fpdfxfa" ]
   }
+
+  if (pdf_use_skia || pdf_use_skia_paths) {
+    deps += [ "//skia" ]
+  }
 }
 
 pdfium_unittest_source_set("unittests") {
@@ -158,4 +162,8 @@
     "../core/fxge",
   ]
   pdfium_root_dir = "../"
+
+  if (pdf_use_skia || pdf_use_skia_paths) {
+    deps += [ "//skia" ]
+  }
 }
diff --git a/fpdfsdk/DEPS b/fpdfsdk/DEPS
index 0e367fb..14d2700 100644
--- a/fpdfsdk/DEPS
+++ b/fpdfsdk/DEPS
@@ -2,6 +2,7 @@
   '+core',
   '+fxjs',
   '+public',
+  '+third_party/skia/include',
   '+v8',
   '+xfa/fgas/font',
   '+xfa/fgas/graphics',
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index 211de79..8f93c66 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -51,6 +51,11 @@
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/span.h"
 
+#ifdef _SKIA_SUPPORT_
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkRect.h"
+#endif  // _SKIA_SUPPORT_
+
 #ifdef PDF_ENABLE_V8
 #include "fxjs/cfx_v8_array_buffer_allocator.h"
 #include "third_party/base/no_destructor.h"
@@ -735,14 +740,15 @@
   pPage->SetRenderContext(std::move(pOwnedContext));
 
   auto skDevice = std::make_unique<CFX_DefaultRenderDevice>();
-  FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y);
+  std::unique_ptr<SkPictureRecorder> recorder =
+      skDevice->CreateRecorder(SkRect::MakeWH(size_x, size_y));
   pContext->m_pDevice = std::move(skDevice);
 
   CPDFSDK_RenderPageWithContext(pContext, pPage, 0, 0, size_x, size_y, 0, 0,
                                 /*color_scheme=*/nullptr,
                                 /*need_to_restore=*/true, /*pause=*/nullptr);
 
-  return recorder;
+  return recorder.release();
 }
 #endif  // defined(_SKIA_SUPPORT_)
 
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index 13f1f89..0f3ccbd 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -7,6 +7,7 @@
 #include <limits>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "build/build_config.h"
@@ -26,6 +27,19 @@
 #include "testing/utils/path_service.h"
 #include "third_party/base/check.h"
 
+#ifdef _SKIA_SUPPORT_
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkColorType.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#endif  // _SKIA_SUPPORT_
+
 using pdfium::ManyRectanglesChecksum;
 
 namespace {
@@ -95,6 +109,48 @@
   ~MockDownloadHints() = default;
 };
 
+#ifdef _SKIA_SUPPORT_
+ScopedFPDFBitmap SkImageToPdfiumBitmap(const SkImage& image) {
+  ScopedFPDFBitmap bitmap(
+      FPDFBitmap_Create(image.width(), image.height(), /*alpha=*/1));
+  if (!bitmap) {
+    ADD_FAILURE() << "Could not create FPDF_BITMAP";
+    return nullptr;
+  }
+
+  if (!image.readPixels(/*context=*/nullptr,
+                        image.imageInfo().makeColorType(kBGRA_8888_SkColorType),
+                        FPDFBitmap_GetBuffer(bitmap.get()),
+                        FPDFBitmap_GetStride(bitmap.get()),
+                        /*srcX=*/0, /*srcY=*/0)) {
+    ADD_FAILURE() << "Could not read pixels from SkImage";
+    return nullptr;
+  }
+
+  return bitmap;
+}
+
+ScopedFPDFBitmap SkPictureToPdfiumBitmap(sk_sp<SkPicture> picture,
+                                         const SkISize& size) {
+  sk_sp<SkSurface> surface =
+      SkSurface::MakeRasterN32Premul(size.width(), size.height());
+  if (!surface) {
+    ADD_FAILURE() << "Could not create SkSurface";
+    return nullptr;
+  }
+
+  surface->getCanvas()->clear(SK_ColorWHITE);
+  surface->getCanvas()->drawPicture(picture);
+  sk_sp<SkImage> image = surface->makeImageSnapshot();
+  if (!image) {
+    ADD_FAILURE() << "Could not snapshot SkSurface";
+    return nullptr;
+  }
+
+  return SkImageToPdfiumBitmap(*image);
+}
+#endif  // _SKIA_SUPPORT_
+
 }  // namespace
 
 TEST(fpdf, CApiTest) {
@@ -1892,3 +1948,35 @@
   ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size));
   EXPECT_EQ(kExpectedEnds, ends);
 }
+
+#ifdef _SKIA_SUPPORT_
+TEST_F(FPDFViewEmbedderTest, RenderPageToSkp) {
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    GTEST_SKIP() << "FPDF_RenderPageSkp() only makes sense with Skia";
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr SkISize kOutputSize = SkISize::Make(200, 300);
+
+  FPDF_RECORDER opaque_recorder =
+      FPDF_RenderPageSkp(page, kOutputSize.width(), kOutputSize.height());
+  UnloadPage(page);
+  ASSERT_TRUE(opaque_recorder);
+
+  SkPictureRecorder* recorder =
+      reinterpret_cast<SkPictureRecorder*>(opaque_recorder);
+  sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
+  delete recorder;
+  ASSERT_TRUE(picture);
+
+  ScopedFPDFBitmap bitmap =
+      SkPictureToPdfiumBitmap(std::move(picture), kOutputSize);
+  ASSERT_TRUE(bitmap);
+
+  CompareBitmap(bitmap.get(), kOutputSize.width(), kOutputSize.height(),
+                pdfium::RectanglesChecksum());
+}
+#endif  // _SKIA_SUPPORT_