1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5 
6 #include "lib/jxl/blending.h"
7 
8 #include "gmock/gmock.h"
9 #include "gtest/gtest.h"
10 #include "lib/extras/codec.h"
11 #include "lib/jxl/dec_file.h"
12 #include "lib/jxl/image_test_utils.h"
13 #include "lib/jxl/testdata.h"
14 
15 namespace jxl {
16 namespace {
17 
18 using ::testing::SizeIs;
19 
TEST(BlendingTest,Crops)20 TEST(BlendingTest, Crops) {
21   ThreadPool* pool = nullptr;
22 
23   const PaddedBytes compressed =
24       ReadTestData("jxl/blending/cropped_traffic_light.jxl");
25   DecompressParams dparams;
26   CodecInOut decoded;
27   ASSERT_TRUE(DecodeFile(dparams, compressed, &decoded, pool));
28   ASSERT_THAT(decoded.frames, SizeIs(4));
29 
30   int i = 0;
31   for (const ImageBundle& ib : decoded.frames) {
32     std::ostringstream filename;
33     filename << "jxl/blending/cropped_traffic_light_frame-" << i << ".png";
34     const PaddedBytes compressed_frame = ReadTestData(filename.str());
35     CodecInOut frame;
36     ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(compressed_frame), &frame));
37     EXPECT_TRUE(SamePixels(ib.color(), *frame.Main().color()));
38     ++i;
39   }
40 }
41 
TEST(BlendingTest,Offset)42 TEST(BlendingTest, Offset) {
43   const PaddedBytes background_bytes = ReadTestData("jxl/splines.png");
44   CodecInOut background;
45   ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(background_bytes), &background));
46   const PaddedBytes foreground_bytes =
47       ReadTestData("jxl/grayscale_patches.png");
48   CodecInOut foreground;
49   ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(foreground_bytes), &foreground));
50 
51   ImageBlender blender;
52   ImageBundle output;
53   CodecMetadata nonserialized_metadata;
54   ASSERT_TRUE(
55       nonserialized_metadata.size.Set(background.xsize(), background.ysize()));
56   PassesSharedState state;
57   state.frame_header.blending_info.mode = BlendMode::kReplace;
58   state.frame_header.blending_info.source = 0;
59   state.frame_header.nonserialized_metadata = &nonserialized_metadata;
60   state.metadata = &background.metadata;
61   state.reference_frames[0].frame = &background.Main();
62   PassesDecoderState dec_state;
63   dec_state.shared = &state;
64   const FrameOrigin foreground_origin = {-50, -50};
65   ASSERT_TRUE(blender.PrepareBlending(&dec_state, foreground_origin,
66                                       foreground.xsize(), foreground.ysize(),
67                                       background.Main().c_current(), &output));
68 
69   static constexpr int kStep = 20;
70   for (size_t x0 = 0; x0 < foreground.xsize(); x0 += kStep) {
71     for (size_t y0 = 0; y0 < foreground.ysize(); y0 += kStep) {
72       const Rect rect =
73           Rect(x0, y0, kStep, kStep).Intersection(Rect(foreground.Main()));
74       Image3F foreground_crop(rect.xsize(), rect.ysize());
75       CopyImageTo(rect, *foreground.Main().color(), Rect(foreground_crop),
76                   &foreground_crop);
77       auto rect_blender =
78           blender.PrepareRect(rect, foreground_crop, {}, Rect(foreground_crop));
79       for (size_t y = 0; y < rect.ysize(); ++y) {
80         ASSERT_TRUE(rect_blender.DoBlending(y));
81       }
82     }
83   }
84 
85   const PaddedBytes expected_bytes =
86       ReadTestData("jxl/blending/grayscale_patches_on_splines.png");
87   CodecInOut expected;
88   ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(expected_bytes), &expected));
89   VerifyRelativeError(*expected.Main().color(), *output.color(), 1. / (2 * 255),
90                       0);
91 }
92 
93 }  // namespace
94 }  // namespace jxl
95