Add CFX_DIBBase::RealizeSkImage()

Adds a CFX_DIBBase::RealizeSkImage() API intended to create an SkImage
from a CFX_DIBBase in the most efficient way possible. Also moves
Skia-specific CFX_DIBBase code into its own file, cfx_dibbase_skia.cpp.

Bug: pdfium:2034
Change-Id: I3d8f33e10aab7a990fbdfd3b462a7cc4029e4a00
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/108510
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 a771bfe..a3d2b0e 100644
--- a/core/fxge/BUILD.gn
+++ b/core/fxge/BUILD.gn
@@ -134,7 +134,10 @@
   }
 
   if (pdf_use_skia) {
-    sources += [ "skia/fx_skia_device.cpp" ]
+    sources += [
+      "skia/cfx_dibbase_skia.cpp",
+      "skia/fx_skia_device.cpp",
+    ]
     public_deps += [ "//skia" ]
   }
 
diff --git a/core/fxge/DEPS b/core/fxge/DEPS
index 8586210..9094236 100644
--- a/core/fxge/DEPS
+++ b/core/fxge/DEPS
@@ -1,5 +1,5 @@
 specific_include_rules = {
-  'cfx_glyphcache\.(cpp|h)|cfx_renderdevice\.cpp': [
+  'cfx_dibbase\.h|cfx_glyphcache\.(cpp|h)|cfx_renderdevice\.cpp': [
     '+third_party/skia/include',
   ],
 }
diff --git a/core/fxge/dib/cfx_dibbase.h b/core/fxge/dib/cfx_dibbase.h
index bfe4534..8d2cfa5 100644
--- a/core/fxge/dib/cfx_dibbase.h
+++ b/core/fxge/dib/cfx_dibbase.h
@@ -14,12 +14,20 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "third_party/base/span.h"
 
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/skia/include/core/SkRefCnt.h"  // nogncheck
+#endif
+
 class CFX_ClipRgn;
 class CFX_DIBitmap;
 class CFX_Matrix;
 class PauseIndicatorIface;
 struct FX_RECT;
 
+#if defined(_SKIA_SUPPORT_)
+class SkImage;
+#endif  // defined(_SKIA_SUPPORT_)
+
 // Base class for all Device-Independent Bitmaps.
 class CFX_DIBBase : public Retainable {
  public:
@@ -81,6 +89,12 @@
                       int& src_top,
                       const CFX_ClipRgn* pClipRgn) const;
 
+#if defined(_SKIA_SUPPORT_)
+  // Realizes an `SkImage` from this DIB. Changes to the DIB do not affect the
+  // `SkImage`. `force_alpha` forces gray images to be treated as 8-bit alpha.
+  virtual sk_sp<SkImage> RealizeSkImage(bool force_alpha) const;
+#endif  // defined(_SKIA_SUPPORT_)
+
  protected:
   CFX_DIBBase();
   ~CFX_DIBBase() override;
diff --git a/core/fxge/skia/cfx_dibbase_skia.cpp b/core/fxge/skia/cfx_dibbase_skia.cpp
new file mode 100644
index 0000000..f293bc9
--- /dev/null
+++ b/core/fxge/skia/cfx_dibbase_skia.cpp
@@ -0,0 +1,280 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/dib/cfx_dibbase.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/span.h"
+#include "third_party/skia/include/core/SkAlphaType.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkColorPriv.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/SkPixmap.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace {
+
+// A builder that can create an `SkImage` either from a `CFX_DIBBase`, or from
+// newly-allocated memory.
+class SkiaImageBuilder {
+ public:
+  SkiaImageBuilder() = default;
+
+  // Constructs a builder backed by pixels shared with a `CFX_DIBBase`. The
+  // pixels are not accessible from the builder. The `CFX_DIBBase` must outlive
+  // the builder, as the reference count on the `CFX_DIBBase` is not incremented
+  // until `Build()` is called.
+  SkiaImageBuilder(const CFX_DIBBase* source, SkColorType color_type)
+      : info_(SkImageInfo::Make(source->GetWidth(),
+                                source->GetHeight(),
+                                color_type,
+                                kPremul_SkAlphaType)),
+        data_(UnownedType(source)) {
+    // TODO(crbug.com/pdfium/2047): `Realize()` if there's no buffer?
+    row_bytes_ = source->GetPitch();
+  }
+
+  // Constructs a builder backed by allocated pixels.
+  explicit SkiaImageBuilder(const SkImageInfo& info) : info_(info) {
+    row_bytes_ = info_.minRowBytes();
+    data_size_ = Fx2DSizeOrDie(row_bytes_, info_.height());
+    data_ = AllocatedType(FX_Alloc(uint8_t, data_size_));
+  }
+
+  SkiaImageBuilder(SkiaImageBuilder&&) = default;
+  SkiaImageBuilder& operator=(SkiaImageBuilder&&) = default;
+
+  // Gets the number of bytes per row.
+  size_t row_bytes() const { return row_bytes_; }
+
+  // Gets the 8-bit pixels (if using allocated memory).
+  pdfium::span<uint8_t> allocated_pixels8() {
+    DCHECK_EQ(static_cast<size_t>(info_.bytesPerPixel()), sizeof(uint8_t));
+    return pdfium::make_span(
+        reinterpret_cast<uint8_t*>(absl::get<AllocatedType>(data_).get()),
+        data_size_);
+  }
+
+  // Gets the 32-bit pixels (if using allocated memory).
+  pdfium::span<uint32_t> allocated_pixels32() {
+    DCHECK_EQ(static_cast<size_t>(info_.bytesPerPixel()), sizeof(uint32_t));
+    return pdfium::make_span(
+        reinterpret_cast<uint32_t*>(absl::get<AllocatedType>(data_).get()),
+        data_size_ / sizeof(uint32_t));
+  }
+
+  // Builds an `SkImage` that takes ownership of the pixels. If sharing pixels
+  // with a `CFX_DIBBase`, this increments the reference count on the
+  // `CFX_DIBBase`.
+  //
+  // Note that an `SkImage` must be immutable, so the pixels must not be
+  // modified during the lifetime of the `SkImage`.
+  sk_sp<SkImage> Build() && {
+    if (absl::holds_alternative<AllocatedType>(data_)) {
+      // "Leak" allocated memory to `SkImage`.
+      return SkImages::RasterFromPixmap(
+          SkPixmap(info_, absl::get<AllocatedType>(data_).release(),
+                   row_bytes_),
+          /*rasterReleaseProc=*/ReleaseAllocated,
+          /*releaseContext=*/nullptr);
+    }
+
+    // Convert unowned pointer to a retained pointer, then "leak" to `SkImage`.
+    RetainPtr<const CFX_DIBBase> retained(
+        absl::get<UnownedType>(data_).ExtractAsDangling());
+    const CFX_DIBBase* source = retained.Leak();
+    return SkImages::RasterFromPixmap(
+        SkPixmap(info_, source->GetBuffer().data(), row_bytes_),
+        /*rasterReleaseProc=*/ReleaseRetained,
+        /*releaseContext=*/const_cast<CFX_DIBBase*>(source));
+  }
+
+ private:
+  using UnownedType = UnownedPtr<const CFX_DIBBase>;
+  using AllocatedType = std::unique_ptr<void, FxFreeDeleter>;
+
+  // Releases `CFX_DIBBase` "leaked" by `Build()`.
+  static void ReleaseRetained(const void* /*pixels*/,
+                              SkImages::ReleaseContext context) {
+    RetainPtr<const CFX_DIBBase> retained;
+    retained.Unleak(reinterpret_cast<const CFX_DIBBase*>(context));
+  }
+
+  // Releases allocated memory "leaked" by `Build()`.
+  static void ReleaseAllocated(const void* pixels,
+                               SkImages::ReleaseContext /*context*/) {
+    FX_Free(const_cast<void*>(pixels));
+  }
+
+  SkImageInfo info_;
+  size_t row_bytes_;
+
+  // Similar to `MaybeOwned<uint32_t, FxFreeDeleter>`, but holds either an
+  // unowned `CFX_DIBBase` pointer or an owned `void` pointer.
+  absl::variant<UnownedType, AllocatedType> data_;
+  size_t data_size_;
+};
+
+// Creates a `SkiaImageBuilder` using colors from a 1-bit-per-pixel palette.
+SkiaImageBuilder CreateSkiaImageBuilderUsingSingleBitPalette(
+    const CFX_DIBBase* source) {
+  DCHECK_EQ(1, source->GetBPP());
+  int width = source->GetWidth();
+  int height = source->GetHeight();
+  const void* buffer = source->GetBuffer().data();
+  DCHECK(buffer);
+
+  uint32_t color0 = source->GetPaletteArgb(0);
+  uint32_t color1 = source->GetPaletteArgb(1);
+  SkiaImageBuilder image_builder(SkImageInfo::Make(
+      width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType));
+  pdfium::span<SkPMColor> dst32_pixels(image_builder.allocated_pixels32());
+
+  for (int y = 0; y < height; ++y) {
+    const uint8_t* src_row =
+        static_cast<const uint8_t*>(buffer) + y * source->GetPitch();
+    pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
+    for (int x = 0; x < width; ++x) {
+      bool use_color1 = src_row[x / 8] & (1 << (7 - x % 8));
+      dst_row[x] = use_color1 ? color1 : color0;
+    }
+  }
+  return image_builder;
+}
+
+// Creates a `SkiaImageBuilder` using colors from `palette`.
+SkiaImageBuilder CreateSkiaImageBuilderUsingPalette(
+    const CFX_DIBBase* source,
+    pdfium::span<const uint32_t> palette) {
+  DCHECK_EQ(8, source->GetBPP());
+  int width = source->GetWidth();
+  int height = source->GetHeight();
+  const void* buffer = source->GetBuffer().data();
+  DCHECK(buffer);
+  SkiaImageBuilder image_builder(SkImageInfo::Make(
+      width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType));
+  pdfium::span<SkPMColor> dst32_pixels(image_builder.allocated_pixels32());
+
+  for (int y = 0; y < height; ++y) {
+    const uint8_t* src_row =
+        static_cast<const uint8_t*>(buffer) + y * source->GetPitch();
+    pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
+    for (int x = 0; x < width; ++x) {
+      unsigned index = src_row[x];
+      if (index >= palette.size()) {
+        index = 0;
+      }
+      dst_row[x] = palette[index];
+    }
+  }
+  return image_builder;
+}
+
+bool IsRGBColorGrayScale(uint32_t color) {
+  return FXARGB_R(color) == FXARGB_G(color) &&
+         FXARGB_R(color) == FXARGB_B(color);
+}
+
+}  // namespace
+
+sk_sp<SkImage> CFX_DIBBase::RealizeSkImage(bool force_alpha) const {
+  const uint8_t* const buffer = GetBuffer().data();
+  if (!buffer) {
+    return nullptr;
+  }
+
+  const SkColorType color_type = force_alpha || IsMaskFormat()
+                                     ? SkColorType::kAlpha_8_SkColorType
+                                     : SkColorType::kGray_8_SkColorType;
+  const int width = GetWidth();
+  const int height = GetHeight();
+  const int row_bytes = GetPitch();
+  SkiaImageBuilder image_builder(this, color_type);
+  switch (GetBPP()) {
+    case 1: {
+      // By default, the two colors for grayscale are 0xFF and 0x00 unless they
+      // are specified in the palette.
+      uint8_t color0 = 0x00;
+      uint8_t color1 = 0xFF;
+
+      if (GetFormat() == FXDIB_Format::k1bppRgb && HasPalette()) {
+        uint32_t palette_color0 = GetPaletteArgb(0);
+        uint32_t palette_color1 = GetPaletteArgb(1);
+        bool use_gray_colors = IsRGBColorGrayScale(palette_color0) &&
+                               IsRGBColorGrayScale(palette_color1);
+        if (!use_gray_colors) {
+          image_builder = CreateSkiaImageBuilderUsingSingleBitPalette(this);
+          break;
+        }
+
+        color0 = FXARGB_R(palette_color0);
+        color1 = FXARGB_R(palette_color1);
+      }
+
+      image_builder = SkiaImageBuilder(
+          SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType));
+      pdfium::span<uint8_t> dst8_pixels = image_builder.allocated_pixels8();
+      for (int y = 0; y < height; ++y) {
+        const uint8_t* src_row = buffer + y * row_bytes;
+        pdfium::span<uint8_t> dst_row =
+            dst8_pixels.subspan(y * image_builder.row_bytes());
+        for (int x = 0; x < width; ++x) {
+          dst_row[x] = src_row[x >> 3] & (1 << (~x & 0x07)) ? color1 : color0;
+        }
+      }
+      break;
+    }
+    case 8:
+      // we upscale ctables to 32bit.
+      if (HasPalette()) {
+        const size_t src_palette_size = GetRequiredPaletteSize();
+        pdfium::span<const uint32_t> src_palette = GetPaletteSpan();
+        CHECK_LE(src_palette_size, src_palette.size());
+        if (src_palette_size < src_palette.size()) {
+          src_palette = src_palette.first(src_palette_size);
+        }
+
+        image_builder = CreateSkiaImageBuilderUsingPalette(this, src_palette);
+      }
+      break;
+    case 24: {
+      image_builder = SkiaImageBuilder(SkImageInfo::Make(
+          width, height, kBGRA_8888_SkColorType, kOpaque_SkAlphaType));
+      pdfium::span<uint32_t> dst32_pixels = image_builder.allocated_pixels32();
+      for (int y = 0; y < height; ++y) {
+        const uint8_t* src_row = buffer + y * row_bytes;
+        pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
+        for (int x = 0; x < width; ++x) {
+          dst_row[x] = SkPackARGB32NoCheck(
+              0xFF, src_row[x * 3 + 2], src_row[x * 3 + 1], src_row[x * 3 + 0]);
+        }
+      }
+      break;
+    }
+    case 32:
+      image_builder = SkiaImageBuilder(this, kBGRA_8888_SkColorType);
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  return std::move(image_builder).Build();
+}
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index b5e0f6a..e78b91e 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -30,10 +30,8 @@
 #include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_memory.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/stl_util.h"
