Add high-resolution re-rendering test

Adds a test for repeatedly rendering a PDF page containing a single
full-page, high-resolution (600 DPI) image. This should cache the
derived SkImage, but currently does not.

Bug: pdfium:2034
Change-Id: I80dff81abbb0b7009fe3ea46d1fe3f21554fcf19
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/108050
Reviewed-by: Nigi <nigi@chromium.org>
Commit-Queue: K. Moon <kmoon@chromium.org>
diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn
index c526c06..dcb17d4 100644
--- a/core/fxge/BUILD.gn
+++ b/core/fxge/BUILD.gn
@@ -236,6 +236,8 @@
     deps += [
       ":fxge",
       "../../fpdfsdk",
+      "../fpdfapi/page",
+      "../fpdfapi/render",
       "//skia",
     ]
   }
diff --git a/core/fxge/skia/fx_skia_device_embeddertest.cpp b/core/fxge/skia/fx_skia_device_embeddertest.cpp
index 517d863..8e8ee2c 100644
--- a/core/fxge/skia/fx_skia_device_embeddertest.cpp
+++ b/core/fxge/skia/fx_skia_device_embeddertest.cpp
@@ -5,7 +5,11 @@
 #include "core/fxge/skia/fx_skia_device.h"
 
 #include <memory>
+#include <set>
+#include <utility>
 
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
@@ -15,13 +19,22 @@
 #include "core/fxge/cfx_textrenderoptions.h"
 #include "core/fxge/text_char_pos.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_renderpage.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
 
 namespace {
 
+using ::testing::NiceMock;
+using ::testing::SizeIs;
+using ::testing::WithArg;
+
 struct State {
   enum class Change { kNo, kYes };
   enum class Save { kNo, kYes };
@@ -141,6 +154,47 @@
   EXPECT_EQ(state.m_pixel, pixel);
 }
 
+void RenderPageToSkCanvas(FPDF_PAGE page,
+                          int start_x,
+                          int start_y,
+                          int size_x,
+                          int size_y,
+                          SkCanvas* canvas) {
+  CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page);
+
+  auto context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* unowned_context = context.get();
+
+  CPDF_Page::RenderContextClearer clearer(cpdf_page);
+  cpdf_page->SetRenderContext(std::move(context));
+
+  auto default_device = std::make_unique<CFX_DefaultRenderDevice>();
+  default_device->AttachCanvas(canvas);
+  unowned_context->m_pDevice = std::move(default_device);
+
+  CPDFSDK_RenderPageWithContext(unowned_context, cpdf_page, start_x, start_y,
+                                size_x, size_y, /*rotate=*/0, /*flags=*/0,
+                                /*color_scheme=*/nullptr,
+                                /*need_to_restore=*/true, /*pause=*/nullptr);
+}
+
+class MockCanvas : public SkNoDrawCanvas {
+ public:
+  MockCanvas(int width, int height) : SkNoDrawCanvas(width, height) {}
+
+  MOCK_METHOD(void,
+              onDrawImageRect2,
+              (const SkImage*,
+               const SkRect&,
+               const SkRect&,
+               const SkSamplingOptions&,
+               const SkPaint*,
+               SrcRectConstraint),
+              (override));
+};
+
+using FxgeSkiaEmbedderTest = EmbedderTest;
+
 }  // namespace
 
 TEST(fxge, SkiaStateEmpty) {
@@ -182,3 +236,36 @@
     return;
   Harness(&OutOfSequenceClipTest, {});
 }
+
+TEST_F(FxgeSkiaEmbedderTest, RenderBigImageTwice) {
+  static constexpr int kCanvasWidth = 306;
+  static constexpr int kCanvasHeight = 396;
+
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    GTEST_SKIP() << "Skia is not the default renderer";
+  }
+
+  ASSERT_TRUE(OpenDocument("bug_2034.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::set<int> image_ids;
+  NiceMock<MockCanvas> canvas(kCanvasWidth, kCanvasHeight);
+  EXPECT_CALL(canvas, onDrawImageRect2)
+      .WillRepeatedly(WithArg<0>([&image_ids](const SkImage* image) {
+        ASSERT_TRUE(image);
+        image_ids.insert(image->uniqueID());
+      }));
+
+  RenderPageToSkCanvas(page, /*start_x=*/0, /*start_y=*/0,
+                       /*size_x=*/kCanvasWidth, /*size_y=*/kCanvasHeight / 2,
+                       &canvas);
+  RenderPageToSkCanvas(page, /*start_x=*/0, /*start_y=*/kCanvasHeight / 2,
+                       /*size_x=*/kCanvasWidth, /*size_y=*/kCanvasHeight / 2,
+                       &canvas);
+
+  // TODO(crbug.com/pdfium/2034): This should be 1, not 2.
+  EXPECT_THAT(image_ids, SizeIs(2));
+
+  UnloadPage(page);
+}
diff --git a/testing/resources/bug_2034.idat b/testing/resources/bug_2034.idat
new file mode 100644
index 0000000..873c408
--- /dev/null
+++ b/testing/resources/bug_2034.idat
Binary files differ
diff --git a/testing/resources/bug_2034.in b/testing/resources/bug_2034.in
new file mode 100644
index 0000000..f762659
--- /dev/null
+++ b/testing/resources/bug_2034.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject <<
+      /HighResolutionImage 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  612 0 0 792 0 0 cm
+  /HighResolutionImage Do
+Q
+endstream
+endobj
+
+% Image meant to simulate scanner output: full page, 600 DPI, in RGB color.
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 4
+  /ColorSpace [
+    /Indexed
+    /DeviceRGB
+    4
+    <000000 FF0000 00FF00 0000FF FFFFFF>
+  ]
+  /DecodeParms <<
+    /BitsPerComponent 4
+    /Columns 5100
+    /Predictor 10
+  >>
+  /Filter /FlateDecode
+  /Height 6600
+  /Width 5100
+  {{streamlen}}
+>>
+stream
+{{include bug_2034.idat}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_2034.pdf b/testing/resources/bug_2034.pdf
new file mode 100644
index 0000000..78ddedf
--- /dev/null
+++ b/testing/resources/bug_2034.pdf
Binary files differ