2 * Copyright (c) 2022 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 #ifndef DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H
19 #define DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H
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>
37 * @brief Identify which combination of x and y dimensions matter in terminating iterative box filtering.
41 BoxDimensionTestEither,
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.
52 typedef Uint16Pair ImageDimensions;
55 * @brief Work out the true desired width and height, accounting for special
56 * rules for zeros in either or both input requested dimensions.
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.
62 ImageDimensions CalculateDesiredDimensions(ImageDimensions rawDimensions, ImageDimensions requestedDimensions);
65 * @defgroup BitmapOperations Bitmap-to-Bitmap Image operations.
70 * @brief Apply requested attributes to bitmap.
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,
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
83 Dali::Devel::PixelBuffer ApplyAttributesToBitmap(Dali::Devel::PixelBuffer bitmap, ImageDimensions dimensions, FittingMode::Type fittingMode = FittingMode::DEFAULT, SamplingMode::Type samplingMode = SamplingMode::DEFAULT);
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.
89 Dali::Devel::PixelBuffer DownscaleBitmap(Dali::Devel::PixelBuffer bitmap,
90 ImageDimensions desired,
91 FittingMode::Type fittingMode,
92 SamplingMode::Type samplingMode);
96 * @defgroup ImageBufferScalingAlgorithms Pixel buffer-level scaling algorithms.
101 * @brief Destructive in-place downscaling by a power of 2 factor.
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
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] inputStride The stride of the input image.
111 * @param[in] desiredWidth The width the client is requesting.
112 * @param[in] desiredHeight The height the client is requesting.
113 * @param[out] outWidth The resulting width after downscaling.
114 * @param[out] outHeight The resulting height after downscaling.
115 * @param[out] outStride The resulting stride after downscaling.
117 void DownscaleInPlacePow2(unsigned char* const pixels,
118 Pixel::Format pixelFormat,
119 unsigned int inputWidth,
120 unsigned int inputHeight,
121 unsigned int inputStride,
122 unsigned int desiredWidth,
123 unsigned int desiredHeight,
124 FittingMode::Type fittingMode,
125 SamplingMode::Type samplingMode,
128 unsigned& outStride);
131 * @brief Destructive in-place downscaling by a power of 2 factor.
133 * A box filter with a 2x2 kernel is repeatedly applied as long as the result
134 * of the next downscaling step would not be smaller than the desired
136 * @param[in,out] pixels The buffer both to read from and write the result to.
137 * @param[in] inputWidth The width of the input image.
138 * @param[in] inputHeight The height of the input image.
139 * @param[in] inputStride The stride of the input image.
140 * @param[in] desiredWidth The width the client is requesting.
141 * @param[in] desiredHeight The height the client is requesting.
142 * @param[out] outWidth The resulting width after downscaling.
143 * @param[out] outHeight The resulting height after downscaling.
144 * @param[out] outStride The resulting stride after downscaling.
146 void DownscaleInPlacePow2RGB888(unsigned char* pixels,
147 unsigned int inputWidth,
148 unsigned int inputHeight,
149 unsigned int inputStride,
150 unsigned int desiredWidth,
151 unsigned int desiredHeight,
152 BoxDimensionTest dimensionTest,
153 unsigned int& outWidth,
154 unsigned int& outHeight,
155 unsigned int& outStride);
158 * @copydoc DownscaleInPlacePow2RGB888
160 void DownscaleInPlacePow2RGBA8888(unsigned char* pixels,
161 unsigned int inputWidth,
162 unsigned int inputHeight,
163 unsigned int inputStride,
164 unsigned int desiredWidth,
165 unsigned int desiredHeight,
166 BoxDimensionTest dimensionTest,
167 unsigned int& outWidth,
168 unsigned int& outHeight,
169 unsigned int& outStride);
172 * @copydoc DownscaleInPlacePow2RGB888
174 * For the 2-byte packed 16 bit format RGB565.
176 void DownscaleInPlacePow2RGB565(unsigned char* pixels,
177 unsigned int inputWidth,
178 unsigned int inputHeight,
179 unsigned int inputStride,
180 unsigned int desiredWidth,
181 unsigned int desiredHeight,
182 BoxDimensionTest dimensionTest,
183 unsigned int& outWidth,
184 unsigned int& outHeight,
185 unsigned int& outStride);
188 * @copydoc DownscaleInPlacePow2RGB888
190 * For 2-byte formats such as lum8alpha8, but not packed 16 bit formats like RGB565.
192 void DownscaleInPlacePow2ComponentPair(unsigned char* pixels,
193 unsigned int inputWidth,
194 unsigned int inputHeight,
195 unsigned int inputStride,
196 unsigned int desiredWidth,
197 unsigned int desiredHeight,
198 BoxDimensionTest dimensionTest,
199 unsigned int& outWidth,
200 unsigned int& outHeight,
201 unsigned int& outStride);
204 * @copydoc DownscaleInPlacePow2RGB888
206 * For single-byte formats such as lum8 or alpha8.
208 void DownscaleInPlacePow2SingleBytePerPixel(unsigned char* pixels,
209 unsigned int inputWidth,
210 unsigned int inputHeight,
211 unsigned int inputStride,
212 unsigned int desiredWidth,
213 unsigned int desiredHeight,
214 BoxDimensionTest dimensionTest,
215 unsigned int& outWidth,
216 unsigned int& outHeight,
217 unsigned int& outStride);
220 * @brief Rescales an input image into the exact output dimensions passed-in.
222 * Uses point sampling, equivalent to GL_NEAREST texture filter mode, for the
223 * fastest results, at the expense of aliasing (noisy images) when downscaling.
224 * @note inPixels is allowed to alias outPixels if this is a downscaling,
225 * but not for upscaling.
227 void PointSample(const unsigned char* inPixels,
228 unsigned int inputWidth,
229 unsigned int inputHeight,
230 unsigned int inputStride,
231 Pixel::Format pixelFormat,
232 unsigned char* outPixels,
233 unsigned int desiredWidth,
234 unsigned int desiredHeight);
237 * @copydoc PointSample
239 * Specialised for 4-byte formats like RGBA8888 and BGRA8888.
241 void PointSample4BPP(const unsigned char* inPixels,
242 unsigned int inputWidth,
243 unsigned int inputHeight,
244 unsigned int inputStride,
245 unsigned char* outPixels,
246 unsigned int desiredWidth,
247 unsigned int desiredHeight);
250 * @copydoc PointSample
252 * Specialised for 3-byte formats like RGB888 and BGR888.
254 void PointSample3BPP(const unsigned char* inPixels,
255 unsigned int inputWidth,
256 unsigned int inputHeight,
257 unsigned int inputStride,
258 unsigned char* outPixels,
259 unsigned int desiredWidth,
260 unsigned int desiredHeight);
263 * @copydoc PointSample
265 * Specialised for 2-byte formats like LA88.
267 void PointSample2BPP(const unsigned char* inPixels,
268 unsigned int inputWidth,
269 unsigned int inputHeight,
270 unsigned int inputStride,
271 unsigned char* outPixels,
272 unsigned int desiredWidth,
273 unsigned int desiredHeight);
276 * @copydoc PointSample
278 * Specialised for 1-byte formats like L8 and A8.
280 void PointSample1BPP(const unsigned char* inPixels,
281 unsigned int inputWidth,
282 unsigned int inputHeight,
283 unsigned int inputStride,
284 unsigned char* outPixels,
285 unsigned int desiredWidth,
286 unsigned int desiredHeight);
289 * @brief Resample input image to output image using a bilinear filter.
291 * Each output pixel is formed of a weighted sum of a 2x2 block of four input
293 * @pre inPixels must not alias outPixels. The input image should be a totally
294 * separate buffer from the input one.
296 void LinearSample(const unsigned char* __restrict__ inPixels,
297 ImageDimensions inDimensions,
298 unsigned int inStride,
299 Pixel::Format pixelFormat,
300 unsigned char* __restrict__ outPixels,
301 ImageDimensions outDimensions);
304 * @copydoc LinearSample
306 * Specialised for one byte per pixel formats.
308 void LinearSample1BPP(const unsigned char* __restrict__ inPixels,
309 ImageDimensions inputDimensions,
310 unsigned int inputStride,
311 unsigned char* __restrict__ outPixels,
312 ImageDimensions desiredDimensions);
315 * @copydoc LinearSample
317 * Specialised for two byte per pixel formats.
319 void LinearSample2BPP(const unsigned char* __restrict__ inPixels,
320 ImageDimensions inputDimensions,
321 unsigned int inputStride,
322 unsigned char* __restrict__ outPixels,
323 ImageDimensions desiredDimensions);
326 * @copydoc LinearSample
328 * Specialised for RGB565 16 bit pixel format.
330 void LinearSampleRGB565(const unsigned char* __restrict__ inPixels,
331 ImageDimensions inputDimensions,
332 unsigned int inputStride,
333 unsigned char* __restrict__ outPixels,
334 ImageDimensions desiredDimensions);
337 * @copydoc LinearSample
339 * Specialised for three byte per pixel formats like RGB888.
341 void LinearSample3BPP(const unsigned char* __restrict__ inPixels,
342 ImageDimensions inputDimensions,
343 unsigned int inputStride,
344 unsigned char* __restrict__ outPixels,
345 ImageDimensions desiredDimensions);
348 * @copydoc LinearSample
350 * Specialised for four byte per pixel formats like RGBA8888.
351 * @note, If used on RGBA8888, the A component will be blended independently.
353 void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
354 ImageDimensions inputDimensions,
355 unsigned int inputStride,
356 unsigned char* __restrict__ outPixels,
357 ImageDimensions desiredDimensions);
360 * @brief Resamples the input image with the Lanczos algorithm.
362 * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
363 * separate buffer from the output buffer.
365 * @param[in] inPixels Pointer to the input image buffer.
366 * @param[in] inputDimensions The input dimensions of the image.
367 * @param[in] inputStride The input stride of the image.
368 * @param[out] outPixels Pointer to the output image buffer.
369 * @param[in] desiredDimensions The output dimensions of the image.
371 void LanczosSample4BPP(const unsigned char* __restrict__ inPixels,
372 ImageDimensions inputDimensions,
373 unsigned int inputStride,
374 unsigned char* __restrict__ outPixels,
375 ImageDimensions desiredDimensions);
378 * @brief Resamples the input image with the Lanczos algorithm.
380 * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
381 * separate buffer from the output buffer.
383 * @param[in] inPixels Pointer to the input image buffer.
384 * @param[in] inputDimensions The input dimensions of the image.
385 * @param[in] inputStride The input stride of the image.
386 * @param[out] outPixels Pointer to the output image buffer.
387 * @param[in] desiredDimensions The output dimensions of the image.
389 void LanczosSample1BPP(const unsigned char* __restrict__ inPixels,
390 ImageDimensions inputDimensions,
391 unsigned int inputStride,
392 unsigned char* __restrict__ outPixels,
393 ImageDimensions desiredDimensions);
396 * @brief Resamples the input image with the Lanczos algorithm.
398 * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
399 * separate buffer from the output buffer.
401 * @param[in] inPixels Pointer to the input image buffer.
402 * @param[in] inputDimensions The input dimensions of the image.
403 * @param[in] inputStride The input stride of the image.
404 * @param[out] outPixels Pointer to the output image buffer.
405 * @param[in] desiredDimensions The output dimensions of the image.
407 void Resample(const unsigned char* __restrict__ inPixels,
408 ImageDimensions inputDimensions,
409 unsigned int inputStride,
410 unsigned char* __restrict__ outPixels,
411 ImageDimensions desiredDimensions,
412 Resampler::Filter filterType,
417 * @brief Rotates the input image with an implementation of the 'Rotate by Shear' algorithm.
419 * @pre @p pixelsIn must not alias @p pixelsOut. The input image should be a totally
420 * separate buffer from the output buffer.
422 * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free()
424 * @param[in] pixelsIn The input buffer.
425 * @param[in] widthIn The width of the input buffer.
426 * @param[in] heightIn The height of the input buffer.
427 * @param[in] strideIn The stride of the input buffer.
428 * @param[in] pixelSize The size of the pixel.
429 * @param[in] radians The rotation angle in radians.
430 * @param[out] pixelsOut The rotated output buffer.
431 * @param[out] widthOut The width of the output buffer.
432 * @param[out] heightOut The height of the output buffer.
434 void RotateByShear(const uint8_t* const pixelsIn,
435 unsigned int widthIn,
436 unsigned int heightIn,
437 unsigned int strideIn,
438 unsigned int pixelSize,
441 unsigned int& widthOut,
442 unsigned int& heightOut);
445 * @brief Applies to the input image a horizontal shear transformation.
447 * @pre @p pixelsIn must not alias @p pixelsOut. The input image should be a totally
448 * separate buffer from the output buffer.
449 * @pre The maximun/minimum shear angle is +/-45 degrees (PI/4 around 0.79 radians).
451 * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free()
453 * @param[in] pixelsIn The input buffer.
454 * @param[in] widthIn The width of the input buffer.
455 * @param[in] heightIn The height of the input buffer.
456 * @param[in] strideIn The stride of the input buffer.
457 * @param[in] pixelSize The size of the pixel.
458 * @param[in] radians The shear angle in radians.
459 * @param[out] pixelsOut The rotated output buffer.
460 * @param[out] widthOut The width of the output buffer.
461 * @param[out] heightOut The height of the output buffer.
463 void HorizontalShear(const uint8_t* const pixelsIn,
464 unsigned int widthIn,
465 unsigned int heightIn,
466 unsigned int strideIn,
467 unsigned int pixelSize,
470 unsigned int& widthOut,
471 unsigned int& heightOut);
476 * @defgroup ScalingAlgorithmFragments Composable subunits of the scaling algorithms.
481 * @brief Average adjacent pairs of pixels, overwriting the input array.
482 * @param[in,out] pixels The array of pixels to work on.
483 * @param[i] width The number of pixels in the array passed-in.
485 void HalveScanlineInPlaceRGB888(unsigned char* pixels, unsigned int width);
488 * @copydoc HalveScanlineInPlaceRGB888
490 void HalveScanlineInPlaceRGBA8888(unsigned char* pixels, unsigned int width);
493 * @copydoc HalveScanlineInPlaceRGB888
495 void HalveScanlineInPlaceRGB565(unsigned char* pixels, unsigned int width);
498 * @copydoc HalveScanlineInPlaceRGB888
500 void HalveScanlineInPlace2Bytes(unsigned char* pixels, unsigned int width);
503 * @copydoc HalveScanlineInPlaceRGB888
505 void HalveScanlineInPlace1Byte(unsigned char* pixels, unsigned int width);
508 * @brief Average pixels at corresponding offsets in two scanlines.
510 * outputScanline is allowed to alias scanline1.
511 * @param[in] scanline1 First scanline of pixels to average.
512 * @param[in] scanline2 Second scanline of pixels to average.
513 * @param[out] outputScanline Destination for the averaged pixels.
514 * @param[in] width The widths of all the scanlines passed-in.
516 void AverageScanlines1(const unsigned char* scanline1,
517 const unsigned char* scanline2,
518 unsigned char* outputScanline,
519 /** Image width in pixels (1 byte == 1 pixel: e.g. lum8 or alpha8).*/
523 * @copydoc AverageScanlines1
525 void AverageScanlines2(const unsigned char* scanline1,
526 const unsigned char* scanline2,
527 unsigned char* outputScanline,
528 /** Image width in pixels (2 bytes == 1 pixel: e.g. lum8alpha8).*/
532 * @copydoc AverageScanlines1
534 void AverageScanlines3(const unsigned char* scanline1,
535 const unsigned char* scanline2,
536 unsigned char* outputScanline,
537 /** Image width in pixels (3 bytes == 1 pixel: e.g. RGB888).*/
541 * @copydoc AverageScanlines1
543 void AverageScanlinesRGBA8888(const unsigned char* scanline1,
544 const unsigned char* scanline2,
545 unsigned char* outputScanline,
549 * @copydoc AverageScanlines1
551 void AverageScanlinesRGB565(const unsigned char* scanline1,
552 const unsigned char* scanline2,
553 unsigned char* outputScanline,
558 * @defgroup TestableInlines Inline functions exposed in header to allow unit testing.
563 * @brief Average two integer arguments.
564 * @return The average of two uint arguments.
565 * @param[in] a First component to average.
566 * @param[in] b Second component to average.
568 inline unsigned int AverageComponent(unsigned int a, unsigned int b)
570 unsigned int avg = (a + b) >> 1u;
575 * @brief Average a pair of RGBA8888 pixels.
576 * @return The average of two RGBA8888 pixels.
577 * @param[in] a First pixel to average.
578 * @param[in] b Second pixel to average
580 inline uint32_t AveragePixelRGBA8888(uint32_t a, uint32_t b)
584 * const unsigned int avg =
585 * (AverageComponent((a & 0xff000000) >> 1u, (b & 0xff000000) >> 1u) << 1u) & 0xff000000) +
586 * (AverageComponent(a & 0x00ff0000, b & 0x00ff0000) & 0x00ff0000) +
587 * (AverageComponent(a & 0x0000ff00, b & 0x0000ff00) & 0x0000ff00) +
588 * (AverageComponent(a & 0x000000ff, b & 0x000000ff);
592 return (((a ^ b) & 0xfefefefeu) >> 1) + (a & b);
593 ///@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.
597 * @brief Average a pair of RGB565 pixels.
598 * @param a[in] Low 16 bits hold a color value as RGB565 to average with parameter b.
599 * @param b[in] Low 16 bits hold a color value as RGB565 to average with parameter a.
600 * @return The average color of the two RGB565 pixels passed in, in the low 16 bits of the returned value.
602 inline uint32_t AveragePixelRGB565(uint32_t a, uint32_t b)
606 * const unsigned int avg =
607 * (AverageComponent(a & 0xf800, b & 0xf800) & 0xf800) +
608 * (AverageComponent(a & 0x7e0, b & 0x7e0) & 0x7e0) +
609 * (AverageComponent(a & 0x1f, b & 0x1f));
613 return (((a ^ b) & 0xf7deu) >> 1) + (a & b);
616 /** @return The weighted blend of two integers as a 16.16 fixed-point number, given a 0.16 fixed-point blending factor. */
617 inline unsigned int WeightedBlendIntToFixed1616(unsigned int a, unsigned int b, unsigned int fractBlend)
619 DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
622 * const unsigned int weightedAFixed = a * (65535u - fractBlend);
623 * const unsigned int weightedBFixed = b * fractBlend;
624 * const unsigned blended = (weightedAFixed + weightedBFixed);
627 const unsigned int blended = (a << 16) - a + (static_cast<int32_t>(b) - static_cast<int32_t>(a)) * fractBlend;
631 /** @brief Blend two 16.16 inputs to give a 16.32 output. */
632 inline uint64_t WeightedBlendFixed1616ToFixed1632(unsigned int a, unsigned int b, unsigned int fractBlend)
634 DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
637 * // Blend while promoting intermediates to 16.32 fixed point:
638 * const uint64_t weightedAFixed = uint64_t(a) * (65535u - fractBlend);
639 * const uint64_t weightedBFixed = uint64_t(b) * fractBlend;
640 * const uint64_t blended = (weightedAFixed + weightedBFixed);
643 const uint64_t blended = (static_cast<uint64_t>(a) << 16) - a + (static_cast<int64_t>(b) - static_cast<int64_t>(a)) * fractBlend;
648 * @brief Blend 4 taps into one value using horizontal and vertical weights.
650 inline unsigned int BilinearFilter1Component(unsigned int tl, unsigned int tr, unsigned int bl, unsigned int br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical)
652 DALI_ASSERT_DEBUG(fractBlendHorizontal <= 65535u && "Factor should be in 0.16 fixed-point.");
653 DALI_ASSERT_DEBUG(fractBlendVertical <= 65535u && "Factor should be in 0.16 fixed-point.");
657 * const unsigned int topBlend = WeightedBlendIntToFixed1616(tl, tr, fractBlendHorizontal);
658 * const unsigned int botBlend = WeightedBlendIntToFixed1616(bl, br, fractBlendHorizontal);
659 * const uint64_t blended2x2 = WeightedBlendFixed1616ToFixed1632(topBlend, botBlend, fractBlendVertical);
660 * const unsigned int rounded = (blended2x2 + (1u << 31u)) >> 32u;
665 * Hard-coding optimize!
667 * Let p = 65536, s.t we can optimze it as << 16.
668 * Let x = fractBlendHorizontal, y = fractBlendVertical.
669 * topBlend = (tl*p - tl - tl*x + tr*x)
670 * botBlend = (bl*p - bl - bl*x + br*x)
671 * blended2x2 = topBlend*p - topBlend - topBlend*y + botBlend*y
673 * And now we can split all values.
674 * tl*p*p - tl*p - tl*x*p + tr*x*p - tl*p + tl + tl*x - tr*x - tl*y*p + tl*y + tl*x*y - tr*x*y + bl*y*p - bl*y - bl*x*y + br*x*y;
675 * --> (collect by p, x, and y)
676 * (tl)*p*p + (-2tl + (-tl + tr)*x + (-tl+bl)*y)*p + tl + (tl - tr)*x + (tl - bl)*y + (tl - tr - bl + br)*x*y
680 * C = (tl - tr - bl + br) * x * y;
683 * (tl << 32) - (D << 16) + tl + A + B + C
685 * Becareful of overflow and negative value.
687 const int32_t A = (static_cast<int32_t>(tl) - static_cast<int32_t>(tr)) * static_cast<int32_t>(fractBlendHorizontal);
688 const int32_t B = (static_cast<int32_t>(tl) - static_cast<int32_t>(bl)) * static_cast<int32_t>(fractBlendVertical);
689 const int64_t C = (static_cast<int64_t>(tl) - static_cast<int64_t>(tr) - static_cast<int64_t>(bl) + static_cast<int64_t>(br)) * static_cast<int64_t>(fractBlendHorizontal) * static_cast<int64_t>(fractBlendVertical);
690 const int64_t D = ((static_cast<int64_t>(tl) << 1) + A + B);
692 const uint64_t blended2x2 = (static_cast<int64_t>(tl) << 32u) - (D << 16u) + tl + A + B + C;
693 const unsigned int rounded = (blended2x2 + (1u << 31u)) >> 32u;
699 } /* namespace Platform */
700 } /* namespace Internal */
701 } /* namespace Dali */
703 #endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H */