Add special FPDFBitmap_FillRect() handling for 24 and 32 BPP bitmaps

If Skia were to truly use pre-multiplied bitmaps internally, then the
current FPDFBitmap_FillRect() implementation would take an
un-premultiplied bitmap, pre-multiply it, let Skia fill it, and
un-premultiply it. Avoid this extra work by changing
FPDFBitmap_FillRect() to write to 32 BPP bitmaps directly.

Similarly, handle filling 24 BPP bitmaps directly in
FPDFBitmap_FillRect() to avoid a roundtrip conversion to 32 BPP and
back.

This also improves the rendering in an embedder test that uses
FPDFBitmap_FillRect(). There, the current Skia-specific test
expectations are for an image that is a drawn a bit too dark.

Bug: 42271020
Change-Id: Id156780f3ea4aa7479dc933b2da5ca192b2e27a1
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/122911
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@google.com>
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index d7c53da..dc4dd1d 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -1387,11 +1387,11 @@
   const char* md5_new_image = []() {
     if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
 #if BUILDFLAG(IS_WIN)
-      return "38c4bdbb9fd3fcc81e8b3f7d015a8c80";
+      return "256f4673854c0f4399d4768c7df73922";
 #elif BUILDFLAG(IS_APPLE)
-      return "fab7e76a223f7fd4f2f9da88b2ced171";
+      return "57a55882da9caf4d0feb2de68fac2f94";
 #else
-      return "5efd7ff61ff87e5a615a546c55450c7d";
+      return "92d2c4d7d8e56edd972f7f092c815bb0";
 #endif
     }
 #if BUILDFLAG(IS_APPLE)
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index 2c9c79c..f978330 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -967,12 +967,50 @@
   }
   CHECK(!pBitmap->IsPremultiplied());
 
-  CFX_DefaultRenderDevice device;
-  device.Attach(pBitmap);
-  if (!pBitmap->IsAlphaFormat())
+  FX_RECT fill_rect(left, top, left + width, top + height);
+
+  if (!pBitmap->IsAlphaFormat()) {
     color |= 0xFF000000;
-  device.FillRect(FX_RECT(left, top, left + width, top + height),
-                  static_cast<uint32_t>(color));
+  }
+
+  // Let CFX_DefaultRenderDevice handle the 8-bit case.
+  const int bpp = pBitmap->GetBPP();
+  if (bpp == 8) {
+    CFX_DefaultRenderDevice device;
+    device.Attach(std::move(pBitmap));
+    device.FillRect(fill_rect, static_cast<uint32_t>(color));
+    return;
+  }
+
+  // Handle filling 24/32-bit bitmaps directly without CFX_DefaultRenderDevice.
+  // When CFX_DefaultRenderDevice is using Skia, this avoids extra work to
+  // change `pBitmap` to be premultiplied and back, or extra work to change
+  // `pBitmap` to 32 BPP and back.
+  fill_rect.Intersect(FX_RECT(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight()));
+  if (fill_rect.IsEmpty()) {
+    return;
+  }
+
+  const int row_end = fill_rect.top + fill_rect.Height();
+  if (bpp == 32) {
+    for (int row = fill_rect.top; row < row_end; ++row) {
+      auto span32 = pBitmap->GetWritableScanlineAs<uint32_t>(row).subspan(
+          fill_rect.left, fill_rect.Width());
+      fxcrt::Fill(span32, static_cast<uint32_t>(color));
+    }
+    return;
+  }
+
+  CHECK_EQ(bpp, 24);
+  const FX_BGR_STRUCT<uint8_t> bgr = {.blue = FXARGB_B(color),
+                                      .green = FXARGB_G(color),
+                                      .red = FXARGB_R(color)};
+  for (int row = fill_rect.top; row < row_end; ++row) {
+    auto bgr_span =
+        pBitmap->GetWritableScanlineAs<FX_BGR_STRUCT<uint8_t>>(row).subspan(
+            fill_rect.left, fill_rect.Width());
+    fxcrt::Fill(bgr_span, bgr);
+  }
 }
 
 FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {