Formatting automated-tests
[platform/core/uifw/dali-adaptor.git] / automated-tests / src / dali-platform-abstraction / utc-image-fitting-modes.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include "utc-image-loading-common.h"
19
20 #include <dali/internal/imaging/common/image-operations.h>
21
22 using Dali::Internal::Platform::ApplyAttributesToBitmap;
23
24 #define ANSI_BLACK "\x1B[0m"
25 #define ANSI_RED "\x1B[31m"
26 #define ANSI_GREEN "\x1B[32m"
27 #define ANSI_YELLOW "\x1B[33m"
28 #define ANSI_BLUE "\x1B[34m"
29 #define ANSI_MAGENTA "\x1B[35m"
30 #define ANSI_CYAN "\x1B[36m"
31 #define ANSI_WHITE "\x1B[37m"
32 #define ANSI_RESET "\033[0m"
33
34 const unsigned char   BORDER_FILL_VALUE = 0xff;
35 const char*           ASCII_FILL_VALUE  = ANSI_YELLOW "#";
36 const char*           ASCII_PAD_VALUE   = ANSI_BLUE "#";
37 typedef unsigned char PixelBuffer;
38
39 void FillBitmap(Dali::Devel::PixelBuffer bitmap)
40 {
41   // Fill the given bitmap fully.
42   const Pixel::Format pixelFormat   = bitmap.GetPixelFormat();
43   const unsigned int  bytesPerPixel = Pixel::GetBytesPerPixel(pixelFormat);
44   PixelBuffer* const  targetPixels  = bitmap.GetBuffer();
45   const int           bytesToFill   = bitmap.GetWidth() * bitmap.GetHeight() * bytesPerPixel;
46
47   memset(targetPixels, BORDER_FILL_VALUE, bytesToFill);
48 }
49
50 typedef Rect<int> ActiveArea;
51
52 // This struct defines all information for one test.
53 struct ImageFittingTestParameters
54 {
55   unsigned int      sourceWidth;
56   unsigned int      sourceHeight;
57   unsigned int      desiredWidth;
58   unsigned int      desiredHeight;
59   FittingMode::Type fittingMode;
60
61   unsigned int expectedWidth;
62   unsigned int expectedHeight;
63   ActiveArea   expectedActiveImageArea;
64
65   ImageFittingTestParameters(unsigned int newSourceWidth, unsigned int newSourceHeight, unsigned int newDesiredWidth, unsigned int newDesiredHeight, FittingMode::Type newFittingMode, unsigned int newExpectedWidth, unsigned int newExpectedHeight, ActiveArea newExpectedActiveImageArea)
66   : sourceWidth(newSourceWidth),
67     sourceHeight(newSourceHeight),
68     desiredWidth(newDesiredWidth),
69     desiredHeight(newDesiredHeight),
70     fittingMode(newFittingMode),
71     expectedWidth(newExpectedWidth),
72     expectedHeight(newExpectedHeight),
73     expectedActiveImageArea(newExpectedActiveImageArea)
74   {
75   }
76 };
77
78 typedef std::vector<ImageFittingTestParameters> TestContainer;
79
80 void PerformFittingTests(TestContainer& tests)
81 {
82   // Iterate through all pre-defined tests.
83   for(unsigned int testNumber = 0; testNumber < tests.size(); ++testNumber)
84   {
85     // Gather info for this test.
86     ImageFittingTestParameters& test = tests[testNumber];
87
88     unsigned int      sourceWidth   = test.sourceWidth;
89     unsigned int      sourceHeight  = test.sourceHeight;
90     unsigned int      desiredWidth  = test.desiredWidth;
91     unsigned int      desiredHeight = test.desiredHeight;
92     FittingMode::Type fittingMode   = test.fittingMode;
93
94     // Create a source bitmap.
95     ImageDimensions    desiredDimensions(desiredWidth, desiredHeight);
96     SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR;
97
98     auto                sourceBitmap = Dali::Devel::PixelBuffer::New(sourceWidth, sourceHeight, Pixel::Format::RGBA8888);
99     const Pixel::Format pixelFormat  = sourceBitmap.GetPixelFormat();
100     // Completely fill the source bitmap (with white).
101     FillBitmap(sourceBitmap);
102
103     // Perform fitting operations (this is the method we are testing).
104     auto newBitmap = ApplyAttributesToBitmap(sourceBitmap, desiredDimensions, fittingMode, samplingMode);
105
106     DALI_TEST_CHECK(newBitmap);
107
108     // As we do not need performance within this test, we branch to exit here (for readability, maintainability).
109     if(!newBitmap)
110     {
111       return;
112     }
113
114     auto bitmap(newBitmap);
115
116     unsigned int resultWidth  = bitmap.GetWidth();
117     unsigned int resultHeight = bitmap.GetHeight();
118
119     // Check the dimensions of the modified image match against the expected values defined in the test.
120     DALI_TEST_EQUALS(resultWidth, test.expectedWidth, TEST_LOCATION);
121     DALI_TEST_EQUALS(resultHeight, test.expectedHeight, TEST_LOCATION);
122
123     PixelBuffer*       resultBuffer  = bitmap.GetBuffer();
124     const unsigned int bytesPerPixel = Pixel::GetBytesPerPixel(pixelFormat);
125
126     // We generate an ASCII representation of the source, desired and result images to log, purely as a debugging aid.
127     // (0 = border, 1 = active image area - from the source image).
128     std::string xSourceImageString(sourceWidth, '#');
129     std::string xDesiredSizeString(desiredWidth - 2, '-');
130     std::string xDesiredSizePadString(desiredWidth - 2, ' ');
131     tet_printf("%sRunning test: %d%s\n", ANSI_RED, testNumber + 1, ANSI_RESET);
132     tet_printf("Source image: %s%s%s\n", ANSI_YELLOW, xSourceImageString.c_str(), ANSI_RESET);
133     for(unsigned int i = 0; i < sourceHeight - 1; ++i)
134     {
135       tet_printf("              %s%s%s\n", ANSI_YELLOW, xSourceImageString.c_str(), ANSI_RESET);
136     }
137     tet_printf("Desired size: %s+%s+%s\n", ANSI_YELLOW, xDesiredSizeString.c_str(), ANSI_RESET);
138     for(unsigned int i = 0; i < desiredHeight - 2; ++i)
139     {
140       tet_printf("              %s|%s|%s\n", ANSI_YELLOW, xDesiredSizePadString.c_str(), ANSI_RESET);
141     }
142     tet_printf("              %s+%s+%s\n", ANSI_YELLOW, xDesiredSizeString.c_str(), ANSI_RESET);
143
144     // We want to calculate the active image area (the area filled with image data as opposed to borders).
145     // This is so we can determine if the fitting modes worked correctly.
146     ActiveArea resultActiveArea(-1, -1, -1, -1);
147
148     // Iterate over the result image data to find the active area.
149     for(unsigned int y = 0; y < resultHeight; ++y)
150     {
151       int         activeStartX = -1;
152       int         activeEndX   = -1;
153       std::string xResultImageString;
154
155       for(unsigned int x = 0; x < resultWidth; ++x)
156       {
157         bool pixelPopulated = resultBuffer[x * bytesPerPixel] != 0x00;
158
159         // If the pixel is filled AND we haven't found a filled pixel yet,
160         // this is the horizontal start of the active pixel area (for this line).
161         if(pixelPopulated && (activeStartX == -1))
162         {
163           activeStartX = x;
164         }
165         else if(!pixelPopulated && (activeStartX != -1) && (activeEndX == -1))
166         {
167           // If the pixel is NOT filled AND we HAVE rpeviously found a filled pixel,
168           // then this is the horizontal end of the active pixel area (for this line).
169           activeEndX = x + 1;
170         }
171
172         // Populate a string with the filled state of the result pixels, to facilitate debugging.
173         xResultImageString += pixelPopulated ? ASCII_FILL_VALUE : ASCII_PAD_VALUE;
174       }
175
176       // First calculate the X-end span value, if we ran out of image before reaching the end of active image area.
177       if((activeStartX != -1) && (activeEndX == -1))
178       {
179         activeEndX = resultWidth - activeStartX;
180       }
181
182       // If the X-start pixel on this line is earlier than other lines, the overall active area starts earlier.
183       // Note: This is ignored if there was no pixels found.
184       if((activeStartX != -1) && ((activeStartX < resultActiveArea.x) || (resultActiveArea.x == -1)))
185       {
186         resultActiveArea.x = activeStartX;
187       }
188
189       // If the X-end pixel on this line is later than other lines, the overall active area starts later.
190       // Note: This is ignored if there was no pixels found.
191       if((activeEndX != -1) && ((activeEndX > resultActiveArea.width) || (resultActiveArea.width == -1)))
192       {
193         resultActiveArea.width = activeEndX;
194       }
195
196       // If there was an X-start pixel on this line AND we don't yet have a Y-start, this line IS the Y-start.
197       if((activeStartX != -1) && (resultActiveArea.y == -1))
198       {
199         resultActiveArea.y = y;
200       }
201
202       // If there was no X-start pixel on this line AND we already have a Y-start value,
203       // then the last Y becomes the new Y-end value.
204       if((activeStartX == -1) && (resultActiveArea.y != -1) && (resultActiveArea.height == -1))
205       {
206         resultActiveArea.height = y - 1;
207       }
208
209       if(y == 0)
210       {
211         tet_printf("Result image: %s\n", xResultImageString.c_str());
212       }
213       else
214       {
215         tet_printf("              %s\n", xResultImageString.c_str());
216       }
217
218       resultBuffer += resultWidth * bytesPerPixel;
219     }
220
221     // Calculate the Y-end value, if we ran out of image before reaching the end of active image area.
222     if((resultActiveArea.y != -1) && (resultActiveArea.height == -1))
223     {
224       resultActiveArea.height = resultHeight - resultActiveArea.y;
225     }
226
227     tet_printf("%s", ANSI_RESET);
228     tet_printf("Test: %d  Result image dimensions: %d,%d  ActiveArea: %d,%d,%d,%d\n",
229                testNumber + 1,
230                resultWidth,
231                resultHeight,
232                resultActiveArea.x,
233                resultActiveArea.y,
234                resultActiveArea.width,
235                resultActiveArea.height);
236
237     // Test the result images active area matches the expected active area defined in the test.
238     DALI_TEST_EQUALS(resultActiveArea, test.expectedActiveImageArea, TEST_LOCATION);
239   }
240 }
241
242 // Test cases:
243
244 // Positive test case for fitting mode: FIT_WIDTH.
245 int UtcDaliFittingModesFitWidth(void)
246 {
247   tet_printf("Running fitting mode test for: FIT_WIDTH\n");
248
249   TestContainer tests;
250
251   // Here we can define the input and expected output of each test on a single line.
252   // Source Width, Source Height, Desired Width, Desired Height, Fitting Mode, Expected Width, Expected Height, ActiveArea: X-start, Y-start, width, height
253
254   // Test Image source size = desired size. Output should be the same.
255   tests.push_back(ImageFittingTestParameters(4, 4, 4, 4, FittingMode::FIT_WIDTH, 4, 4, ActiveArea(0, 0, 4, 4)));
256   // Test Image source size > desired size, but aspect same. Should scale size down.
257   tests.push_back(ImageFittingTestParameters(4, 4, 2, 2, FittingMode::FIT_WIDTH, 2, 2, ActiveArea(0, 0, 2, 2)));
258   // Test Image source size < desired size, but aspect same. Should not scale size up.
259   tests.push_back(ImageFittingTestParameters(2, 2, 4, 4, FittingMode::FIT_WIDTH, 2, 2, ActiveArea(0, 0, 2, 2)));
260   // Test Image source size < desired size, but aspect different. Should crop height, so no borders. No scale up as result has same aspect after crop.
261   tests.push_back(ImageFittingTestParameters(2, 4, 8, 8, FittingMode::FIT_WIDTH, 2, 2, ActiveArea(0, 0, 2, 2)));
262   // Test Image source size > desired size, but aspect different (w < h). Should crop height, so no borders. No scale as result is same size as desired size.
263   tests.push_back(ImageFittingTestParameters(4, 8, 4, 4, FittingMode::FIT_WIDTH, 4, 4, ActiveArea(0, 0, 4, 4)));
264   // Test Image source size > desired size, but aspect different (w > h). Should add borders, AND scale down to desired size.
265   tests.push_back(ImageFittingTestParameters(8, 4, 4, 4, FittingMode::FIT_WIDTH, 4, 4, ActiveArea(0, 1, 4, 2)));
266
267   PerformFittingTests(tests);
268
269   END_TEST;
270 }
271
272 // Positive test case for fitting mode: FIT_HEIGHT.
273 int UtcDaliFittingModesFitHeight(void)
274 {
275   tet_printf("Running fitting mode test for: FIT_HEIGHT\n");
276
277   TestContainer tests;
278
279   // Here we can define the input and expected output of each test on a single line.
280   // Source Width, Source Height, Desired Width, Desired Height, Fitting Mode, Expected Width, Expected Height, ActiveArea: X-start, Y-start, width, height
281
282   // Test Image source size = desired size. Output should be the same.
283   tests.push_back(ImageFittingTestParameters(4, 4, 4, 4, FittingMode::FIT_HEIGHT, 4, 4, ActiveArea(0, 0, 4, 4)));
284   // Test Image source size > desired size, but aspect same. Should scale size down.
285   tests.push_back(ImageFittingTestParameters(4, 4, 2, 2, FittingMode::FIT_HEIGHT, 2, 2, ActiveArea(0, 0, 2, 2)));
286   // Test Image source size < desired size, but aspect same. Should not scale size up.
287   tests.push_back(ImageFittingTestParameters(2, 2, 4, 4, FittingMode::FIT_HEIGHT, 2, 2, ActiveArea(0, 0, 2, 2)));
288   // Test Image source size < desired size, but aspect different. Should add borders, but not scale overall size up.
289   tests.push_back(ImageFittingTestParameters(2, 4, 8, 8, FittingMode::FIT_HEIGHT, 4, 4, ActiveArea(1, 0, 4, 4)));
290   // Test Image source size > desired size, but aspect different (w < h). Should add borders, AND scale down to desired size.
291   tests.push_back(ImageFittingTestParameters(4, 8, 4, 4, FittingMode::FIT_HEIGHT, 4, 4, ActiveArea(1, 0, 4, 4)));
292   // Test Image source size > desired size, but aspect different (w > h). Should crop width, so no borders. No scale as result is same size as desired size.
293   tests.push_back(ImageFittingTestParameters(8, 4, 4, 4, FittingMode::FIT_HEIGHT, 4, 4, ActiveArea(0, 0, 4, 4)));
294
295   PerformFittingTests(tests);
296
297   END_TEST;
298 }
299
300 // Positive test case for fitting mode: SHRINK_TO_FIT.
301 int UtcDaliFittingModesShrinkToFit(void)
302 {
303   tet_printf("Running fitting mode test for: SHRINK_TO_FIT\n");
304
305   TestContainer tests;
306
307   // Here we can define the input and expected output of each test on a single line.
308   // Source Width, Source Height, Desired Width, Desired Height, Fitting Mode, Expected Width, Expected Height, ActiveArea: X-start, Y-start, width, height
309
310   // Test Image source size = desired size. Output should be the same.
311   tests.push_back(ImageFittingTestParameters(4, 4, 4, 4, FittingMode::SHRINK_TO_FIT, 4, 4, ActiveArea(0, 0, 4, 4)));
312   // Test Image source size > desired size, but aspect same. Should scale size down.
313   tests.push_back(ImageFittingTestParameters(4, 4, 2, 2, FittingMode::SHRINK_TO_FIT, 2, 2, ActiveArea(0, 0, 2, 2)));
314   // Test Image source size < desired size, but aspect same. Should not scale size up.
315   tests.push_back(ImageFittingTestParameters(2, 2, 4, 4, FittingMode::SHRINK_TO_FIT, 2, 2, ActiveArea(0, 0, 2, 2)));
316   // Test Image source size < desired size, but aspect different. Should add borders, but not scale overall size up, as although image is smaller than desired size, aspect is the same.
317   tests.push_back(ImageFittingTestParameters(2, 4, 8, 8, FittingMode::SHRINK_TO_FIT, 4, 4, ActiveArea(1, 0, 4, 4)));
318   // Test Image source size > desired size, but aspect different (w < h). Should add borders, AND scale down to desired size.
319   tests.push_back(ImageFittingTestParameters(4, 8, 4, 4, FittingMode::SHRINK_TO_FIT, 4, 4, ActiveArea(1, 0, 4, 4)));
320   // Test Image source size > desired size, but aspect different (w > h). Should add borders, AND scale down to desired size.
321   tests.push_back(ImageFittingTestParameters(8, 4, 4, 4, FittingMode::SHRINK_TO_FIT, 4, 4, ActiveArea(0, 1, 4, 2)));
322
323   PerformFittingTests(tests);
324
325   END_TEST;
326 }
327
328 // Positive test case for fitting mode: SCALE_TO_FILL.
329 int UtcDaliFittingModesScaleToFill(void)
330 {
331   tet_printf("Running fitting mode test for: SCALE_TO_FILL\n");
332
333   TestContainer tests;
334
335   // Here we can define the input and expected output of each test on a single line.
336   // Source Width, Source Height, Desired Width, Desired Height, Fitting Mode, Expected Width, Expected Height, ActiveArea: X-start, Y-start, width, height
337
338   // Test Image source size = desired size. Output should be the same.
339   tests.push_back(ImageFittingTestParameters(4, 4, 4, 4, FittingMode::SCALE_TO_FILL, 4, 4, ActiveArea(0, 0, 4, 4)));
340   // Test Image source size > desired size, but aspect same. Should scale size down.
341   tests.push_back(ImageFittingTestParameters(4, 4, 2, 2, FittingMode::SCALE_TO_FILL, 2, 2, ActiveArea(0, 0, 2, 2)));
342   // Test Image source size < desired size, but aspect same. Should not scale size up.
343   tests.push_back(ImageFittingTestParameters(2, 2, 4, 4, FittingMode::SCALE_TO_FILL, 2, 2, ActiveArea(0, 0, 2, 2)));
344   // Test Image source size < desired size, but aspect different. Should crop height, so no borders. No scale up as result has same aspect after crop.
345   tests.push_back(ImageFittingTestParameters(2, 4, 8, 8, FittingMode::SCALE_TO_FILL, 2, 2, ActiveArea(0, 0, 2, 2)));
346   // Test Image source size > desired size, but aspect different (w < h). Should crop height, so no borders. No scale as result is same size as desired size.
347   tests.push_back(ImageFittingTestParameters(4, 8, 4, 4, FittingMode::SCALE_TO_FILL, 4, 4, ActiveArea(0, 0, 4, 4)));
348   // Test Image source size > desired size, but aspect different (w > h). Should crop width, so no borders. No scale as result is same size as desired size.
349   tests.push_back(ImageFittingTestParameters(8, 4, 4, 4, FittingMode::SCALE_TO_FILL, 4, 4, ActiveArea(0, 0, 4, 4)));
350
351   PerformFittingTests(tests);
352
353   END_TEST;
354 }