[rust png] Fix handling of `discard_transparency` in `EncodeBGRAPNG`.
https://pdfium-review.googlesource.com/c/pdfium/+/136570 has landed an
implementation of `EncodeBGRAPNG` that ignores `discard_transparency`.
This behavior was different from the old, `libpng`-based implementation
as shown by the new `EncodeBGRAPNGAndDiscardTransparency` test where
the last assertion (`EXPECT_EQ(rgba_output[3], 255)`) would pass with
`libpng`-based implementation but (before this CL) fail with the
Rust-based implementation.
Bug: 444045690
Change-Id: Ifa894e36ab0f568aeea8a4421bc8ed9010f23d6b
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/138891
Auto-Submit: Ćukasz Anforowicz <lukasza@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index a929c27..df7110c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -293,6 +293,7 @@
"fpdfsdk/*",
"fxbarcode/*",
"fxjs/*",
+ "testing/image_diff/*",
"xfa/*",
]
}
@@ -315,6 +316,7 @@
"core/fxge:unittests",
"fpdfsdk:unittests",
"testing:unit_test_support",
+ "testing/image_diff:unittests",
"//testing/gmock",
"//testing/gtest",
]
diff --git a/testing/image_diff/BUILD.gn b/testing/image_diff/BUILD.gn
index c104767..6706ba2 100644
--- a/testing/image_diff/BUILD.gn
+++ b/testing/image_diff/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("../../pdfium.gni")
+import("../../testing/test.gni")
source_set("image_diff") {
testonly = true
@@ -26,3 +27,9 @@
]
public_deps = [ "../../core/fxcrt" ]
}
+
+pdfium_unittest_source_set("unittests") {
+ sources = [ "image_diff_png_unittests.cpp" ]
+ deps = [ ":image_diff" ]
+ pdfium_root_dir = "../../"
+}
diff --git a/testing/image_diff/image_diff_png_skia.cpp b/testing/image_diff/image_diff_png_skia.cpp
index cc78334..64ce821 100644
--- a/testing/image_diff/image_diff_png_skia.cpp
+++ b/testing/image_diff/image_diff_png_skia.cpp
@@ -169,8 +169,10 @@
int height,
int row_byte_width,
bool discard_transparency) {
- return EncodePNG(input, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, width,
- height, pdfium::checked_cast<size_t>(row_byte_width));
+ return EncodePNG(
+ input, kBGRA_8888_SkColorType,
+ discard_transparency ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType, width,
+ height, pdfium::checked_cast<size_t>(row_byte_width));
}
std::vector<uint8_t> EncodeGrayPNG(pdfium::span<const uint8_t> input,
diff --git a/testing/image_diff/image_diff_png_unittests.cpp b/testing/image_diff/image_diff_png_unittests.cpp
new file mode 100644
index 0000000..b92c66b
--- /dev/null
+++ b/testing/image_diff/image_diff_png_unittests.cpp
@@ -0,0 +1,132 @@
+// 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 "testing/image_diff/image_diff_png.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace image_diff_png {
+
+namespace {
+
+// Test helper to decode `input` bytes into RGBA output, expecting a 1x1 image.
+//
+// When successful, a vector of exactly 4 bytes is returned.
+// An empty vector indicates that an error has occurred.
+std::vector<uint8_t> Decode1x1PNGToRGBA(pdfium::span<const uint8_t> input) {
+ constexpr bool kReverseByteOrder = false; // This asks for RGBA output.
+
+ int output_width = -1;
+ int output_height = -1;
+ std::vector<uint8_t> rgba_output =
+ DecodePNG(input, kReverseByteOrder, &output_width, &output_height);
+ EXPECT_EQ(output_height, 1);
+ EXPECT_EQ(output_width, 1);
+ return rgba_output;
+}
+
+} // namespace
+
+TEST(ImageDiffPng, EncodeBGRAPNGAndDiscardTransparency) {
+ const std::vector<uint8_t> bgrx_input = {1, 2, 3, /*ignored_alpha=*/4};
+ std::vector<uint8_t> png = EncodeBGRAPNG(bgrx_input,
+ /*width=*/1,
+ /*height=*/1,
+ /*row_byte_width=*/4,
+ /*discard_transparency=*/true);
+ ASSERT_FALSE(png.empty());
+
+ std::vector<uint8_t> rgba_output = Decode1x1PNGToRGBA(png);
+ ASSERT_EQ(rgba_output.size(), 4u);
+
+ // Verifying that the RGB / BGR pixels are preserved.
+ EXPECT_EQ(rgba_output[0], bgrx_input[2]);
+ EXPECT_EQ(rgba_output[1], bgrx_input[1]);
+ EXPECT_EQ(rgba_output[2], bgrx_input[0]);
+
+ // Verifying that the decoded pixels are fully opaque
+ // (i.e. that the `4` in input got discarded / ignored).
+ EXPECT_EQ(rgba_output[3], 255);
+}
+
+TEST(ImageDiffPng, EncodeBGRAPNGAndPreserveTransparency) {
+ const std::vector<uint8_t> bgra_input = {1, 2, 3, 4};
+ std::vector<uint8_t> png = EncodeBGRAPNG(bgra_input,
+ /*width=*/1,
+ /*height=*/1,
+ /*row_byte_width=*/4,
+ /*discard_transparency=*/false);
+ ASSERT_FALSE(png.empty());
+
+ std::vector<uint8_t> rgba_output = Decode1x1PNGToRGBA(png);
+ ASSERT_EQ(rgba_output.size(), 4u);
+
+ // Verifying that all the RGBA / BGRA pixels are preserved.
+ EXPECT_EQ(rgba_output[0], bgra_input[2]);
+ EXPECT_EQ(rgba_output[1], bgra_input[1]);
+ EXPECT_EQ(rgba_output[2], bgra_input[0]);
+ EXPECT_EQ(rgba_output[3], bgra_input[3]);
+}
+
+TEST(ImageDiffPng, EncodeBGRPNG) {
+ std::vector<uint8_t> bgr_input = {1, 2, 3};
+ std::vector<uint8_t> png = EncodeBGRPNG(bgr_input,
+ /*width=*/1,
+ /*height=*/1,
+ /*row_byte_width=*/3);
+ ASSERT_FALSE(png.empty());
+
+ std::vector<uint8_t> rgba_output = Decode1x1PNGToRGBA(png);
+ ASSERT_EQ(rgba_output.size(), 4u);
+
+ // Verifying that all the RGB / BGR pixels are preserved.
+ EXPECT_EQ(rgba_output[0], bgr_input[2]);
+ EXPECT_EQ(rgba_output[1], bgr_input[1]);
+ EXPECT_EQ(rgba_output[2], bgr_input[0]);
+
+ // Verifying that alpha is opaque.
+ EXPECT_EQ(rgba_output[3], 255);
+}
+
+TEST(ImageDiffPng, EncodeRGBAPNG) {
+ std::vector<uint8_t> rgba_input = {1, 2, 3, 4};
+ std::vector<uint8_t> png = EncodeRGBAPNG(rgba_input,
+ /*width=*/1,
+ /*height=*/1,
+ /*row_byte_width=*/4);
+ ASSERT_FALSE(png.empty());
+
+ std::vector<uint8_t> rgba_output = Decode1x1PNGToRGBA(png);
+ ASSERT_EQ(rgba_output.size(), 4u);
+
+ // Verifying that all the RGBA values are preserved.
+ EXPECT_EQ(rgba_output[0], rgba_input[0]);
+ EXPECT_EQ(rgba_output[1], rgba_input[1]);
+ EXPECT_EQ(rgba_output[2], rgba_input[2]);
+ EXPECT_EQ(rgba_output[3], rgba_input[3]);
+}
+
+TEST(ImageDiffPng, EncodeGrayPNG) {
+ std::vector<uint8_t> grayscale_input = {123};
+ std::vector<uint8_t> png = EncodeGrayPNG(grayscale_input,
+ /*width=*/1,
+ /*height=*/1,
+ /*row_byte_width=*/1);
+ ASSERT_FALSE(png.empty());
+
+ std::vector<uint8_t> rgba_output = Decode1x1PNGToRGBA(png);
+ ASSERT_EQ(rgba_output.size(), 4u);
+
+ // Verifying that all the RGBA values correspond to the input.
+ EXPECT_EQ(rgba_output[0], grayscale_input[0]); // R=gray
+ EXPECT_EQ(rgba_output[1], grayscale_input[0]); // G=gray
+ EXPECT_EQ(rgba_output[2], grayscale_input[0]); // B=gray
+ EXPECT_EQ(rgba_output[3], 255); // A=opaque
+}
+
+} // namespace image_diff_png