Add Rust PNG codec backend for testing and remove Skia PNG encoder

Replace the legacy Skia-based PNG backend in testing/png_codec with
a pure Rust backend using the png crate via CXX bindings:
- Add testing/png_codec/png_codec_rust_ffi.rs implementing PNG
  encoding (BGR, BGRA, RGBA, Gray) and decoding via the png crate.
- Add testing/png_codec/png_codec_rust.cpp implementing the
  png_codec C++ API using the Rust FFI.
- Delete testing/png_codec/png_codec_skia.cpp, decoupling PNG
  testing code completely from Skia.
- Update testing/png_codec/BUILD.gn to switch between the Rust PNG
  and libpng backends based on pdf_enable_rust_png.
- Update testing/png_codec/DEPS.

TAG=agy
CONV=db6cb163-190b-47d4-bbdc-b519626ce208

Change-Id: I60a69517a58f9d6d53990daca30782d0c3b20004
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/156030
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/testing/png_codec/BUILD.gn b/testing/png_codec/BUILD.gn
index 2afa986..5b1d794 100644
--- a/testing/png_codec/BUILD.gn
+++ b/testing/png_codec/BUILD.gn
@@ -5,14 +5,35 @@
 import("../../pdfium.gni")
 import("../../testing/test.gni")
 
