2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "utc-image-loading-common.h"
20 #include <dali/internal/imaging/common/image-operations.h>
22 using Dali::Internal::Platform::ApplyAttributesToBitmap;
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"
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;
39 void FillBitmap(Dali::Devel::PixelBuffer bitmap)
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;
47 memset(targetPixels, BORDER_FILL_VALUE, bytesToFill);
50 typedef Rect<int> ActiveArea;
52 // This struct defines all information for one test.
53 struct ImageFittingTestParameters
55 unsigned int sourceWidth;
56 unsigned int sourceHeight;
57 unsigned int desiredWidth;
58 unsigned int desiredHeight;
59 FittingMode::Type fittingMode;
61 unsigned int expectedWidth;
62 unsigned int expectedHeight;
63 ActiveArea expectedActiveImageArea;
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)
78 typedef std::vector<ImageFittingTestParameters> TestContainer;
80 void PerformFittingTests(TestContainer& tests)
82 // Iterate through all pre-defined tests.
83 for(unsigned int testNumber = 0; testNumber < tests.size(); ++testNumber)
85 // Gather info for this test.
86 ImageFittingTestParameters& test = tests[testNumber];
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;
94 // Create a source bitmap.
95 ImageDimensions desiredDimensions(desiredWidth, desiredHeight);
96 SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR;
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);
103 // Perform fitting operations (this is the method we are testing).
104 auto newBitmap = ApplyAttributesToBitmap(sourceBitmap, desiredDimensions, fittingMode, samplingMode);
106 DALI_TEST_CHECK(newBitmap);
108 // As we do not need performance within this test, we branch to exit here (for readability, maintainability).
114 auto bitmap(newBitmap);
116 unsigned int resultWidth = bitmap.GetWidth();
117 unsigned int resultHeight = bitmap.GetHeight();
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);
123 PixelBuffer* resultBuffer = bitmap.GetBuffer();
124 const unsigned int bytesPerPixel = Pixel::GetBytesPerPixel(pixelFormat);
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)
135 tet_printf(" %s%s%s\n", ANSI_YELLOW, xSourceImageString.c_str(), ANSI_RESET);
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)
140 tet_printf(" %s|%s|%s\n", ANSI_YELLOW, xDesiredSizePadString.c_str(), ANSI_RESET);
142 tet_printf(" %s+%s+%s\n", ANSI_YELLOW, xDesiredSizeString.c_str(), ANSI_RESET);
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);
148 // Iterate over the result image data to find the active area.
149 for(unsigned int y = 0; y < resultHeight; ++y)
151 int activeStartX = -1;
153 std::string xResultImageString;
155 for(unsigned int x = 0; x < resultWidth; ++x)
157 bool pixelPopulated = resultBuffer[x * bytesPerPixel] != 0x00;
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))
165 else if(!pixelPopulated && (activeStartX != -1) && (activeEndX == -1))
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).
172 // Populate a string with the filled state of the result pixels, to facilitate debugging.
173 xResultImageString += pixelPopulated ? ASCII_FILL_VALUE : ASCII_PAD_VALUE;
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))
179 activeEndX = resultWidth - activeStartX;
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)))
186 resultActiveArea.x = activeStartX;
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)))
193 resultActiveArea.width = activeEndX;
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))
199 resultActiveArea.y = y;
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))
206 resultActiveArea.height = y - 1;
211 tet_printf("Result image: %s\n", xResultImageString.c_str());
215 tet_printf(" %s\n", xResultImageString.c_str());
218 resultBuffer += resultWidth * bytesPerPixel;
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))
224 resultActiveArea.height = resultHeight - resultActiveArea.y;
227 tet_printf("%s", ANSI_RESET);
228 tet_printf("Test: %d Result image dimensions: %d,%d ActiveArea: %d,%d,%d,%d\n",
234 resultActiveArea.width,
235 resultActiveArea.height);
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);
244 // Positive test case for fitting mode: FIT_WIDTH.
245 int UtcDaliFittingModesFitWidth(void)
247 tet_printf("Running fitting mode test for: FIT_WIDTH\n");
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
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)));
267 PerformFittingTests(tests);
272 // Positive test case for fitting mode: FIT_HEIGHT.
273 int UtcDaliFittingModesFitHeight(void)
275 tet_printf("Running fitting mode test for: FIT_HEIGHT\n");
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
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)));
295 PerformFittingTests(tests);
300 // Positive test case for fitting mode: SHRINK_TO_FIT.
301 int UtcDaliFittingModesShrinkToFit(void)
303 tet_printf("Running fitting mode test for: SHRINK_TO_FIT\n");
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
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)));
323 PerformFittingTests(tests);
328 // Positive test case for fitting mode: SCALE_TO_FILL.
329 int UtcDaliFittingModesScaleToFill(void)
331 tet_printf("Running fitting mode test for: SCALE_TO_FILL\n");
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
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)));
351 PerformFittingTests(tests);