| // Copyright 2019 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 for ArgbBuffer |
| // |
| // Author: Skal (pascal.massimino@gmail.com) |
| |
| #include <array> |
| #include <string> |
| #include <tuple> |
| #include <vector> |
| |
| #include "examples/example_utils.h" |
| #include "include/helpers.h" |
| #include "include/helpers_filter.h" |
| #include "src/wp2/decode.h" |
| #include "src/common/vdebug.h" |
| |
| namespace WP2 { |
| namespace { |
| |
| //------------------------------------------------------------------------------ |
| // Test that some functions clear metadata, some don't. |
| |
| TEST(BufferTest, Metadata) { |
| std::array<uint8_t, 128> metadata{0}; |
| std::array<uint8_t, 64 * 4> pixels8{0}; |
| std::array<uint16_t, 64 * 4> pixels16{0}; |
| const uint8_t color8[] = {255, 1, 2, 3}; |
| const uint16_t color16[] = {255, 1, 2, 3}; |
| for (uint32_t format_ind = 0; format_ind < (uint32_t)WP2_FORMAT_NUM; |
| ++format_ind) { |
| const WP2SampleFormat format = (WP2SampleFormat)format_ind; |
| ArgbBuffer buffer(format); |
| |
| ASSERT_WP2_OK( |
| buffer.metadata_.exif.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| // SetExternal() should clear the metadata. |
| if (WP2Formatbpc(format) <= 8) { |
| ASSERT_WP2_OK(buffer.SetExternal(8, 8, pixels8.data(), 8 * 4)); |
| } else { |
| ASSERT_WP2_OK( |
| buffer.SetExternal(8, 8, pixels16.data(), 8 * 4 * sizeof(uint16_t))); |
| } |
| ASSERT_TRUE(buffer.metadata_.IsEmpty()); |
| |
| ASSERT_WP2_OK( |
| buffer.metadata_.iccp.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| ASSERT_WP2_OK(buffer.SetView(buffer, {1, 1, 1, 1})); |
| ASSERT_TRUE(buffer.IsView()); |
| ASSERT_FALSE( |
| buffer.metadata_.IsEmpty()); // Kept metadata because same buffer |
| |
| ArgbBuffer other_buffer(format); |
| ASSERT_WP2_OK(other_buffer.Resize(2, 2)); |
| ASSERT_WP2_OK( |
| other_buffer.metadata_.iccp.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_WP2_OK(buffer.SetView(other_buffer, {1, 1, 1, 1})); |
| ASSERT_EQ(other_buffer.SetView(buffer), WP2_STATUS_INVALID_PARAMETER); |
| ASSERT_TRUE(buffer.metadata_.IsEmpty()); |
| |
| // Resize() should clear the buffer anew, including metadata. |
| ASSERT_WP2_OK( |
| buffer.metadata_.xmp.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| ASSERT_EQ(buffer.Resize(8, 8), |
| WP2_STATUS_BAD_DIMENSION); // Not exactly same |
| ASSERT_WP2_OK(buffer.Resize(buffer.width(), buffer.height())); |
| ASSERT_TRUE(buffer.IsView()); |
| ASSERT_TRUE(buffer.metadata_.IsEmpty()); |
| |
| ASSERT_WP2_OK( |
| buffer.metadata_.exif.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| buffer.Deallocate(); |
| ASSERT_TRUE(buffer.metadata_.IsEmpty()); |
| |
| ASSERT_WP2_OK( |
| buffer.metadata_.exif.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| ASSERT_WP2_OK(buffer.Resize(16, 16)); |
| ASSERT_TRUE(buffer.metadata_.IsEmpty()); |
| |
| if (WP2Formatbpc(format) <= 8) { |
| other_buffer.Fill({0, 0, 2, 2}, color8); |
| } else { |
| other_buffer.Fill({0, 0, 2, 2}, color16); |
| } |
| ASSERT_WP2_OK( |
| buffer.metadata_.exif.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| ASSERT_WP2_OK(buffer.CopyFrom(other_buffer)); |
| ASSERT_TRUE( |
| buffer.metadata_.IsEmpty()); // Clears and doesn't copy metadata |
| |
| ArgbBuffer other_format( |
| (WP2SampleFormat)((format_ind == 0) ? 1 : format_ind - 1)); |
| ASSERT_WP2_OK(other_format.Resize(2, 2)); |
| ASSERT_WP2_OK( |
| other_format.metadata_.iccp.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_WP2_OK( |
| buffer.metadata_.iccp.CopyFrom(metadata.data(), metadata.size())); |
| ASSERT_FALSE(other_format.metadata_.IsEmpty()); |
| if (buffer.format() == WP2_Argb_32 && |
| WP2Formatbpc(other_format.format()) <= 8) { |
| ASSERT_WP2_OK(other_format.ConvertFrom(buffer)); |
| ASSERT_TRUE(other_format.metadata_.IsEmpty()); // Doesn't copy metadata |
| } |
| |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| buffer.Deallocate(); |
| ASSERT_TRUE(buffer.metadata_.IsEmpty()); |
| ASSERT_FALSE(other_buffer.metadata_.IsEmpty()); |
| ASSERT_WP2_OK(buffer.Swap(&other_buffer)); |
| ASSERT_FALSE(buffer.metadata_.IsEmpty()); |
| ASSERT_TRUE(other_buffer.metadata_.IsEmpty()); |
| } |
| } |
| |
| TEST(BufferTest, Fill) { |
| const Argb32b color = Argb32b{200, 30, 200, 0}; |
| |
| ArgbBuffer premultiplied(WP2_Argb_32); |
| ASSERT_WP2_OK(premultiplied.Resize(10, 20)); |
| premultiplied.Fill(color); |
| EXPECT_THAT_FIRST_ELEMENTS_ARE(premultiplied.GetRow8(0), 200, 30, 200, 0); |
| |
| ArgbBuffer unmultiplied(WP2_ARGB_32); |
| ASSERT_WP2_OK(unmultiplied.Resize(10, 20)); |
| unmultiplied.Fill(color); |
| EXPECT_THAT_FIRST_ELEMENTS_ARE(unmultiplied.GetRow8(0), 200, 38, 255, 0); |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| class BufferTest |
| : public testing::TestWithParam<std::tuple<std::string, bool, bool>> {}; |
| |
| WP2Status Decode(const uint8_t* data, size_t data_size, |
| ArgbBuffer* output, bool use_decoder) { |
| if (use_decoder) { |
| ArrayDecoder decoder(data, data_size, DecoderConfig::kDefault, output); |
| const bool decoded_frame = decoder.ReadFrame(); |
| return decoder.Failed() ? decoder.GetStatus() |
| : decoded_frame ? WP2_STATUS_OK |
| : WP2_STATUS_NOT_ENOUGH_DATA; |
| } |
| return Decode(data, data_size, output); |
| } |
| |
| TEST_P(BufferTest, API) { |
| const std::string filename = std::get<0>(GetParam()); |
| const float quality = std::get<1>(GetParam()) ? 100.f : 75.f; |
| const int effort = 2; |
| const bool use_decoder = std::get<2>(GetParam()); |
| ArgbBuffer src; |
| MemoryWriter data; |
| ASSERT_WP2_OK( |
| testutil::CompressImage(filename, &data, &src, quality, effort)); |
| EXPECT_FALSE(src.IsView()); |
| ASSERT_WP2_OK(src.SetView(src, {1, 1, src.width() / 2, src.height() / 2})); |
| EXPECT_FALSE(src.IsView()); // We still own the memory. |
| |
| ArgbBuffer output1; |
| ASSERT_WP2_OK(Decode(data.mem_, data.size_, &output1, use_decoder)); |
| |
| ArgbBuffer output2; |
| // Try a too small buffer |
| ASSERT_WP2_OK(output2.SetView( |
| output1, {0, 0, output1.width() / 2, output1.height() / 2})); |
| ASSERT_EQ(Decode(data.mem_, data.size_, &output2, use_decoder), |
| WP2_STATUS_BAD_DIMENSION); |
| EXPECT_FALSE(testutil::Compare(output1, output2, filename)); |
| // Try a tightly fit one |
| ASSERT_WP2_OK(output2.SetView(output1)); |
| EXPECT_TRUE(output2.IsView()); |
| ASSERT_WP2_OK(Decode(data.mem_, data.size_, &output2, use_decoder)); |
| EXPECT_TRUE(output2.IsView()); // should decode directly in the view |
| EXPECT_TRUE(testutil::Compare(output1, output2, filename)); |
| |
| // And then a large internal one |
| output2.Deallocate(); |
| ASSERT_WP2_OK(output2.Resize(output1.width() * 2, output1.height() * 3)); |
| ASSERT_WP2_OK(Decode(data.mem_, data.size_, &output2, use_decoder)); |
| EXPECT_TRUE(testutil::Compare(output1, output2, filename)); |
| |
| // And a too large external one |
| ASSERT_WP2_OK(output1.Resize(output1.width() * 2, output1.height() * 3)); |
| ASSERT_WP2_OK(output2.SetView(output1)); |
| ASSERT_EQ(Decode(data.mem_, data.size_, &output2, use_decoder), |
| WP2_STATUS_BAD_DIMENSION); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| BufferTestInstantiation1, BufferTest, |
| testing::Combine(testing::Values("source1.png"), |
| testing::Values(true, false) /* lossless */, |
| testing::Values(true, false) /* use_decoder */)); |
| |
| //------------------------------------------------------------------------------ |
| |
| TEST(BufferTest, Composite) { |
| ArgbBuffer buffer(WP2_Argb_32); |
| ASSERT_WP2_OK(buffer.Resize(4, 1)); |
| buffer.Fill({0, 0, 1, 1}, Argb32b({255, 0, 30, 60})); |
| buffer.Fill({1, 0, 1, 1}, Argb32b({200, 120, 30, 60})); |
| buffer.Fill({2, 0, 1, 1}, Argb32b({30, 20, 25, 0})); |
| buffer.Fill({3, 0, 1, 1}, Argb32b({0, 0, 0, 0})); |
| |
| EXPECT_TRUE(buffer.HasTransparency()); |
| |
| ASSERT_WP2_OK(buffer.CompositeOver({127, 13, 57, 116})); |
| |
| uint8_t* row = buffer.GetRow8(0); |
| |
| EXPECT_TRUE(buffer.HasTransparency()); |
| |
| EXPECT_EQ(row[4 * 0 + 0], 255); |
| EXPECT_EQ(row[4 * 0 + 1], 0); |
| EXPECT_EQ(row[4 * 0 + 2], 30); |
| EXPECT_EQ(row[4 * 0 + 3], 60); |
| |
| EXPECT_EQ(row[4 * 1 + 0], 227); |
| EXPECT_EQ(row[4 * 1 + 1], 123); |
| EXPECT_EQ(row[4 * 1 + 2], 42); |
| EXPECT_EQ(row[4 * 1 + 3], 85); |
| |
| EXPECT_EQ(row[4 * 2 + 0], 142); |
| EXPECT_EQ(row[4 * 2 + 1], 31); |
| EXPECT_EQ(row[4 * 2 + 2], 75); |
| EXPECT_EQ(row[4 * 2 + 3], 102); |
| |
| EXPECT_EQ(row[4 * 3 + 0], 127); |
| EXPECT_EQ(row[4 * 3 + 1], 13); |
| EXPECT_EQ(row[4 * 3 + 2], 57); |
| EXPECT_EQ(row[4 * 3 + 3], 116); |
| |
| ASSERT_WP2_OK(buffer.CompositeOver({255, 0, 0, 0})); |
| |
| EXPECT_FALSE(buffer.HasTransparency()); |
| |
| EXPECT_EQ(row[4 * 0 + 0], 255); |
| EXPECT_EQ(row[4 * 0 + 1], 0); |
| EXPECT_EQ(row[4 * 0 + 2], 30); |
| EXPECT_EQ(row[4 * 0 + 3], 60); |
| |
| EXPECT_EQ(row[4 * 1 + 0], 255); |
| EXPECT_EQ(row[4 * 1 + 1], 123); |
| EXPECT_EQ(row[4 * 1 + 2], 42); |
| EXPECT_EQ(row[4 * 1 + 3], 85); |
| |
| EXPECT_EQ(row[4 * 2 + 0], 255); |
| EXPECT_EQ(row[4 * 2 + 1], 31); |
| EXPECT_EQ(row[4 * 2 + 2], 75); |
| EXPECT_EQ(row[4 * 2 + 3], 102); |
| |
| EXPECT_EQ(row[4 * 3 + 0], 255); |
| EXPECT_EQ(row[4 * 3 + 1], 13); |
| EXPECT_EQ(row[4 * 3 + 2], 57); |
| EXPECT_EQ(row[4 * 3 + 3], 116); |
| } |
| |
| TEST(BufferTest, CompositeOnBuffer) { |
| ArgbBuffer buffer(WP2_Argb_32); |
| ASSERT_WP2_OK(buffer.Resize(4, 2)); |
| buffer.Fill({0, 0, 1, 2}, Argb32b({255, 0, 30, 60})); |
| buffer.Fill({1, 0, 1, 2}, Argb32b({200, 120, 30, 60})); |
| buffer.Fill({2, 0, 1, 2}, Argb32b({30, 20, 25, 0})); |
| buffer.Fill({3, 0, 1, 2}, Argb32b({0, 0, 0, 0})); |
| ArgbBuffer buffer2(WP2_Argb_32); |
| ASSERT_WP2_OK(buffer2.CopyFrom(buffer)); |
| |
| ArgbBuffer bg_buffer(WP2_Argb_32); |
| ASSERT_WP2_OK(bg_buffer.Resize(4, 2)); |
| bg_buffer.Fill({0, 0, 4, 1}, Argb32b({127, 13, 57, 116})); |
| bg_buffer.Fill({0, 1, 4, 1}, Argb32b({255, 0, 0, 0})); |
| ArgbBuffer bg_buffer2(WP2_Argb_32); |
| ASSERT_WP2_OK(bg_buffer2.CopyFrom(bg_buffer)); |
| |
| ASSERT_WP2_OK(buffer.CompositeOver(bg_buffer)); |
| ASSERT_WP2_OK(bg_buffer2.CompositeUnder(buffer2)); |
| |
| ASSERT_TRUE(testutil::Compare(buffer, bg_buffer2, "buffer")); |
| |
| uint8_t* row = buffer.GetRow8(0); |
| |
| EXPECT_EQ(row[4 * 0 + 0], 255); |
| EXPECT_EQ(row[4 * 0 + 1], 0); |
| EXPECT_EQ(row[4 * 0 + 2], 30); |
| EXPECT_EQ(row[4 * 0 + 3], 60); |
| |
| EXPECT_EQ(row[4 * 1 + 0], 227); |
| EXPECT_EQ(row[4 * 1 + 1], 123); |
| EXPECT_EQ(row[4 * 1 + 2], 42); |
| EXPECT_EQ(row[4 * 1 + 3], 85); |
| |
| EXPECT_EQ(row[4 * 2 + 0], 142); |
| EXPECT_EQ(row[4 * 2 + 1], 31); |
| EXPECT_EQ(row[4 * 2 + 2], 75); |
| EXPECT_EQ(row[4 * 2 + 3], 102); |
| |
| EXPECT_EQ(row[4 * 3 + 0], 127); |
| EXPECT_EQ(row[4 * 3 + 1], 13); |
| EXPECT_EQ(row[4 * 3 + 2], 57); |
| EXPECT_EQ(row[4 * 3 + 3], 116); |
| |
| row = buffer.GetRow8(1); |
| |
| EXPECT_EQ(row[4 * 0 + 0], 255); |
| EXPECT_EQ(row[4 * 0 + 1], 0); |
| EXPECT_EQ(row[4 * 0 + 2], 30); |
| EXPECT_EQ(row[4 * 0 + 3], 60); |
| |
| EXPECT_EQ(row[4 * 1 + 0], 255); |
| EXPECT_EQ(row[4 * 1 + 1], 120); |
| EXPECT_EQ(row[4 * 1 + 2], 30); |
| EXPECT_EQ(row[4 * 1 + 3], 60); |
| |
| EXPECT_EQ(row[4 * 2 + 0], 255); |
| EXPECT_EQ(row[4 * 2 + 1], 20); |
| EXPECT_EQ(row[4 * 2 + 2], 25); |
| EXPECT_EQ(row[4 * 2 + 3], 0); |
| |
| EXPECT_EQ(row[4 * 3 + 0], 255); |
| EXPECT_EQ(row[4 * 3 + 1], 0); |
| EXPECT_EQ(row[4 * 3 + 2], 0); |
| EXPECT_EQ(row[4 * 3 + 3], 0); |
| } |
| |
| TEST(BufferTest, CompositeYuv) { |
| ArgbBuffer foreground(WP2_Argb_32); |
| const uint32_t width = 100; |
| const uint32_t height = 50; |
| ASSERT_WP2_OK(foreground.Resize(width, height)); |
| foreground.Fill({128, 128, 128, 128}); |
| testutil::Noise(/*seed=*/1, /*strength=*/128, &foreground); |
| |
| ArgbBuffer background(WP2_Argb_32); |
| ASSERT_WP2_OK(background.Resize(width, height)); |
| background.Fill({128, 128, 128, 128}); |
| testutil::Noise(/*seed=*/2, /*strength=*/128, &background); |
| |
| CSPTransform csp; |
| static constexpr int16_t kYuvToRgb[] = { |
| 1188, 0, 1638, 1188, -399, -829, 1188, 2058, 0, |
| }; |
| static constexpr int16_t kRgbAvg[] = {-100, 50, 110}; |
| ASSERT_TRUE(csp.Init(kYuvToRgb, kRgbAvg)); |
| YUVPlane yuv_foreground; |
| ASSERT_WP2_OK(yuv_foreground.Import(foreground, /*import_alpha=*/true, csp, |
| /*resize_if_needed=*/true)); |
| YUVPlane yuv_background; |
| ASSERT_WP2_OK(yuv_background.Import(background, /*import_alpha=*/true, csp, |
| /*resize_if_needed=*/true)); |
| |
| // Composite yuv foreground on yuv background. |
| YUVPlane yuv_composite_over; |
| ASSERT_WP2_OK( |
| yuv_composite_over.Copy(yuv_foreground, /*resize_if_needed=*/true)); |
| ASSERT_WP2_OK(yuv_composite_over.CompositeOver(yuv_background, csp)); |
| |
| // Composite yuv background under yuv foreground. |
| YUVPlane yuv_composite_under; |
| ASSERT_WP2_OK( |
| yuv_composite_under.Copy(yuv_background, /*resize_if_needed=*/true)); |
| ASSERT_WP2_OK(yuv_composite_under.CompositeUnder(yuv_foreground, csp)); |
| |
| // Composite in rgb then convert to yuv. |
| ASSERT_WP2_OK(foreground.CompositeOver(background)); |
| YUVPlane yuv_imported_frop_rgb; |
| ASSERT_WP2_OK(yuv_imported_frop_rgb.Import(foreground, /*import_alpha=*/true, |
| csp, |
| /*resize_if_needed=*/true)); |
| |
| // All three methods should give the same result. |
| // CompositeOVer/CompositeUnder. |
| EXPECT_TRUE(testutil::Compare(yuv_composite_over, yuv_composite_under, |
| /*bit_depth=*/{16, /*is_signed=*/true}, "test", |
| 99)); |
| // Composite in RGB vs YUV. We allow for some rounding error. |
| for (uint32_t y = 0; y < height; ++y) { |
| for (uint32_t x = 0; x < width; ++x) { |
| SCOPED_TRACE(SPrintf("x %d y %d", x, y)); |
| EXPECT_NEAR(yuv_composite_over.Y.At(x, y), |
| yuv_imported_frop_rgb.Y.At(x, y), 3); |
| EXPECT_NEAR(yuv_composite_over.U.At(x, y), |
| yuv_imported_frop_rgb.U.At(x, y), 3); |
| EXPECT_NEAR(yuv_composite_over.V.At(x, y), |
| yuv_imported_frop_rgb.V.At(x, y), 3); |
| EXPECT_EQ(yuv_composite_over.A.At(x, y), |
| yuv_imported_frop_rgb.A.At(x, y)); |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| TEST(BufferTest, GetDistortion_BlackSquare) { |
| // Transparent background. |
| const uint8_t transparent_8[] = {0, 0, 0, 0}; |
| const uint16_t transparent_16[] = {0, 0, 0, 0}; |
| // Black square. |
| const uint8_t black_8[] = {255, 0, 0, 0}; |
| const uint16_t black_16[] = {255, 0, 0, 0}; |
| // Transparent black square. |
| const uint8_t black_transp_8[] = {250, 0, 0, 0}; |
| const uint16_t black_transp_16[] = {250, 0, 0, 0}; |
| for (WP2SampleFormat format : {WP2_Argb_32, WP2_Argb_38}) { |
| ArgbBuffer orig(format); |
| ASSERT_WP2_OK(orig.Resize(100, 100)); |
| // Opaque black square on a transparent background. |
| if (WP2Formatbpc(format) <= 8) { |
| orig.Fill(/*window=*/{0, 0, 100, 100}, transparent_8); |
| orig.Fill(/*window=*/{25, 25, 50, 50}, black_8); |
| } else { |
| orig.Fill(/*window=*/{0, 0, 100, 100}, transparent_16); |
| orig.Fill(/*window=*/{25, 25, 50, 50}, black_16); |
| } |
| |
| ArgbBuffer compressed(format); |
| ASSERT_WP2_OK(compressed.Resize(100, 100)); |
| // Slightly transparent black square on a transparent background. |
| if (WP2Formatbpc(format) <= 8) { |
| compressed.Fill(/*window=*/{0, 0, 100, 100}, transparent_8); |
| compressed.Fill(/*window=*/{25, 25, 50, 50}, black_transp_8); |
| } else { |
| compressed.Fill(/*window=*/{0, 0, 100, 100}, transparent_16); |
| compressed.Fill(/*window=*/{25, 25, 50, 50}, black_transp_16); |
| } |
| |
| for (uint32_t metric = 0; metric < NUM_METRIC_TYPES; ++metric) { |
| // Not yet implemented. |
| if (format == WP2_Argb_38 && (MetricType)metric != PSNR) continue; |
| SCOPED_TRACE(SPrintf("metric %s", kMetricNames[metric])); |
| float result[5]; |
| ASSERT_WP2_OK(compressed.GetDistortion(orig, (MetricType)metric, result)); |
| EXPECT_LT(result[0], 50.f); // a |
| EXPECT_EQ(result[1], 99.f); // r (or y for PSNRHVS) |
| EXPECT_EQ(result[2], 99.f); // g (or u for PSNRHVS) |
| EXPECT_EQ(result[3], 99.f); // b (or v for PSNRHVS) |
| if (metric == MetricType::PSNR_YUV) { |
| EXPECT_LT(result[4], 60.f); // all |
| } else { |
| EXPECT_LT(result[4], 50.f); // all |
| } |
| // Not yet implemented. |
| if (format == WP2_Argb_38) continue; |
| ASSERT_WP2_OK(compressed.GetDistortionBlackOrWhiteBackground( |
| orig, (MetricType)metric, result)); |
| |
| EXPECT_LT(result[0], 50.f); // a |
| if (metric == MetricType::PSNRHVS || metric == MetricType::PSNR_YUV || |
| metric == MetricType::SSIM_YUV) { |
| if (metric == MetricType::PSNR_YUV) { |
| EXPECT_EQ(result[1], 99.f); // y |
| } else { |
| EXPECT_LT(result[1], 40.f); // y |
| } |
| EXPECT_EQ(result[2], 99.f); // u |
| EXPECT_EQ(result[3], 99.f); // v |
| } else { |
| EXPECT_LT(result[1], 50.f); // r |
| EXPECT_LT(result[2], 50.f); // g |
| EXPECT_LT(result[3], 50.f); // b |
| } |
| EXPECT_LT(result[4], 50.f); // all |
| } |
| } |
| } |
| |
| TEST(BufferTest, GetDistortion_WhiteSquare) { |
| ArgbBuffer orig(WP2_Argb_32); |
| ASSERT_WP2_OK(orig.Resize(100, 100)); |
| // Opaque white square on a transparent background. |
| orig.Fill({0, 0, 100, 100}, Argb32b({0, 0, 0, 0})); |
| orig.Fill({25, 25, 50, 50}, Argb32b({255, 255, 255, 255})); |
| |
| ArgbBuffer compressed(WP2_Argb_32); |
| ASSERT_WP2_OK(compressed.Resize(100, 100)); |
| // Slightly transparent white square on a transparent background. |
| compressed.Fill({0, 0, 100, 100}, Argb32b({0, 0, 0, 0})); |
| compressed.Fill({25, 25, 50, 50}, Argb32b({250, 250, 250, 250})); |
| |
| for (uint32_t metric = 0; metric < NUM_METRIC_TYPES; ++metric) { |
| SCOPED_TRACE(SPrintf("metric %s", kMetricNames[metric])); |
| float result[5]; |
| ASSERT_WP2_OK(compressed.GetDistortion(orig, (MetricType)metric, result)); |
| |
| EXPECT_LT(result[0], 50.f); // a |
| if (metric == MetricType::PSNRHVS || metric == MetricType::PSNR_YUV || |
| metric == MetricType::SSIM_YUV) { |
| EXPECT_LT(result[1], 50.f); // y |
| EXPECT_EQ(result[2], 99.f); // u |
| EXPECT_EQ(result[3], 99.f); // v |
| } else { |
| EXPECT_LT(result[1], 50.f); // r |
| EXPECT_LT(result[2], 50.f); // g |
| EXPECT_LT(result[3], 50.f); // b |
| } |
| EXPECT_LT(result[4], 50.f); // all |
| |
| ASSERT_WP2_OK(compressed.GetDistortionBlackOrWhiteBackground( |
| orig, (MetricType)metric, result)); |
| |
| EXPECT_LT(result[0], 50.f); // a |
| if (metric == MetricType::PSNRHVS || metric == MetricType::PSNR_YUV || |
| metric == MetricType::SSIM_YUV) { |
| EXPECT_LT(result[1], 50.f); // y |
| EXPECT_EQ(result[2], 99.f); // u |
| EXPECT_EQ(result[3], 99.f); // v |
| } else { |
| EXPECT_LT(result[1], 50.f); // r |
| EXPECT_LT(result[2], 50.f); // g |
| EXPECT_LT(result[3], 50.f); // b |
| } |
| EXPECT_LT(result[4], 50.f); // all |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| TEST(RectangleTest, Contains) { |
| Rectangle r1{5, 0, 10, 12}; |
| EXPECT_TRUE(r1.Contains(5, 0)); |
| EXPECT_TRUE(r1.Contains(6, 0)); |
| EXPECT_TRUE(r1.Contains(8, 11)); |
| EXPECT_TRUE(r1.Contains(14, 11)); |
| EXPECT_FALSE(r1.Contains(4, 0)); |
| EXPECT_FALSE(r1.Contains(15, 2)); |
| EXPECT_FALSE(r1.Contains(7, 12)); |
| EXPECT_FALSE(r1.Contains(15, 12)); |
| |
| EXPECT_TRUE(r1.Contains(r1)); |
| EXPECT_TRUE(r1.Contains({5, 0, 0, 0})); |
| EXPECT_TRUE(r1.Contains({6, 2, 3, 7})); |
| EXPECT_TRUE(r1.Contains({6, 2, 9, 7})); |
| EXPECT_FALSE(r1.Contains({6, 2, 10, 7})); |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| #if !defined(WP2_REDUCE_BINARY_SIZE) |
| |
| TEST(BufferTest, TestPrint) { |
| std::string msg; |
| msg.resize(256); |
| for (int c = 0; c < 256; ++c) msg[c] = c ^ 42; |
| |
| ArgbBuffer buf(WP2_Argb_32); |
| for (int s = 1; s <= 8; ++s) { |
| ASSERT_WP2_OK(buf.Resize(256 * s, 100)); |
| const int x = s * 7 - 14, y = s * 13 - 27; |
| // just check it doesn't crash: |
| Print(msg, x, y, Argb32b{255, 32, 51, 12}, &buf); |
| } |
| } |
| |
| #endif // WP2_REDUCE_BINARY_SIZE |
| |
| //------------------------------------------------------------------------------ |
| |
| } // namespace |
| } // namespace WP2 |