Merge "Make some image operation as bitwise operation + Reduce alpha-masking op"...
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / image-operations.h
1 /*
2  * Copyright (c) 2022 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 #ifndef DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H
19 #define DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H
20
21 // EXTERNAL INCLUDES
22 #include <stdint.h>
23
24 // INTERNAL INCLUDES
25 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
26 #include <dali/integration-api/bitmap.h>
27 #include <dali/public-api/images/image-operations.h>
28 #include <third-party/resampler/resampler.h>
29
30 namespace Dali
31 {
32 namespace Internal
33 {
34 namespace Platform
35 {
36 /**
37  * @brief Identify which combination of x and y dimensions matter in terminating iterative box filtering.
38  */
39 enum BoxDimensionTest
40 {
41   BoxDimensionTestEither,
42   BoxDimensionTestBoth,
43   BoxDimensionTestX,
44   BoxDimensionTestY
45 };
46
47 /**
48  * @brief The integer dimensions of an image or a region of an image packed into
49  *        16 bits per component.
50  * @note  This can only be used for images of up to 65535 x 65535 pixels.
51   */
52 typedef Uint16Pair ImageDimensions;
53
54 /**
55  * @brief Work out the true desired width and height, accounting for special
56  * rules for zeros in either or both input requested dimensions.
57  *
58  * @param[in] rawDimensions Width and height of image before processing.
59  * @param[in] requestedDimensions Width and height of area to scale image into. Can be zero.
60  * @return Dimensions of area to scale image into after special rules are applied.
61  */
62 ImageDimensions CalculateDesiredDimensions(ImageDimensions rawDimensions, ImageDimensions requestedDimensions);
63
64 /**
65  * @defgroup BitmapOperations Bitmap-to-Bitmap Image operations.
66  * @{
67  */
68
69 /**
70  * @brief Apply requested attributes to bitmap.
71  *
72  * This is the top-level function which runs the on-load image post-processing
73  * pipeline. Bitmaps enter here as loaded from the file system by the file
74  * loaders and leave downscaled and filtered as requested by the application,
75  * ready for use.
76  *
77  * @param[in] bitmap The input bitmap.
78  * @param[in] requestedAttributes Attributes which should be applied to bitmap.
79  * @return A bitmap which results from applying the requested attributes to the
80  *         bitmap passed-in, or the original bitmap passed in if the attributes
81  *         have no effect.
82  */
83 Dali::Devel::PixelBuffer ApplyAttributesToBitmap(Dali::Devel::PixelBuffer bitmap, ImageDimensions dimensions, FittingMode::Type fittingMode = FittingMode::DEFAULT, SamplingMode::Type samplingMode = SamplingMode::DEFAULT);
84
85 /**
86  * @brief Apply downscaling to a bitmap according to requested attributes.
87  * @note The input bitmap pixel buffer may be modified and used as scratch working space for efficiency, so it must be discarded.
88  **/
89 Dali::Devel::PixelBuffer DownscaleBitmap(Dali::Devel::PixelBuffer bitmap,
90                                          ImageDimensions          desired,
91                                          FittingMode::Type        fittingMode,
92                                          SamplingMode::Type       samplingMode);
93 /**@}*/
94
95 /**
96  * @defgroup ImageBufferScalingAlgorithms Pixel buffer-level scaling algorithms.
97  * @{
98  */
99
100 /**
101  * @brief Destructive in-place downscaling by a power of 2 factor.
102  *
103  * A box filter with a 2x2 kernel is repeatedly applied as long as the result
104  * of the next downscaling step would not be smaller than the desired
105  * dimensions.
106  * @param[in,out] pixels The buffer both to read from and write the result to.
107  * @param[in]     pixelFormat The format of the image pointed at by pixels.
108  * @param[in]     inputWidth The width of the input image.
109  * @param[in]     inputHeight The height of the input image.
110  * @param[in]     desiredWidth The width the client is requesting.
111  * @param[in]     desiredHeight The height the client is requesting.
112  * @param[out]    outWidth  The resulting width after downscaling.
113  * @param[out]    outHeight The resulting height after downscaling.
114  */
115 void DownscaleInPlacePow2(unsigned char* const pixels,
116                           Pixel::Format        pixelFormat,
117                           unsigned int         inputWidth,
118                           unsigned int         inputHeight,
119                           unsigned int         desiredWidth,
120                           unsigned int         desiredHeight,
121                           FittingMode::Type    fittingMode,
122                           SamplingMode::Type   samplingMode,
123                           unsigned&            outWidth,
124                           unsigned&            outHeight);
125
126 /**
127  * @brief Destructive in-place downscaling by a power of 2 factor.
128  *
129  * A box filter with a 2x2 kernel is repeatedly applied as long as the result
130  * of the next downscaling step would not be smaller than the desired
131  * dimensions.
132  * @param[in,out] pixels The buffer both to read from and write the result to.
133  * @param[in]     inputWidth The width of the input image.
134  * @param[in]     inputHeight The height of the input image.
135  * @param[in]     desiredWidth The width the client is requesting.
136  * @param[in]     desiredHeight The height the client is requesting.
137  * @param[out]    outWidth  The resulting width after downscaling.
138  * @param[out]    outHeight The resulting height after downscaling.
139  */
140 void DownscaleInPlacePow2RGB888(unsigned char*   pixels,
141                                 unsigned int     inputWidth,
142                                 unsigned int     inputHeight,
143                                 unsigned int     desiredWidth,
144                                 unsigned int     desiredHeight,
145                                 BoxDimensionTest dimensionTest,
146                                 unsigned int&    outWidth,
147                                 unsigned int&    outHeight);
148
149 /**
150  * @copydoc DownscaleInPlacePow2RGB888
151  */
152 void DownscaleInPlacePow2RGBA8888(unsigned char*   pixels,
153                                   unsigned int     inputWidth,
154                                   unsigned int     inputHeight,
155                                   unsigned int     desiredWidth,
156                                   unsigned int     desiredHeight,
157                                   BoxDimensionTest dimensionTest,
158                                   unsigned int&    outWidth,
159                                   unsigned int&    outHeight);
160
161 /**
162  * @copydoc DownscaleInPlacePow2RGB888
163  *
164  * For the 2-byte packed 16 bit format RGB565.
165  */
166 void DownscaleInPlacePow2RGB565(unsigned char*   pixels,
167                                 unsigned int     inputWidth,
168                                 unsigned int     inputHeight,
169                                 unsigned int     desiredWidth,
170                                 unsigned int     desiredHeight,
171                                 BoxDimensionTest dimensionTest,
172                                 unsigned int&    outWidth,
173                                 unsigned int&    outHeight);
174
175 /**
176  * @copydoc DownscaleInPlacePow2RGB888
177  *
178  * For 2-byte formats such as lum8alpha8, but not packed 16 bit formats like RGB565.
179  */
180 void DownscaleInPlacePow2ComponentPair(unsigned char*   pixels,
181                                        unsigned int     inputWidth,
182                                        unsigned int     inputHeight,
183                                        unsigned int     desiredWidth,
184                                        unsigned int     desiredHeight,
185                                        BoxDimensionTest dimensionTest,
186                                        unsigned int&    outWidth,
187                                        unsigned int&    outHeight);
188
189 /**
190  * @copydoc DownscaleInPlacePow2RGB888
191  *
192  * For single-byte formats such as lum8 or alpha8.
193  */
194 void DownscaleInPlacePow2SingleBytePerPixel(unsigned char*   pixels,
195                                             unsigned int     inputWidth,
196                                             unsigned int     inputHeight,
197                                             unsigned int     desiredWidth,
198                                             unsigned int     desiredHeight,
199                                             BoxDimensionTest dimensionTest,
200                                             unsigned int&    outWidth,
201                                             unsigned int&    outHeight);
202
203 /**
204  * @brief Rescales an input image into the exact output dimensions passed-in.
205  *
206  * Uses point sampling, equivalent to GL_NEAREST texture filter mode, for the
207  * fastest results, at the expense of aliasing (noisy images) when downscaling.
208  * @note inPixels is allowed to alias outPixels if this is a downscaling,
209  * but not for upscaling.
210  */
211 void PointSample(const unsigned char* inPixels,
212                  unsigned int         inputWidth,
213                  unsigned int         inputHeight,
214                  Pixel::Format        pixelFormat,
215                  unsigned char*       outPixels,
216                  unsigned int         desiredWidth,
217                  unsigned int         desiredHeight);
218
219 /**
220  * @copydoc PointSample
221  *
222  * Specialised for 4-byte formats like RGBA8888 and BGRA8888.
223  */
224 void PointSample4BPP(const unsigned char* inPixels,
225                      unsigned int         inputWidth,
226                      unsigned int         inputHeight,
227                      unsigned char*       outPixels,
228                      unsigned int         desiredWidth,
229                      unsigned int         desiredHeight);
230
231 /**
232  * @copydoc PointSample
233  *
234  * Specialised for 3-byte formats like RGB888 and BGR888.
235  */
236 void PointSample3BPP(const unsigned char* inPixels,
237                      unsigned int         inputWidth,
238                      unsigned int         inputHeight,
239                      unsigned char*       outPixels,
240                      unsigned int         desiredWidth,
241                      unsigned int         desiredHeight);
242
243 /**
244  * @copydoc PointSample
245  *
246  * Specialised for 2-byte formats like LA88.
247  */
248 void PointSample2BPP(const unsigned char* inPixels,
249                      unsigned int         inputWidth,
250                      unsigned int         inputHeight,
251                      unsigned char*       outPixels,
252                      unsigned int         desiredWidth,
253                      unsigned int         desiredHeight);
254
255 /**
256  * @copydoc PointSample
257  *
258  * Specialised for 1-byte formats like L8 and A8.
259  */
260 void PointSample1BPP(const unsigned char* inPixels,
261                      unsigned int         inputWidth,
262                      unsigned int         inputHeight,
263                      unsigned char*       outPixels,
264                      unsigned int         desiredWidth,
265                      unsigned int         desiredHeight);
266
267 /**
268  * @brief Resample input image to output image using a bilinear filter.
269  *
270  * Each output pixel is formed of a weighted sum of a 2x2 block of four input
271  * pixels
272  * @pre inPixels must not alias outPixels. The input image should be a totally
273  * separate buffer from the input one.
274  */
275 void LinearSample(const unsigned char* __restrict__ inPixels,
276                   ImageDimensions inDimensions,
277                   Pixel::Format   pixelFormat,
278                   unsigned char* __restrict__ outPixels,
279                   ImageDimensions outDimensions);
280
281 /**
282  * @copydoc LinearSample
283  *
284  * Specialised for one byte per pixel formats.
285  */
286 void LinearSample1BPP(const unsigned char* __restrict__ inPixels,
287                       ImageDimensions inputDimensions,
288                       unsigned char* __restrict__ outPixels,
289                       ImageDimensions desiredDimensions);
290
291 /**
292  * @copydoc LinearSample
293  *
294  * Specialised for two byte per pixel formats.
295  */
296 void LinearSample2BPP(const unsigned char* __restrict__ inPixels,
297                       ImageDimensions inputDimensions,
298                       unsigned char* __restrict__ outPixels,
299                       ImageDimensions desiredDimensions);
300
301 /**
302  * @copydoc LinearSample
303  *
304  * Specialised for RGB565 16 bit pixel format.
305  */
306 void LinearSampleRGB565(const unsigned char* __restrict__ inPixels,
307                         ImageDimensions inputDimensions,
308                         unsigned char* __restrict__ outPixels,
309                         ImageDimensions desiredDimensions);
310
311 /**
312  * @copydoc LinearSample
313  *
314  * Specialised for three byte per pixel formats like RGB888.
315  */
316 void LinearSample3BPP(const unsigned char* __restrict__ inPixels,
317                       ImageDimensions inputDimensions,
318                       unsigned char* __restrict__ outPixels,
319                       ImageDimensions desiredDimensions);
320
321 /**
322  * @copydoc LinearSample
323  *
324  * Specialised for four byte per pixel formats like RGBA8888.
325  * @note, If used on RGBA8888, the A component will be blended independently.
326  */
327 void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
328                       ImageDimensions inputDimensions,
329                       unsigned char* __restrict__ outPixels,
330                       ImageDimensions desiredDimensions);
331
332 /**
333  * @brief Resamples the input image with the Lanczos algorithm.
334  *
335  * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
336  * separate buffer from the output buffer.
337  *
338  * @param[in] inPixels Pointer to the input image buffer.
339  * @param[in] inputDimensions The input dimensions of the image.
340  * @param[out] outPixels Pointer to the output image buffer.
341  * @param[in] desiredDimensions The output dimensions of the image.
342  */
343 void LanczosSample4BPP(const unsigned char* __restrict__ inPixels,
344                        ImageDimensions inputDimensions,
345                        unsigned char* __restrict__ outPixels,
346                        ImageDimensions desiredDimensions);
347
348 /**
349  * @brief Resamples the input image with the Lanczos algorithm.
350  *
351  * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
352  * separate buffer from the output buffer.
353  *
354  * @param[in] inPixels Pointer to the input image buffer.
355  * @param[in] inputDimensions The input dimensions of the image.
356  * @param[out] outPixels Pointer to the output image buffer.
357  * @param[in] desiredDimensions The output dimensions of the image.
358  */
359 void LanczosSample1BPP(const unsigned char* __restrict__ inPixels,
360                        ImageDimensions inputDimensions,
361                        unsigned char* __restrict__ outPixels,
362                        ImageDimensions desiredDimensions);
363
364 /**
365  * @brief Resamples the input image with the Lanczos algorithm.
366  *
367  * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
368  * separate buffer from the output buffer.
369  *
370  * @param[in] inPixels Pointer to the input image buffer.
371  * @param[in] inputDimensions The input dimensions of the image.
372  * @param[out] outPixels Pointer to the output image buffer.
373  * @param[in] desiredDimensions The output dimensions of the image.
374  */
375 void Resample(const unsigned char* __restrict__ inPixels,
376               ImageDimensions inputDimensions,
377               unsigned char* __restrict__ outPixels,
378               ImageDimensions   desiredDimensions,
379               Resampler::Filter filterType,
380               int               numChannels,
381               bool              hasAlpha);
382
383 /**
384  * @brief Rotates the input image with an implementation of the 'Rotate by Shear' algorithm.
385  *
386  * @pre @p pixelsIn must not alias @p pixelsOut. The input image should be a totally
387  * separate buffer from the output buffer.
388  *
389  * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free()
390  *
391  * @param[in] pixelsIn The input buffer.
392  * @param[in] widthIn The width of the input buffer.
393  * @param[in] heightIn The height of the input buffer.
394  * @param[in] pixelSize The size of the pixel.
395  * @param[in] radians The rotation angle in radians.
396  * @param[out] pixelsOut The rotated output buffer.
397  * @param[out] widthOut The width of the output buffer.
398  * @param[out] heightOut The height of the output buffer.
399  */
400 void RotateByShear(const uint8_t* const pixelsIn,
401                    unsigned int         widthIn,
402                    unsigned int         heightIn,
403                    unsigned int         pixelSize,
404                    float                radians,
405                    uint8_t*&            pixelsOut,
406                    unsigned int&        widthOut,
407                    unsigned int&        heightOut);
408
409 /**
410  * @brief Applies to the input image a horizontal shear transformation.
411  *
412  * @pre @p pixelsIn must not alias @p pixelsOut. The input image should be a totally
413  * separate buffer from the output buffer.
414  * @pre The maximun/minimum shear angle is +/-45 degrees (PI/4 around 0.79 radians).
415  *
416  * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free()
417  *
418  * @param[in] pixelsIn The input buffer.
419  * @param[in] widthIn The width of the input buffer.
420  * @param[in] heightIn The height of the input buffer.
421  * @param[in] pixelSize The size of the pixel.
422  * @param[in] radians The shear angle in radians.
423  * @param[out] pixelsOut The rotated output buffer.
424  * @param[out] widthOut The width of the output buffer.
425  * @param[out] heightOut The height of the output buffer.
426  */
427 void HorizontalShear(const uint8_t* const pixelsIn,
428                      unsigned int         widthIn,
429                      unsigned int         heightIn,
430                      unsigned int         pixelSize,
431                      float                radians,
432                      uint8_t*&            pixelsOut,
433                      unsigned int&        widthOut,
434                      unsigned int&        heightOut);
435
436 /**@}*/
437
438 /**
439  * @defgroup ScalingAlgorithmFragments Composable subunits of the scaling algorithms.
440  * @{
441  */
442
443 /**
444  * @brief Average adjacent pairs of pixels, overwriting the input array.
445  * @param[in,out] pixels The array of pixels to work on.
446  * @param[i]      width  The number of pixels in the array passed-in.
447  */
448 void HalveScanlineInPlaceRGB888(unsigned char* pixels, unsigned int width);
449
450 /**
451  * @copydoc HalveScanlineInPlaceRGB888
452  */
453 void HalveScanlineInPlaceRGBA8888(unsigned char* pixels, unsigned int width);
454
455 /**
456  * @copydoc HalveScanlineInPlaceRGB888
457  */
458 void HalveScanlineInPlaceRGB565(unsigned char* pixels, unsigned int width);
459
460 /**
461  * @copydoc HalveScanlineInPlaceRGB888
462  */
463 void HalveScanlineInPlace2Bytes(unsigned char* pixels, unsigned int width);
464
465 /**
466  * @copydoc HalveScanlineInPlaceRGB888
467  */
468 void HalveScanlineInPlace1Byte(unsigned char* pixels, unsigned int width);
469
470 /**
471  * @brief Average pixels at corresponding offsets in two scanlines.
472  *
473  * outputScanline is allowed to alias scanline1.
474  * @param[in] scanline1 First scanline of pixels to average.
475  * @param[in] scanline2 Second scanline of pixels to average.
476  * @param[out] outputScanline Destination for the averaged pixels.
477  * @param[in] width The widths of all the scanlines passed-in.
478  */
479 void AverageScanlines1(const unsigned char* scanline1,
480                        const unsigned char* scanline2,
481                        unsigned char*       outputScanline,
482                        /** Image width in pixels (1 byte == 1 pixel: e.g. lum8 or alpha8).*/
483                        unsigned int width);
484
485 /**
486  * @copydoc AverageScanlines1
487  */
488 void AverageScanlines2(const unsigned char* scanline1,
489                        const unsigned char* scanline2,
490                        unsigned char*       outputScanline,
491                        /** Image width in pixels (2 bytes == 1 pixel: e.g. lum8alpha8).*/
492                        unsigned int width);
493
494 /**
495  * @copydoc AverageScanlines1
496  */
497 void AverageScanlines3(const unsigned char* scanline1,
498                        const unsigned char* scanline2,
499                        unsigned char*       outputScanline,
500                        /** Image width in pixels (3 bytes == 1 pixel: e.g. RGB888).*/
501                        unsigned int width);
502
503 /**
504  * @copydoc AverageScanlines1
505  */
506 void AverageScanlinesRGBA8888(const unsigned char* scanline1,
507                               const unsigned char* scanline2,
508                               unsigned char*       outputScanline,
509                               unsigned int         width);
510
511 /**
512  * @copydoc AverageScanlines1
513  */
514 void AverageScanlinesRGB565(const unsigned char* scanline1,
515                             const unsigned char* scanline2,
516                             unsigned char*       outputScanline,
517                             unsigned int         width);
518 /**@}*/
519
520 /**
521  * @defgroup TestableInlines Inline functions exposed in header to allow unit testing.
522  * @{
523  */
524
525 /**
526  * @brief Average two integer arguments.
527  * @return The average of two uint arguments.
528  * @param[in] a First component to average.
529  * @param[in] b Second component to average.
530  **/
531 inline unsigned int AverageComponent(unsigned int a, unsigned int b)
532 {
533   unsigned int avg = (a + b) >> 1u;
534   return avg;
535 }
536
537 /**
538  * @brief Average a pair of RGBA8888 pixels.
539  * @return The average of two RGBA8888 pixels.
540  * @param[in] a First pixel to average.
541  * @param[in] b Second pixel to average
542  **/
543 inline uint32_t AveragePixelRGBA8888(uint32_t a, uint32_t b)
544 {
545   /**
546    * @code
547    * const unsigned int avg =
548    *   (AverageComponent((a & 0xff000000) >> 1u, (b & 0xff000000) >> 1u) << 1u) & 0xff000000) +
549    *   (AverageComponent(a & 0x00ff0000, b & 0x00ff0000) & 0x00ff0000) +
550    *   (AverageComponent(a & 0x0000ff00, b & 0x0000ff00) & 0x0000ff00) +
551    *   (AverageComponent(a & 0x000000ff, b & 0x000000ff);
552    * return avg;
553    * @endcode
554    */
555   return (((a ^ b) & 0xfefefefeu) >> 1) + (a & b);
556   ///@ToDo: Optimise for ARM using the single ARMV6 instruction: UHADD8  R4, R0, R5. This is not Neon. It runs in the normal integer pipeline so there is no downside like a stall moving between integer and copro.
557 }
558
559 /**
560  * @brief Average a pair of RGB565 pixels.
561  * @param a[in] Low 16 bits hold a color value as RGB565 to average with parameter b.
562  * @param b[in] Low 16 bits hold a color value as RGB565 to average with parameter a.
563  * @return The average color of the two RGB565 pixels passed in, in the low 16 bits of the returned value.
564  **/
565 inline uint32_t AveragePixelRGB565(uint32_t a, uint32_t b)
566 {
567   /**
568    * @code
569    * const unsigned int avg =
570    *   (AverageComponent(a & 0xf800, b & 0xf800) & 0xf800) +
571    *   (AverageComponent(a & 0x7e0, b & 0x7e0) & 0x7e0) +
572    *   (AverageComponent(a & 0x1f, b & 0x1f));
573    * return avg;
574    * @endcode
575    */
576   return (((a ^ b) & 0xf7deu) >> 1) + (a & b);
577 }
578
579 /** @return The weighted blend of two integers as a 16.16 fixed-point number, given a 0.16 fixed-point blending factor. */
580 inline unsigned int WeightedBlendIntToFixed1616(unsigned int a, unsigned int b, unsigned int fractBlend)
581 {
582   DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
583   const unsigned int weightedAFixed = a * (65535u - fractBlend);
584   const unsigned int weightedBFixed = b * fractBlend;
585   const unsigned     blended        = (weightedAFixed + weightedBFixed);
586   return blended;
587 }
588
589 /** @brief Blend two 16.16 inputs to give a 16.32 output. */
590 inline uint64_t WeightedBlendFixed1616ToFixed1632(unsigned int a, unsigned int b, unsigned int fractBlend)
591 {
592   DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
593   // Blend while promoting intermediates to 16.32 fixed point:
594   const uint64_t weightedAFixed = uint64_t(a) * (65535u - fractBlend);
595   const uint64_t weightedBFixed = uint64_t(b) * fractBlend;
596   const uint64_t blended        = (weightedAFixed + weightedBFixed);
597   return blended;
598 }
599
600 /**
601  * @brief Blend 4 taps into one value using horizontal and vertical weights.
602  */
603 inline unsigned int BilinearFilter1Component(unsigned int tl, unsigned int tr, unsigned int bl, unsigned int br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical)
604 {
605   DALI_ASSERT_DEBUG(fractBlendHorizontal <= 65535u && "Factor should be in 0.16 fixed-point.");
606   DALI_ASSERT_DEBUG(fractBlendVertical <= 65535u && "Factor should be in 0.16 fixed-point.");
607
608   const unsigned int topBlend   = WeightedBlendIntToFixed1616(tl, tr, fractBlendHorizontal);
609   const unsigned int botBlend   = WeightedBlendIntToFixed1616(bl, br, fractBlendHorizontal);
610   const uint64_t     blended2x2 = WeightedBlendFixed1616ToFixed1632(topBlend, botBlend, fractBlendVertical);
611   const unsigned int rounded    = (blended2x2 + (1u << 31u)) >> 32u;
612   return rounded;
613 }
614
615 /**@}*/
616
617 } /* namespace Platform */
618 } /* namespace Internal */
619 } /* namespace Dali */
620
621 #endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H */