blob: cd208f55d9a003ab33cd3e5b00526f3e3d7865af [file] [log] [blame]
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Test several transforms.
#include <cstdint>
#include <memory>
#include <string>
#include <tuple>
#include "imageio/image_dec.h"
#include "src/common/lossless/transforms.h"
#include "src/enc/lossless/backward_references_enc.h"
#include "src/enc/lossless/losslessi_enc.h"
#include "src/utils/utils.h"
#include "src/wp2/base.h"
#include "src/wp2/decode.h"
#include "src/wp2/encode.h"
#include "src/wp2/format_constants.h"
#include "tests/include/helpers.h"
namespace WP2 {
class CrossColorGlobalTest
: public testing::TestWithParam<std::tuple<
WP2L::TransformHeader::Channel, WP2L::TransformHeader::Channel,
WP2L::TransformHeader::CCTransform,
WP2L::TransformHeader::CCTransform>> {};
// Tests the cross color global transform.
TEST_P(CrossColorGlobalTest, Pattern) {
constexpr char kSource1_64x48[] = "source1_64x48.png";
// First and second channel have to be different.
if (std::get<0>(GetParam()) == std::get<1>(GetParam())) return;
ArgbBuffer original(WP2_ARGB_32);
ASSERT_WP2_OK(
ReadImage(testutil::GetTestDataPath(kSource1_64x48).c_str(), &original));
// Define the encoder config for lossless, including kCrossColorGlobal.
EncoderConfig encoder_config;
encoder_config.alpha_quality = 100;
encoder_config.quality = 100;
encoder_config.keep_unmultiplied = true;
encoder_config.lossless_config =
std::make_shared<WP2::EncoderConfig::LosslessCrunchConfig>();
WP2L::CrunchConfig &config = *encoder_config.lossless_config;
config.algorithm = WP2L::EncodingAlgorithm::kWebP;
config.transform_bits = config.histo_bits = 5;
config.lz77s_types_to_try[0] = WP2L::kLZ77Standard;
config.lz77s_types_to_try_size = 1;
config.transforms[0].type = WP2L::TransformType::kSubtractGreen;
config.transforms[1].type = WP2L::TransformType::kPredictor;
config.transforms[2].type = WP2L::TransformType::kCrossColorGlobal;
config.transforms[2].cc_global_first_channel = std::get<0>(GetParam());
config.transforms[2].cc_global_second_channel = std::get<1>(GetParam());
config.transforms[2].cc_global_second_transform = std::get<2>(GetParam());
config.transforms[2].cc_global_third_transform = std::get<3>(GetParam());
MemoryWriter memory_writer;
WP2_ASSERT_STATUS(Encode(original, &memory_writer, encoder_config));
DecoderConfig decoder_config;
ArgbBuffer decompressed(WP2_ARGB_32);
WP2_ASSERT_STATUS(Decode(memory_writer.mem_, memory_writer.size_,
&decompressed, decoder_config));
EXPECT_TRUE(testutil::Compare(original, decompressed,
/*file_name=*/"cross_color_global"));
}
INSTANTIATE_TEST_SUITE_P(
TransformsTestInstantiation, CrossColorGlobalTest,
testing::Combine(
testing::Values(WP2L::TransformHeader::Channel::kRed,
WP2L::TransformHeader::Channel::kGreen,
WP2L::TransformHeader::Channel::kBlue),
testing::Values(WP2L::TransformHeader::Channel::kRed,
WP2L::TransformHeader::Channel::kGreen,
WP2L::TransformHeader::Channel::kBlue),
testing::Values(WP2L::TransformHeader::CCTransform::kNothing,
WP2L::TransformHeader::CCTransform::kSubtractFirst),
testing::Values(WP2L::TransformHeader::CCTransform::kNothing,
WP2L::TransformHeader::CCTransform::kSubtractFirst,
WP2L::TransformHeader::CCTransform::kSubtractSecond,
WP2L::TransformHeader::CCTransform::kSubtractAverage)));
TEST(GenericTest, InvalidTransformCombinations) {
// Create a 2x2 image with a single color.
ArgbBuffer original(WP2_ARGB_32);
constexpr uint32_t dim = 2;
WP2_ASSERT_STATUS(original.Resize(dim, dim));
original.Fill(Argb32b{.a = 255, .r = 26, .g = 4, .b = 79});
// Create a config with a non-existent recipe.
EncoderConfig encoder_config;
encoder_config.alpha_quality = 100;
encoder_config.quality = 100;
encoder_config.lossless_config =
std::make_shared<WP2::EncoderConfig::LosslessCrunchConfig>();
WP2L::CrunchConfig &config = *encoder_config.lossless_config;
config.algorithm = WP2L::EncodingAlgorithm::kGroup4;
config.transforms[0].type = WP2L::TransformType::kNum;
config.transforms[1].type = WP2L::TransformType::kNum;
config.transforms[2].type = WP2L::TransformType::kNum;
MemoryWriter memory_writer;
ASSERT_EQ(Encode(original, &memory_writer, encoder_config),
WP2_STATUS_INVALID_CONFIGURATION);
// Make the recipe valid but that does not work because there is only one
// color.
config.transforms[0].type = WP2L::TransformType::kColorIndexing;
ASSERT_EQ(Encode(original, &memory_writer, encoder_config),
WP2_STATUS_INVALID_PARAMETER);
}
// Tests the cross color global transform.
TEST(PerChannelIndexingTest, Pattern) {
constexpr char kSource1_64x48[] = "source1_64x48.png";
ArgbBuffer full_original(WP2_ARGB_32);
ASSERT_WP2_OK(ReadImage(testutil::GetTestDataPath(kSource1_64x48).c_str(),
&full_original));
for (int test_case = 0; test_case < 2; ++test_case) {
ArgbBuffer original(WP2_ARGB_32);
if (test_case == 0) {
WP2_ASSERT_STATUS(original.SetView(full_original));
} else {
// Trigger the non-palette code.
WP2_ASSERT_STATUS(
original.SetView(full_original, WP2::Rectangle(0, 0, 10, 10)));
}
// Define the encoder config for lossless, including kCrossColorGlobal.
EncoderConfig encoder_config;
encoder_config.alpha_quality = 100;
encoder_config.quality = 100;
encoder_config.keep_unmultiplied = true;
encoder_config.lossless_config =
std::make_shared<WP2::EncoderConfig::LosslessCrunchConfig>();
WP2L::CrunchConfig &config = *encoder_config.lossless_config;
config.algorithm = WP2L::EncodingAlgorithm::kWebP;
config.transform_bits = config.histo_bits = 5;
config.lz77s_types_to_try[0] = WP2L::kLZ77Standard;
config.lz77s_types_to_try_size = 1;
config.transforms[0].type = WP2L::TransformType::kNormalizeChannels;
config.transforms[1].type = WP2L::TransformType::kPredictor;
config.transforms[2].type = WP2L::TransformType::kNum;
MemoryWriter memory_writer;
WP2_ASSERT_STATUS(Encode(original, &memory_writer, encoder_config));
DecoderConfig decoder_config;
ArgbBuffer decompressed(WP2_ARGB_32);
WP2_ASSERT_STATUS(Decode(memory_writer.mem_, memory_writer.size_,
&decompressed, decoder_config));
EXPECT_TRUE(testutil::Compare(original, decompressed,
/*file_name=*/"cross_color_global"));
}
}
} // namespace WP2