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