1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "gtest/gtest.h"
8 
9 #include "mozilla/gfx/2D.h"
10 #include "Common.h"
11 #include "Decoder.h"
12 #include "DecoderFactory.h"
13 #include "SourceBuffer.h"
14 #include "SurfaceFilters.h"
15 #include "SurfacePipe.h"
16 
17 using namespace mozilla;
18 using namespace mozilla::gfx;
19 using namespace mozilla::image;
20 
21 template <typename Func>
WithDownscalingFilter(const IntSize & aInputSize,const IntSize & aOutputSize,Func aFunc)22 void WithDownscalingFilter(const IntSize& aInputSize,
23                            const IntSize& aOutputSize, Func aFunc) {
24   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
25   ASSERT_TRUE(decoder != nullptr);
26 
27   WithFilterPipeline(
28       decoder, std::forward<Func>(aFunc),
29       DownscalingConfig{aInputSize, SurfaceFormat::OS_RGBA},
30       SurfaceConfig{decoder, aOutputSize, SurfaceFormat::OS_RGBA, false});
31 }
32 
AssertConfiguringDownscalingFilterFails(const IntSize & aInputSize,const IntSize & aOutputSize)33 void AssertConfiguringDownscalingFilterFails(const IntSize& aInputSize,
34                                              const IntSize& aOutputSize) {
35   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
36   ASSERT_TRUE(decoder != nullptr);
37 
38   AssertConfiguringPipelineFails(
39       decoder, DownscalingConfig{aInputSize, SurfaceFormat::OS_RGBA},
40       SurfaceConfig{decoder, aOutputSize, SurfaceFormat::OS_RGBA, false});
41 }
42 
TEST(ImageDownscalingFilter,WritePixels100_100to99_99)43 TEST(ImageDownscalingFilter, WritePixels100_100to99_99)
44 {
45   WithDownscalingFilter(IntSize(100, 100), IntSize(99, 99),
46                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
47                           CheckWritePixels(
48                               aDecoder, aFilter,
49                               /* aOutputRect = */ Some(IntRect(0, 0, 99, 99)));
50                         });
51 }
52 
TEST(ImageDownscalingFilter,WritePixels100_100to33_33)53 TEST(ImageDownscalingFilter, WritePixels100_100to33_33)
54 {
55   WithDownscalingFilter(IntSize(100, 100), IntSize(33, 33),
56                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
57                           CheckWritePixels(
58                               aDecoder, aFilter,
59                               /* aOutputRect = */ Some(IntRect(0, 0, 33, 33)));
60                         });
61 }
62 
TEST(ImageDownscalingFilter,WritePixels100_100to1_1)63 TEST(ImageDownscalingFilter, WritePixels100_100to1_1)
64 {
65   WithDownscalingFilter(IntSize(100, 100), IntSize(1, 1),
66                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
67                           CheckWritePixels(
68                               aDecoder, aFilter,
69                               /* aOutputRect = */ Some(IntRect(0, 0, 1, 1)));
70                         });
71 }
72 
TEST(ImageDownscalingFilter,WritePixels100_100to33_99)73 TEST(ImageDownscalingFilter, WritePixels100_100to33_99)
74 {
75   WithDownscalingFilter(IntSize(100, 100), IntSize(33, 99),
76                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
77                           CheckWritePixels(
78                               aDecoder, aFilter,
79                               /* aOutputRect = */ Some(IntRect(0, 0, 33, 99)));
80                         });
81 }
82 
TEST(ImageDownscalingFilter,WritePixels100_100to99_33)83 TEST(ImageDownscalingFilter, WritePixels100_100to99_33)
84 {
85   WithDownscalingFilter(IntSize(100, 100), IntSize(99, 33),
86                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
87                           CheckWritePixels(
88                               aDecoder, aFilter,
89                               /* aOutputRect = */ Some(IntRect(0, 0, 99, 33)));
90                         });
91 }
92 
TEST(ImageDownscalingFilter,WritePixels100_100to99_1)93 TEST(ImageDownscalingFilter, WritePixels100_100to99_1)
94 {
95   WithDownscalingFilter(IntSize(100, 100), IntSize(99, 1),
96                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
97                           CheckWritePixels(
98                               aDecoder, aFilter,
99                               /* aOutputRect = */ Some(IntRect(0, 0, 99, 1)));
100                         });
101 }
102 
TEST(ImageDownscalingFilter,WritePixels100_100to1_99)103 TEST(ImageDownscalingFilter, WritePixels100_100to1_99)
104 {
105   WithDownscalingFilter(IntSize(100, 100), IntSize(1, 99),
106                         [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
107                           CheckWritePixels(
108                               aDecoder, aFilter,
109                               /* aOutputRect = */ Some(IntRect(0, 0, 1, 99)));
110                         });
111 }
112 
TEST(ImageDownscalingFilter,DownscalingFailsFor100_100to101_101)113 TEST(ImageDownscalingFilter, DownscalingFailsFor100_100to101_101)
114 {
115   // Upscaling is disallowed.
116   AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(101, 101));
117 }
118 
TEST(ImageDownscalingFilter,DownscalingFailsFor100_100to100_100)119 TEST(ImageDownscalingFilter, DownscalingFailsFor100_100to100_100)
120 {
121   // "Scaling" to the same size is disallowed.
122   AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(100, 100));
123 }
124 
TEST(ImageDownscalingFilter,DownscalingFailsFor0_0toMinus1_Minus1)125 TEST(ImageDownscalingFilter, DownscalingFailsFor0_0toMinus1_Minus1)
126 {
127   // A 0x0 input size is disallowed.
128   AssertConfiguringDownscalingFilterFails(IntSize(0, 0), IntSize(-1, -1));
129 }
130 
TEST(ImageDownscalingFilter,DownscalingFailsForMinus1_Minus1toMinus2_Minus2)131 TEST(ImageDownscalingFilter, DownscalingFailsForMinus1_Minus1toMinus2_Minus2)
132 {
133   // A negative input size is disallowed.
134   AssertConfiguringDownscalingFilterFails(IntSize(-1, -1), IntSize(-2, -2));
135 }
136 
TEST(ImageDownscalingFilter,DownscalingFailsFor100_100to0_0)137 TEST(ImageDownscalingFilter, DownscalingFailsFor100_100to0_0)
138 {
139   // A 0x0 output size is disallowed.
140   AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(0, 0));
141 }
142 
TEST(ImageDownscalingFilter,DownscalingFailsFor100_100toMinus1_Minus1)143 TEST(ImageDownscalingFilter, DownscalingFailsFor100_100toMinus1_Minus1)
144 {
145   // A negative output size is disallowed.
146   AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(-1, -1));
147 }
148 
TEST(ImageDownscalingFilter,WritePixelsOutput100_100to20_20)149 TEST(ImageDownscalingFilter, WritePixelsOutput100_100to20_20)
150 {
151   WithDownscalingFilter(
152       IntSize(100, 100), IntSize(20, 20),
153       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
154         // Fill the image. It consists of 25 lines of green, followed by 25
155         // lines of red, followed by 25 lines of green, followed by 25 more
156         // lines of red.
157         uint32_t count = 0;
158         auto result =
159             aFilter->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
160               uint32_t color =
161                   (count <= 25 * 100) || (count > 50 * 100 && count <= 75 * 100)
162                       ? BGRAColor::Green().AsPixel()
163                       : BGRAColor::Red().AsPixel();
164               ++count;
165               return AsVariant(color);
166             });
167         EXPECT_EQ(WriteState::FINISHED, result);
168         EXPECT_EQ(100u * 100u, count);
169 
170         AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 100, 100),
171                                         IntRect(0, 0, 20, 20));
172 
173         // Check that the generated image is correct. Note that we skip rows
174         // near the transitions between colors, since the downscaler does not
175         // produce a sharp boundary at these points. Even some of the rows we
176         // test need a small amount of fuzz; this is just the nature of Lanczos
177         // downscaling.
178         RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
179         RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
180         EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(),
181                                       /* aFuzz = */ 2));
182         EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(),
183                                       /* aFuzz = */ 3));
184         EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(),
185                                       /* aFuzz = */ 3));
186         EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(),
187                                       /* aFuzz = */ 3));
188       });
189 }
190 
TEST(ImageDownscalingFilter,WritePixelsOutput100_100to10_20)191 TEST(ImageDownscalingFilter, WritePixelsOutput100_100to10_20)
192 {
193   WithDownscalingFilter(
194       IntSize(100, 100), IntSize(10, 20),
195       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
196         // Fill the image. It consists of 25 lines of green, followed by 25
197         // lines of red, followed by 25 lines of green, followed by 25 more
198         // lines of red.
199         uint32_t count = 0;
200         auto result =
201             aFilter->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
202               uint32_t color =
203                   (count <= 25 * 100) || (count > 50 * 100 && count <= 75 * 100)
204                       ? BGRAColor::Green().AsPixel()
205                       : BGRAColor::Red().AsPixel();
206               ++count;
207               return AsVariant(color);
208             });
209         EXPECT_EQ(WriteState::FINISHED, result);
210         EXPECT_EQ(100u * 100u, count);
211 
212         AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 100, 100),
213                                         IntRect(0, 0, 10, 20));
214 
215         // Check that the generated image is correct. Note that we skip rows
216         // near the transitions between colors, since the downscaler does not
217         // produce a sharp boundary at these points. Even some of the rows we
218         // test need a small amount of fuzz; this is just the nature of Lanczos
219         // downscaling.
220         RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
221         RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
222         EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(),
223                                       /* aFuzz = */ 2));
224         EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(),
225                                       /* aFuzz = */ 3));
226         EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(),
227                                       /* aFuzz = */ 3));
228         EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(),
229                                       /* aFuzz = */ 3));
230       });
231 }
232