[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