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