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