+if (pdf_enable_rust_png) {
+  import("//build/rust/rust_static_library.gni")
+
+  rust_static_library("png_codec_rust_ffi") {
+    testonly = true
+    crate_name = "png_codec_rust_ffi"
+    sources = [ "png_codec_rust_ffi.rs" ]
+    crate_root = "png_codec_rust_ffi.rs"
+    cxx_bindings = [ "png_codec_rust_ffi.rs" ]
+    allow_unsafe = true
+    deps = [
+      "//build/rust:cxx_cppdeps",
+      "//third_party/rust/png/v0_18:lib",
+    ]
+  }
+}
+
 source_set("png_codec") {
   testonly = true
   sources = [ "png_codec.h" ]
   deps = []
 
-  if (pdf_use_skia) {
-    sources += [ "png_codec_skia.cpp" ]
-    deps += [ "//skia" ]
+  if (pdf_enable_rust_png) {
+    sources += [ "png_codec_rust.cpp" ]
+    include_dirs = [ "$target_gen_dir/../.." ]
+    deps += [
+      ":png_codec_rust_ffi",
+      "//build/rust:cxx_cppdeps",
+    ]
   } else {
     sources += [ "png_codec_libpng.cpp" ]
     deps += [
diff --git a/testing/png_codec/DEPS b/testing/png_codec/DEPS
index a9cc1c6..17b552a 100644
--- a/testing/png_codec/DEPS
+++ b/testing/png_codec/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
   '+third_party/libpng',
-  '+third_party/skia/include',
+  '+third_party/rust/png',
   '+third_party/zlib',
 ]
diff --git a/testing/png_codec/png_codec_rust.cpp b/testing/png_codec/png_codec_rust.cpp
new file mode 100644
index 0000000..8d5cf1f
--- /dev/null
+++ b/testing/png_codec/png_codec_rust.cpp
@@ -0,0 +1,89 @@
+// Copyright 2026 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/png_codec/png_codec.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "core/fxcrt/numerics/safe_conversions.h"
+#include "core/fxcrt/span.h"
+#include "core/fxcrt/span_util.h"
+#include "testing/png_codec/png_codec_rust_ffi.rs.h"
+
+namespace png_codec {
+
+std::vector<uint8_t> Decode(pdfium::span<const uint8_t> input,
+                            bool reverse_byte_order,
+                            int* width,
+                            int* height) {
+  rust::Slice<const uint8_t> src_slice(input);
+  int32_t w = 0;
+  int32_t h = 0;
+  rust::Vec<uint8_t> decoded =
+      rust_png::decode_png(src_slice, reverse_byte_order, w, h);
+  if (w <= 0 || h <= 0 || decoded.empty()) {
+    return {};
+  }
+  *width = w;
+  *height = h;
+  return {decoded.begin(), decoded.end()};
+}
+
+std::vector<uint8_t> EncodeBGR(pdfium::span<const uint8_t> input,
+                               int width,
+                               int height,
+                               int row_byte_width) {
+  rust::Slice<const uint8_t> src_slice(input);
+  rust::Vec<uint8_t> encoded = rust_png::encode_bgr(
+      src_slice, width, height, pdfium::checked_cast<size_t>(row_byte_width));
+  if (encoded.empty()) {
+    return {};
+  }
+  return {encoded.begin(), encoded.end()};
+}
+
+std::vector<uint8_t> EncodeRGBA(pdfium::span<const uint8_t> input,
+                                int width,
+                                int height,
+                                int row_byte_width) {
+  rust::Slice<const uint8_t> src_slice(input);
+  rust::Vec<uint8_t> encoded = rust_png::encode_rgba(
+      src_slice, width, height, pdfium::checked_cast<size_t>(row_byte_width));
+  if (encoded.empty()) {
+    return {};
+  }
+  return {encoded.begin(), encoded.end()};
+}
+
+std::vector<uint8_t> EncodeBGRA(pdfium::span<const uint8_t> input,
+                                int width,
+                                int height,
+                                int row_byte_width,
+                                bool discard_transparency) {
+  rust::Slice<const uint8_t> src_slice(input);
+  rust::Vec<uint8_t> encoded = rust_png::encode_bgra(
+      src_slice, width, height, pdfium::checked_cast<size_t>(row_byte_width),
+      discard_transparency);
+  if (encoded.empty()) {
+    return {};
+  }
+  return {encoded.begin(), encoded.end()};
+}
+
+std::vector<uint8_t> EncodeGray(pdfium::span<const uint8_t> input,
+                                int width,
+                                int height,
+                                int row_byte_width) {
+  rust::Slice<const uint8_t> src_slice(input);
+  rust::Vec<uint8_t> encoded = rust_png::encode_gray(
+      src_slice, width, height, pdfium::checked_cast<size_t>(row_byte_width));
+  if (encoded.empty()) {
+    return {};
+  }
+  return {encoded.begin(), encoded.end()};
+}
+
+}  // namespace png_codec
diff --git a/testing/png_codec/png_codec_rust_ffi.rs b/testing/png_codec/png_codec_rust_ffi.rs
new file mode 100644
index 0000000..0dfa81d
--- /dev/null
+++ b/testing/png_codec/png_codec_rust_ffi.rs
@@ -0,0 +1,322 @@
+// Copyright 2026 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::io::Cursor;
+
+#[cxx::bridge(namespace = "png_codec::rust_png")]
+mod ffi {
+    extern "Rust" {
+        fn decode_png(
+            src: &[u8],
+            reverse_byte_order: bool,
+            width: &mut i32,
+            height: &mut i32,
+        ) -> Vec<u8>;
+
+        fn encode_bgr(input: &[u8], width: i32, height: i32, row_byte_width: usize) -> Vec<u8>;
+
+        fn encode_rgba(input: &[u8], width: i32, height: i32, row_byte_width: usize) -> Vec<u8>;
+
+        fn encode_bgra(
+            input: &[u8],
+            width: i32,
+            height: i32,
+            row_byte_width: usize,
+            discard_transparency: bool,
+        ) -> Vec<u8>;
+
+        fn encode_gray(input: &[u8], width: i32, height: i32, row_byte_width: usize) -> Vec<u8>;
+    }
+}
+
+fn decode_png(
+    src: &[u8],
+    reverse_byte_order: bool,
+    width_out: &mut i32,
+    height_out: &mut i32,
+) -> Vec<u8> {
+    *width_out = 0;
+    *height_out = 0;
+
+    let cursor = Cursor::new(src);
+    let mut decoder = png::Decoder::new(cursor);
+    decoder.set_transformations(png::Transformations::EXPAND | png::Transformations::STRIP_16);
+    let mut reader = match decoder.read_info() {
+        Ok(r) => r,
+        Err(_) => return Vec::new(),
+    };
+    let buf_size = match reader.output_buffer_size() {
+        Some(s) => s,
+        None => return Vec::new(),
+    };
+    let mut buf = vec![0; buf_size];
+    let output_info = match reader.next_frame(&mut buf) {
+        Ok(info) => info,
+        Err(_) => return Vec::new(),
+    };
+    let width = output_info.width as usize;
+    let height = output_info.height as usize;
+    if width == 0 || height == 0 {
+        return Vec::new();
+    }
+    let line_size = output_info.line_size;
+    if line_size < width {
+        return Vec::new();
+    }
+    let bytes_per_pixel = line_size / width;
+    if bytes_per_pixel == 0 || bytes_per_pixel > 4 {
+        return Vec::new();
+    }
+    if let Some(total_src_bytes) = line_size.checked_mul(height) {
+        if total_src_bytes > buf.len() {
+            return Vec::new();
+        }
+    } else {
+        return Vec::new();
+    }
+
+    let out_size = match width.checked_mul(height).and_then(|wh| wh.checked_mul(4)) {
+        Some(s) => s,
+        None => return Vec::new(),
+    };
+    let mut out = vec![0u8; out_size];
+
+    // Standard default file gamma for sRGB (1.0 / 2.2).
+    const DEFAULT_FILE_GAMMA: f64 = 0.45455;
+    let file_gamma: f64 = if reader.info().srgb.is_some() {
+        DEFAULT_FILE_GAMMA
+    } else if let Some(gama) = reader.info().gama_chunk {
+        let val = gama.into_value() as f64;
+        if val > 0.0 {
+            val
+        } else {
+            DEFAULT_FILE_GAMMA
+        }
+    } else {
+        DEFAULT_FILE_GAMMA
+    };
+
+    const DISPLAY_GAMMA: f64 = 2.2;
+    let mut gamma_lut = [0u8; 256];
+    let exponent = 1.0 / (file_gamma * DISPLAY_GAMMA);
+    if (exponent - 1.0).abs() < 1e-4 {
+        for (i, item) in gamma_lut.iter_mut().enumerate() {
+            *item = i as u8;
+        }
+    } else {
+        for (i, item) in gamma_lut.iter_mut().enumerate() {
+            let v = (i as f64) / 255.0;
+            *item = (v.powf(exponent) * 255.0).round().clamp(0.0, 255.0) as u8;
+        }
+    }
+
+    for y in 0..height {
+        let src_start = y * line_size;
+        let src_row = &buf[src_start..src_start + line_size];
+        let dst_start = y * width * 4;
+        let dst_slice = &mut out[dst_start..dst_start + width * 4];
+
+        for x in 0..width {
+            let (r, g, b, a) = match bytes_per_pixel {
+                1 => {
+                    let gray = gamma_lut[src_row[x] as usize];
+                    (gray, gray, gray, 255)
+                }
+                2 => {
+                    let gray = gamma_lut[src_row[x * 2] as usize];
+                    let alpha = src_row[x * 2 + 1];
+                    (gray, gray, gray, alpha)
+                }
+                3 => {
+                    let red = gamma_lut[src_row[x * 3] as usize];
+                    let green = gamma_lut[src_row[x * 3 + 1] as usize];
+                    let blue = gamma_lut[src_row[x * 3 + 2] as usize];
+                    (red, green, blue, 255)
+                }
+                4 => {
+                    let red = gamma_lut[src_row[x * 4] as usize];
+                    let green = gamma_lut[src_row[x * 4 + 1] as usize];
+                    let blue = gamma_lut[src_row[x * 4 + 2] as usize];
+                    let alpha = src_row[x * 4 + 3];
+                    (red, green, blue, alpha)
+                }
+                _ => return Vec::new(),
+            };
+
+            if reverse_byte_order {
+                dst_slice[x * 4] = b;
+                dst_slice[x * 4 + 1] = g;
+                dst_slice[x * 4 + 2] = r;
+                dst_slice[x * 4 + 3] = a;
+            } else {
+                dst_slice[x * 4] = r;
+                dst_slice[x * 4 + 1] = g;
+                dst_slice[x * 4 + 2] = b;
+                dst_slice[x * 4 + 3] = a;
+            }
+        }
+    }
+
+    *width_out = width as i32;
+    *height_out = height as i32;
+    out
+}
+
+fn write_png(data: &[u8], width: u32, height: u32, color_type: png::ColorType) -> Vec<u8> {
+    let mut out = Vec::new();
+    {
+        let mut encoder = png::Encoder::new(&mut out, width, height);
+        encoder.set_color(color_type);
+        encoder.set_depth(png::BitDepth::Eight);
+        let mut writer = match encoder.write_header() {
+            Ok(w) => w,
+            Err(_) => return Vec::new(),
+        };
+        if writer.write_image_data(data).is_err() {
+            return Vec::new();
+        }
+    }
+    out
+}
+
+fn encode_bgr(input: &[u8], width: i32, height: i32, row_byte_width: usize) -> Vec<u8> {
+    if width <= 0 || height <= 0 {
+        return Vec::new();
+    }
+    let w = width as usize;
+    let h = height as usize;
+    if row_byte_width < w * 3 {
+        return Vec::new();
+    }
+    if let Some(total_input) = row_byte_width.checked_mul(h) {
+        if total_input > input.len() {
+            return Vec::new();
+        }
+    } else {
+        return Vec::new();
+    }
+
+    let mut rgb = vec![0u8; w * h * 3];
+    for y in 0..h {
+        let src_row = &input[y * row_byte_width..y * row_byte_width + w * 3];
+        let dst_row = &mut rgb[y * w * 3..(y + 1) * w * 3];
+        for x in 0..w {
+            dst_row[x * 3] = src_row[x * 3 + 2]; // R
+            dst_row[x * 3 + 1] = src_row[x * 3 + 1]; // G
+            dst_row[x * 3 + 2] = src_row[x * 3]; // B
+        }
+    }
+    write_png(&rgb, width as u32, height as u32, png::ColorType::Rgb)
+}
+
+fn encode_rgba(input: &[u8], width: i32, height: i32, row_byte_width: usize) -> Vec<u8> {
+    if width <= 0 || height <= 0 {
+        return Vec::new();
+    }
+    let w = width as usize;
+    let h = height as usize;
+    if row_byte_width < w * 4 {
+        return Vec::new();
+    }
+    if let Some(total_input) = row_byte_width.checked_mul(h) {
+        if total_input > input.len() {
+            return Vec::new();
+        }
+    } else {
+        return Vec::new();
+    }
+
+    if row_byte_width == w * 4 {
+        return write_png(&input[..w * h * 4], width as u32, height as u32, png::ColorType::Rgba);
+    }
+
+    let mut rgba = vec![0u8; w * h * 4];
+    for y in 0..h {
+        let src_row = &input[y * row_byte_width..y * row_byte_width + w * 4];
+        let dst_row = &mut rgba[y * w * 4..(y + 1) * w * 4];
+        dst_row.copy_from_slice(src_row);
+    }
+    write_png(&rgba, width as u32, height as u32, png::ColorType::Rgba)
+}
+
+fn encode_bgra(
+    input: &[u8],
+    width: i32,
+    height: i32,
+    row_byte_width: usize,
+    discard_transparency: bool,
+) -> Vec<u8> {
+    if width <= 0 || height <= 0 {
+        return Vec::new();
+    }
+    let w = width as usize;
+    let h = height as usize;
+    if row_byte_width < w * 4 {
+        return Vec::new();
+    }
+    if let Some(total_input) = row_byte_width.checked_mul(h) {
+        if total_input > input.len() {
+            return Vec::new();
+        }
+    } else {
+        return Vec::new();
+    }
+
+    if discard_transparency {
+        let mut rgb = vec![0u8; w * h * 3];
+        for y in 0..h {
+            let src_row = &input[y * row_byte_width..y * row_byte_width + w * 4];
+            let dst_row = &mut rgb[y * w * 3..(y + 1) * w * 3];
+            for x in 0..w {
+                dst_row[x * 3] = src_row[x * 4 + 2]; // R
+                dst_row[x * 3 + 1] = src_row[x * 4 + 1]; // G
+                dst_row[x * 3 + 2] = src_row[x * 4]; // B
+            }
+        }
+        write_png(&rgb, width as u32, height as u32, png::ColorType::Rgb)
+    } else {
+        let mut rgba = vec![0u8; w * h * 4];
+        for y in 0..h {
+            let src_row = &input[y * row_byte_width..y * row_byte_width + w * 4];
+            let dst_row = &mut rgba[y * w * 4..(y + 1) * w * 4];
+            for x in 0..w {
+                dst_row[x * 4] = src_row[x * 4 + 2]; // R
+                dst_row[x * 4 + 1] = src_row[x * 4 + 1]; // G
+                dst_row[x * 4 + 2] = src_row[x * 4]; // B
+                dst_row[x * 4 + 3] = src_row[x * 4 + 3]; // A
+            }
+        }
+        write_png(&rgba, width as u32, height as u32, png::ColorType::Rgba)
+    }
+}
+
+fn encode_gray(input: &[u8], width: i32, height: i32, row_byte_width: usize) -> Vec<u8> {
+    if width <= 0 || height <= 0 {
+        return Vec::new();
+    }
+    let w = width as usize;
+    let h = height as usize;
+    if row_byte_width < w {
+        return Vec::new();
+    }
+    if let Some(total_input) = row_byte_width.checked_mul(h) {
+        if total_input > input.len() {
+            return Vec::new();
+        }
+    } else {
+        return Vec::new();
+    }
+
+    if row_byte_width == w {
+        return write_png(&input[..w * h], width as u32, height as u32, png::ColorType::Grayscale);
+    }
+
+    let mut gray = vec![0u8; w * h];
+    for y in 0..h {
+        let src_row = &input[y * row_byte_width..y * row_byte_width + w];
+        let dst_row = &mut gray[y * w..(y + 1) * w];
+        dst_row.copy_from_slice(src_row);
+    }
+    write_png(&gray, width as u32, height as u32, png::ColorType::Grayscale)
+}
diff --git a/testing/png_codec/png_codec_skia.cpp b/testing/png_codec/png_codec_skia.cpp
deleted file mode 100644
index 2e1b7ed..0000000
--- a/testing/png_codec/png_codec_skia.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2025 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/check_op.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/numerics/checked_math.h"
-#include "core/fxcrt/span.h"
-#include "core/fxcrt/span_util.h"
-#include "testing/png_codec/png_codec.h"
-#include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/core/SkColorType.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/core/SkStream.h"
-
-#ifdef PDF_ENABLE_RUST_PNG
-#include "third_party/skia/include/codec/SkPngRustDecoder.h"
-#include "third_party/skia/include/encode/SkPngRustEncoder.h"
-#else
-#include "third_party/skia/include/codec/SkPngDecoder.h"
-#include "third_party/skia/include/encode/SkPngEncoder.h"
-#endif
-
-namespace png_codec {
-
-namespace {
-
-std::vector<uint8_t> EncodeHelper(pdfium::span<const uint8_t> input,
-                                  SkColorType color,
-                                  SkAlphaType alpha,
-                                  int width,
-                                  int height,
-                                  size_t row_byte_width) {
-  SkImageInfo info =
-      SkImageInfo::Make(width, height, color, alpha, SkColorSpace::MakeSRGB());
-  CHECK_NE(info.minRowBytes(), 0);  // 0 means conversion problems.
-  CHECK_LE(info.minRowBytes(), row_byte_width);
-  CHECK_NE(info.computeMinByteSize(), 0);  // 0 means conversion problems.
-  CHECK_LE(info.computeMinByteSize(), input.size());
-  SkPixmap pixmap(info, input.data(), row_byte_width);
-
-  SkDynamicMemoryWStream output;
-#ifdef PDF_ENABLE_RUST_PNG
-  bool success = SkPngRustEncoder::Encode(&output, pixmap, {});
-#else
-  bool success = SkPngEncoder::Encode(&output, pixmap, {});
-#endif
-  if (!success) {
-    return {};
-  }
-  return output.detachAsVector();
-}
-
-}  // namespace
-
-std::vector<uint8_t> Decode(pdfium::span<const uint8_t> input,
-                            bool reverse_byte_order,
-                            int* width,
-                            int* height) {
-  CHECK(width);
-  CHECK(height);
-
-  auto stream = std::make_unique<SkMemoryStream>(input.data(), input.size(),
-                                                 /*copyData=*/false);
-#ifdef PDF_ENABLE_RUST_PNG
-  std::unique_ptr<SkCodec> codec =
-      SkPngRustDecoder::Decode(std::move(stream), nullptr);
-#else
-  std::unique_ptr<SkCodec> codec =
-      SkPngDecoder::Decode(std::move(stream), nullptr);
-#endif
-  if (!codec) {
-    return {};
-  }
-
-  SkColorType format =
-      reverse_byte_order ? kBGRA_8888_SkColorType : kRGBA_8888_SkColorType;
-  SkImageInfo info = codec->getInfo();
-  info = info.makeColorType(format);
-  info = info.makeColorSpace(SkColorSpace::MakeSRGB());
-
-  std::vector<uint8_t> output;
-  output.resize(info.computeMinByteSize());
-
-  SkCodec::Result result =
-      codec->getPixels(info, output.data(), info.minRowBytes());
-  if (result != SkCodec::kSuccess) {
-    return {};
-  }
-
-  *width = info.width();
-  *height = info.height();
-  return output;
-}
-
-std::vector<uint8_t> EncodeBGR(pdfium::span<const uint8_t> bgr_input,
-                               int width,
-                               int height,
-                               int row_byte_width) {
-  // Check inputs.  Expected values are manually calculated (instead of using
-  // `SkImageInfo`'s `computeMinByteSize` and/or `minRowBytes`), because
-  // `SkColorType` doesn't cover a format with 3 bytes per pixel (bpp) - e.g.
-  // `kRGB_565_SkColorType` is 2 bpp and `kRGB_888x_SkColorType` is 4 bpp.
-  size_t row_byte_width_as_size_t =
-      pdfium::checked_cast<size_t>(row_byte_width);
-  FX_SAFE_SIZE_T expected_minimum_row_byte_width = 3;
-  expected_minimum_row_byte_width *= width;
-  CHECK_LE(expected_minimum_row_byte_width.ValueOrDie(),
-           row_byte_width_as_size_t);
-
-  FX_SAFE_SIZE_T expected_minimum_input_size = row_byte_width_as_size_t;
-  expected_minimum_input_size *= height;
-  CHECK_LE(expected_minimum_input_size.ValueOrDie(), bgr_input.size());
-
-  // Convert `bgr_input` into `intermediate_bgra_buf` (because Skia doesn't
-  // allow encoding BGR pixels - see the comment at the top of the function
-  // that talks about limitations of `SkColorType`).
-  SkImageInfo intermediate_bgra_info =
-      SkImageInfo::Make(width, height, kBGRA_8888_SkColorType,
-                        kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
-  size_t intermediate_bgra_row_byte_width =
-      intermediate_bgra_info.minRowBytes();
-  CHECK_NE(0,
-           intermediate_bgra_row_byte_width);  // 0 means conversion problems.
-  std::vector<uint8_t> intermediate_bgra_buf;
-  intermediate_bgra_buf.resize(intermediate_bgra_info.computeMinByteSize());
-  CHECK_NE(0, intermediate_bgra_buf.size());  // 0 means conversion problems.
-  {
-    pdfium::span<const uint8_t> src = bgr_input;
-    pdfium::span<uint8_t> dst = intermediate_bgra_buf;
-    size_t height_as_size_t = pdfium::checked_cast<size_t>(height);
-    size_t width_as_size_t = pdfium::checked_cast<size_t>(width);
-    for (size_t y = 0; y < height_as_size_t; y++) {
-      for (size_t x = 0; x < width_as_size_t; x++) {
-        // If `computeMinByteSize` didn't report an error (`0`), then integer
-        // overflow won't happen in the `x * N` expressions below.
-        pdfium::span<uint8_t> dst_pixel = dst.subspan(x * 4u).first(4u);
-        pdfium::span<const uint8_t> src_pixel = src.subspan(x * 3u).first(3u);
-
-        fxcrt::spancpy(dst_pixel.first(3u), src_pixel);  // Copy BGR channels.
-        dst_pixel[3] = 0xFF;  // Set alpha channel to "opaque".
-      }
-      src = src.subspan(row_byte_width_as_size_t);
-      dst = dst.subspan(intermediate_bgra_row_byte_width);
-    }
-  }
-
-  return EncodeHelper(intermediate_bgra_buf, kBGRA_8888_SkColorType,
-                      kOpaque_SkAlphaType, width, height,
-                      intermediate_bgra_row_byte_width);
-}
-
-std::vector<uint8_t> EncodeRGBA(pdfium::span<const uint8_t> input,
-                                int width,
-                                int height,
-                                int row_byte_width) {
-  return EncodeHelper(input, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType,
-                      width, height,
-                      pdfium::checked_cast<size_t>(row_byte_width));
-}
-
-std::vector<uint8_t> EncodeBGRA(pdfium::span<const uint8_t> input,
-                                int width,
-                                int height,
-                                int row_byte_width,
-                                bool discard_transparency) {
-  return EncodeHelper(
-      input, kBGRA_8888_SkColorType,
-      discard_transparency ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType, width,
-      height, pdfium::checked_cast<size_t>(row_byte_width));
-}
-
-std::vector<uint8_t> EncodeGray(pdfium::span<const uint8_t> input,
-                                int width,
-                                int height,
-                                int row_byte_width) {
-  return EncodeHelper(input, kGray_8_SkColorType, kOpaque_SkAlphaType, width,
-                      height, pdfium::checked_cast<size_t>(row_byte_width));
-}
-
-}  // namespace png_codec