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] 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.
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,
124 unsigned& outHeight);
127 * @brief Destructive in-place downscaling by a power of 2 factor.
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
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.
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);
150 * @copydoc DownscaleInPlacePow2RGB888
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);
162 * @copydoc DownscaleInPlacePow2RGB888
164 * For the 2-byte packed 16 bit format RGB565.
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);
176 * @copydoc DownscaleInPlacePow2RGB888
178 * For 2-byte formats such as lum8alpha8, but not packed 16 bit formats like RGB565.
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);
190 * @copydoc DownscaleInPlacePow2RGB888
192 * For single-byte formats such as lum8 or alpha8.
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);
204 * @brief Rescales an input image into the exact output dimensions passed-in.
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.
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);
220 * @copydoc PointSample
222 * Specialised for 4-byte formats like RGBA8888 and BGRA8888.
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);
232 * @copydoc PointSample
234 * Specialised for 3-byte formats like RGB888 and BGR888.
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);
244 * @copydoc PointSample
246 * Specialised for 2-byte formats like LA88.
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);
256 * @copydoc PointSample
258 * Specialised for 1-byte formats like L8 and A8.
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);
268 * @brief Resample input image to output image using a bilinear filter.
270 * Each output pixel is formed of a weighted sum of a 2x2 block of four input
272 * @pre inPixels must not alias outPixels. The input image should be a totally
273 * separate buffer from the input one.
275 void LinearSample(const unsigned char* __restrict__ inPixels,
276 ImageDimensions inDimensions,
277 Pixel::Format pixelFormat,
278 unsigned char* __restrict__ outPixels,
279 ImageDimensions outDimensions);
282 * @copydoc LinearSample
284 * Specialised for one byte per pixel formats.
286 void LinearSample1BPP(const unsigned char* __restrict__ inPixels,
287 ImageDimensions inputDimensions,
288 unsigned char* __restrict__ outPixels,
289 ImageDimensions desiredDimensions);
292 * @copydoc LinearSample
294 * Specialised for two byte per pixel formats.
296 void LinearSample2BPP(const unsigned char* __restrict__ inPixels,
297 ImageDimensions inputDimensions,
298 unsigned char* __restrict__ outPixels,
299 ImageDimensions desiredDimensions);
302 * @copydoc LinearSample
304 * Specialised for RGB565 16 bit pixel format.
306 void LinearSampleRGB565(const unsigned char* __restrict__ inPixels,
307 ImageDimensions inputDimensions,
308 unsigned char* __restrict__ outPixels,
309 ImageDimensions desiredDimensions);
312 * @copydoc LinearSample
314 * Specialised for three byte per pixel formats like RGB888.
316 void LinearSample3BPP(const unsigned char* __restrict__ inPixels,
317 ImageDimensions inputDimensions,
318 unsigned char* __restrict__ outPixels,
319 ImageDimensions desiredDimensions);
322 * @copydoc LinearSample
324 * Specialised for four byte per pixel formats like RGBA8888.
325 * @note, If used on RGBA8888, the A component will be blended independently.
327 void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
328 ImageDimensions inputDimensions,
329 unsigned char* __restrict__ outPixels,
330 ImageDimensions desiredDimensions);
333 * @brief Resamples the input image with the Lanczos algorithm.
335 * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
336 * separate buffer from the output buffer.
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.
343 void LanczosSample4BPP(const unsigned char* __restrict__ inPixels,
344 ImageDimensions inputDimensions,
345 unsigned char* __restrict__ outPixels,
346 ImageDimensions desiredDimensions);
349 * @brief Resamples the input image with the Lanczos algorithm.
351 * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
352 * separate buffer from the output buffer.
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.
359 void LanczosSample1BPP(const unsigned char* __restrict__ inPixels,
360 ImageDimensions inputDimensions,
361 unsigned char* __restrict__ outPixels,
362 ImageDimensions desiredDimensions);
365 * @brief Resamples the input image with the Lanczos algorithm.
367 * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
368 * separate buffer from the output buffer.
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.
375 void Resample(const unsigned char* __restrict__ inPixels,
376 ImageDimensions inputDimensions,
377 unsigned char* __restrict__ outPixels,
378 ImageDimensions desiredDimensions,
379 Resampler::Filter filterType,
384 * @brief Rotates the input image with an implementation of the 'Rotate by Shear' algorithm.
386 * @pre @p pixelsIn must not alias @p pixelsOut. The input image should be a totally
387 * separate buffer from the output buffer.
389 * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free()
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.
400 void RotateByShear(const uint8_t* const pixelsIn,
401 unsigned int widthIn,
402 unsigned int heightIn,
403 unsigned int pixelSize,
406 unsigned int& widthOut,
407 unsigned int& heightOut);
410 * @brief Applies to the input image a horizontal shear transformation.
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).
416 * @note This function allocates memory in @p pixelsOut which has to be released by calling @e free()
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.
427 void HorizontalShear(const uint8_t* const pixelsIn,
428 unsigned int widthIn,
429 unsigned int heightIn,
430 unsigned int pixelSize,
433 unsigned int& widthOut,
434 unsigned int& heightOut);
439 * @defgroup ScalingAlgorithmFragments Composable subunits of the scaling algorithms.
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.
448 void HalveScanlineInPlaceRGB888(unsigned char* pixels, unsigned int width);
451 * @copydoc HalveScanlineInPlaceRGB888
453 void HalveScanlineInPlaceRGBA8888(unsigned char* pixels, unsigned int width);
456 * @copydoc HalveScanlineInPlaceRGB888
458 void HalveScanlineInPlaceRGB565(unsigned char* pixels, unsigned int width);
461 * @copydoc HalveScanlineInPlaceRGB888
463 void HalveScanlineInPlace2Bytes(unsigned char* pixels, unsigned int width);
466 * @copydoc HalveScanlineInPlaceRGB888
468 void HalveScanlineInPlace1Byte(unsigned char* pixels, unsigned int width);
471 * @brief Average pixels at corresponding offsets in two scanlines.
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.
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).*/
486 * @copydoc AverageScanlines1
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).*/
495 * @copydoc AverageScanlines1
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).*/
504 * @copydoc AverageScanlines1
506 void AverageScanlinesRGBA8888(const unsigned char* scanline1,
507 const unsigned char* scanline2,
508 unsigned char* outputScanline,
512 * @copydoc AverageScanlines1
514 void AverageScanlinesRGB565(const unsigned char* scanline1,
515 const unsigned char* scanline2,
516 unsigned char* outputScanline,
521 * @defgroup TestableInlines Inline functions exposed in header to allow unit testing.
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.
531 inline unsigned int AverageComponent(unsigned int a, unsigned int b)
533 unsigned int avg = (a + b) >> 1u;
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
543 inline uint32_t AveragePixelRGBA8888(uint32_t a, uint32_t b)
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);
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.
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.
565 inline uint32_t AveragePixelRGB565(uint32_t a, uint32_t b)
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));
576 return (((a ^ b) & 0xf7deu) >> 1) + (a & b);
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)
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);
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)
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);
601 * @brief Blend 4 taps into one value using horizontal and vertical weights.
603 inline unsigned int BilinearFilter1Component(unsigned int tl, unsigned int tr, unsigned int bl, unsigned int br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical)
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.");
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;
617 } /* namespace Platform */
618 } /* namespace Internal */
619 } /* namespace Dali */
621 #endif /* DALI_INTERNAL_PLATFORM_IMAGE_OPERATIONS_H */