-#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
@@ -47,7 +45,6 @@
 #include "core/fxge/dib/cstretchengine.h"
 #include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/text_char_pos.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
 #include "third_party/base/notreached.h"
@@ -160,167 +157,6 @@
 #endif  // SHOW_SKIA_PATH
 }
 
-bool IsRGBColorGrayScale(uint32_t color) {
-  return FXARGB_R(color) == FXARGB_G(color) &&
-         FXARGB_R(color) == FXARGB_B(color);
-}
-
-// A builder that can create an `SkImage` either from a `CFX_DIBBase`, or from
-// newly-allocated memory.
-class SkiaImageBuilder {
- public:
-  SkiaImageBuilder() = default;
-
-  // Constructs a builder backed by pixels shared with a `CFX_DIBBase`. The
-  // pixels are not accessible from the builder. The `CFX_DIBBase` must outlive
-  // the builder, as the reference count on the `CFX_DIBBase` is not incremented
-  // until `Build()` is called.
-  SkiaImageBuilder(CFX_DIBBase* source, SkColorType color_type)
-      : info_(SkImageInfo::Make(source->GetWidth(),
-                                source->GetHeight(),
-                                color_type,
-                                kPremul_SkAlphaType)),
-        data_(UnownedType(source)) {
-    // TODO(crbug.com/pdfium/2047): `Realize()` if there's no buffer?
-    row_bytes_ = source->GetPitch();
-  }
-
-  // Constructs a builder backed by allocated pixels.
-  explicit SkiaImageBuilder(const SkImageInfo& info) : info_(info) {
-    row_bytes_ = info_.minRowBytes();
-    data_size_ = Fx2DSizeOrDie(row_bytes_, info_.height());
-    data_ = AllocatedType(FX_Alloc(uint8_t, data_size_));
-  }
-
-  SkiaImageBuilder(SkiaImageBuilder&&) = default;
-  SkiaImageBuilder& operator=(SkiaImageBuilder&&) = default;
-
-  // Gets the number of bytes per row.
-  size_t row_bytes() const { return row_bytes_; }
-
-  // Gets the 8-bit pixels (if using allocated memory).
-  pdfium::span<uint8_t> allocated_pixels8() {
-    DCHECK_EQ(static_cast<size_t>(info_.bytesPerPixel()), sizeof(uint8_t));
-    return pdfium::make_span(
-        reinterpret_cast<uint8_t*>(absl::get<AllocatedType>(data_).get()),
-        data_size_);
-  }
-
-  // Gets the 32-bit pixels (if using allocated memory).
-  pdfium::span<uint32_t> allocated_pixels32() {
-    DCHECK_EQ(static_cast<size_t>(info_.bytesPerPixel()), sizeof(uint32_t));
-    return pdfium::make_span(
-        reinterpret_cast<uint32_t*>(absl::get<AllocatedType>(data_).get()),
-        data_size_ / sizeof(uint32_t));
-  }
-
-  // Builds an `SkImage` that takes ownership of the pixels. If sharing pixels
-  // with a `CFX_DIBBase`, this increments the reference count on the
-  // `CFX_DIBBase`.
-  //
-  // Note that an `SkImage` must be immutable, so the pixels must not be
-  // modified during the lifetime of the `SkImage`.
-  sk_sp<SkImage> Build() && {
-    if (absl::holds_alternative<AllocatedType>(data_)) {
-      // "Leak" allocated memory to `SkImage`.
-      return SkImages::RasterFromPixmap(
-          SkPixmap(info_, absl::get<AllocatedType>(data_).release(),
-                   row_bytes_),
-          /*rasterReleaseProc=*/ReleaseAllocated,
-          /*releaseContext=*/nullptr);
-    }
-
-    // Convert unowned pointer to a retained pointer, then "leak" to `SkImage`.
-    RetainPtr<CFX_DIBBase> retained(
-        absl::get<UnownedType>(data_).ExtractAsDangling());
-    CFX_DIBBase* source = retained.Leak();
-    return SkImages::RasterFromPixmap(
-        SkPixmap(info_, source->GetBuffer().data(), row_bytes_),
-        /*rasterReleaseProc=*/ReleaseRetained,
-        /*releaseContext=*/source);
-  }
-
- private:
-  using UnownedType = UnownedPtr<CFX_DIBBase>;
-  using AllocatedType = std::unique_ptr<void, FxFreeDeleter>;
-
-  // Releases `CFX_DIBBase` "leaked" by `Build()`.
-  static void ReleaseRetained(const void* /*pixels*/,
-                              SkImages::ReleaseContext context) {
-    RetainPtr<CFX_DIBBase> retained;
-    retained.Unleak(reinterpret_cast<CFX_DIBBase*>(context));
-  }
-
-  // Releases allocated memory "leaked" by `Build()`.
-  static void ReleaseAllocated(const void* pixels,
-                               SkImages::ReleaseContext /*context*/) {
-    FX_Free(const_cast<void*>(pixels));
-  }
-
-  SkImageInfo info_;
-  size_t row_bytes_;
-
-  // Similar to `MaybeOwned<uint32_t, FxFreeDeleter>`, but holds either an
-  // unowned `CFX_DIBBase` pointer or an owned `void` pointer.
-  absl::variant<UnownedType, AllocatedType> data_;
-  size_t data_size_;
-};
-
-// Creates a `SkiaImageBuilder` using colors from a 1-bit-per-pixel palette.
-SkiaImageBuilder CreateSkiaImageBuilderUsingSingleBitPalette(
-    const RetainPtr<CFX_DIBBase>& source) {
-  DCHECK_EQ(1, source->GetBPP());
-  int width = source->GetWidth();
-  int height = source->GetHeight();
-  const void* buffer = source->GetBuffer().data();
-  DCHECK(buffer);
-
-  uint32_t color0 = source->GetPaletteArgb(0);
-  uint32_t color1 = source->GetPaletteArgb(1);
-  SkiaImageBuilder image_builder(SkImageInfo::Make(
-      width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType));
-  pdfium::span<SkPMColor> dst32_pixels(image_builder.allocated_pixels32());
-
-  for (int y = 0; y < height; ++y) {
-    const uint8_t* src_row =
-        static_cast<const uint8_t*>(buffer) + y * source->GetPitch();
-    pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
-    for (int x = 0; x < width; ++x) {
-      bool use_color1 = src_row[x / 8] & (1 << (7 - x % 8));
-      dst_row[x] = use_color1 ? color1 : color0;
-    }
-  }
-  return image_builder;
-}
-
-// Creates a `SkiaImageBuilder` using colors from `palette`.
-SkiaImageBuilder CreateSkiaImageBuilderUsingPalette(
-    const RetainPtr<CFX_DIBBase>& source,
-    pdfium::span<const uint32_t> palette) {
-  DCHECK_EQ(8, source->GetBPP());
-  int width = source->GetWidth();
-  int height = source->GetHeight();
-  const void* buffer = source->GetBuffer().data();
-  DCHECK(buffer);
-  SkiaImageBuilder image_builder(SkImageInfo::Make(
-      width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType));
-  pdfium::span<SkPMColor> dst32_pixels(image_builder.allocated_pixels32());
-
-  for (int y = 0; y < height; ++y) {
-    const uint8_t* src_row =
-        static_cast<const uint8_t*>(buffer) + y * source->GetPitch();
-    pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
-    for (int x = 0; x < width; ++x) {
-      unsigned index = src_row[x];
-      if (index >= palette.size()) {
-        index = 0;
-      }
-      dst_row[x] = palette[index];
-    }
-  }
-  return image_builder;
-}
-
 static void DebugValidate(const RetainPtr<CFX_DIBitmap>& bitmap,
                           const RetainPtr<CFX_DIBitmap>& device) {
   if (bitmap) {
@@ -774,90 +610,6 @@
   paint->setBlendMode(GetSkiaBlendMode(blend_type));
 }
 
-sk_sp<SkImage> Upsample(const RetainPtr<CFX_DIBBase>& source,
-                        bool force_alpha) {
-  const uint8_t* const buffer = source->GetBuffer().data();
-  if (!buffer) {
-    return nullptr;
-  }
-
-  const SkColorType color_type = force_alpha || source->IsMaskFormat()
-                                     ? SkColorType::kAlpha_8_SkColorType
-                                     : SkColorType::kGray_8_SkColorType;
-  const int width = source->GetWidth();
-  const int height = source->GetHeight();
-  const int row_bytes = source->GetPitch();
-  SkiaImageBuilder image_builder(source, color_type);
-  switch (source->GetBPP()) {
-    case 1: {
-      // By default, the two colors for grayscale are 0xFF and 0x00 unless they
-      // are specified in the palette.
-      uint8_t color0 = 0x00;
-      uint8_t color1 = 0xFF;
-
-      if (source->GetFormat() == FXDIB_Format::k1bppRgb &&
-          source->HasPalette()) {
-        uint32_t palette_color0 = source->GetPaletteArgb(0);
-        uint32_t palette_color1 = source->GetPaletteArgb(1);
-        bool use_gray_colors = IsRGBColorGrayScale(palette_color0) &&
-                               IsRGBColorGrayScale(palette_color1);
-        if (!use_gray_colors) {
-          image_builder = CreateSkiaImageBuilderUsingSingleBitPalette(source);
-          break;
-        }
-
-        color0 = FXARGB_R(palette_color0);
-        color1 = FXARGB_R(palette_color1);
-      }
-
-      image_builder = SkiaImageBuilder(
-          SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType));
-      pdfium::span<uint8_t> dst8_pixels = image_builder.allocated_pixels8();
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* src_row = buffer + y * row_bytes;
-        pdfium::span<uint8_t> dst_row =
-            dst8_pixels.subspan(y * image_builder.row_bytes());
-        for (int x = 0; x < width; ++x)
-          dst_row[x] = src_row[x >> 3] & (1 << (~x & 0x07)) ? color1 : color0;
-      }
-      break;
-    }
-    case 8:
-      // we upscale ctables to 32bit.
-      if (source->HasPalette()) {
-        const size_t src_palette_size = source->GetRequiredPaletteSize();
-        pdfium::span<const uint32_t> src_palette = source->GetPaletteSpan();
-        CHECK_LE(src_palette_size, src_palette.size());
-        if (src_palette_size < src_palette.size())
-          src_palette = src_palette.first(src_palette_size);
-
-        image_builder = CreateSkiaImageBuilderUsingPalette(source, src_palette);
-      }
-      break;
-    case 24: {
-      image_builder = SkiaImageBuilder(SkImageInfo::Make(
-          width, height, kBGRA_8888_SkColorType, kOpaque_SkAlphaType));
-      pdfium::span<uint32_t> dst32_pixels = image_builder.allocated_pixels32();
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* src_row = buffer + y * row_bytes;
-        pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
-        for (int x = 0; x < width; ++x) {
-          dst_row[x] = SkPackARGB32NoCheck(
-              0xFF, src_row[x * 3 + 2], src_row[x * 3 + 1], src_row[x * 3 + 0]);
-        }
-      }
-      break;
-    }
-    case 32:
-      image_builder = SkiaImageBuilder(source, kBGRA_8888_SkColorType);
-      break;
-    default:
-      NOTREACHED();
-  }
-
-  return std::move(image_builder).Build();
-}
-
 // Makes a bitmap filled with a solid color for debugging with `SkPicture`.
 RetainPtr<CFX_DIBitmap> MakeDebugBitmap(int width, int height, uint32_t color) {
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
@@ -1205,7 +957,7 @@
     return false;
   }
 
-  sk_sp<SkImage> skia_mask = Upsample(mask, /*force_alpha=*/true);
+  sk_sp<SkImage> skia_mask = mask->RealizeSkImage(/*force_alpha=*/true);
   if (!skia_mask) {
     return false;
   }
@@ -1765,12 +1517,12 @@
     BlendMode blend_type) {
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
 
-  sk_sp<SkImage> skia_source = Upsample(pSource, /*force_alpha=*/false);
+  sk_sp<SkImage> skia_source = pSource->RealizeSkImage(/*force_alpha=*/false);
   if (!skia_source) {
     return false;
   }
 
-  sk_sp<SkImage> skia_mask = Upsample(pMask, /*force_alpha=*/true);
+  sk_sp<SkImage> skia_mask = pMask->RealizeSkImage(/*force_alpha=*/true);
   if (!skia_mask) {
     return false;
   }
@@ -1834,7 +1586,7 @@
     BlendMode blend_type) {
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
 
-  sk_sp<SkImage> skia_source = Upsample(pSource, /*force_alpha=*/false);
+  sk_sp<SkImage> skia_source = pSource->RealizeSkImage(/*force_alpha=*/false);
   if (!skia_source) {
     return false;
   }