Merge "Fix of uninitialized dimensionTest error error: 'dimensionTest' may be used...
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / portable / image-operations.cpp
1 /*
2  * Copyright (c) 2014 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 // INTERNAL INCLUDES
19 #include "image-operations.h"
20 #include <dali/integration-api/debug.h>
21 #include <dali/public-api/common/ref-counted-dali-vector.h>
22 #include <dali/public-api/images/image-attributes.h>
23 #include <dali/integration-api/bitmap.h>
24
25 // EXTERNAL INCLUDES
26 #include <cstring>
27
28 namespace Dali
29 {
30 namespace Internal
31 {
32 namespace Platform
33 {
34
35 namespace
36 {
37 using Integration::Bitmap;
38 using Integration::BitmapPtr;
39 typedef unsigned char PixelBuffer;
40
41 #if defined(DEBUG_ENABLED)
42 /**
43  * Disable logging of image operations or make it verbose from the commandline
44  * as follows (e.g., for dali demo app):
45  * <code>
46  * LOG_IMAGE_OPERATIONS=0 dali-demo #< off
47  * LOG_IMAGE_OPERATIONS=3 dali-demo #< on, verbose
48  * </code>
49  */
50 Debug::Filter* gImageOpsLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_IMAGE_OPERATIONS" );
51 #endif
52
53 /** @return The greatest even number less than or equal to the argument. */
54 inline unsigned int EvenDown( const unsigned int a )
55 {
56   const unsigned int evened = a & ~1u;
57   return evened;
58 }
59
60 /**
61  * @brief Log bad parameters.
62  */
63 void ValidateScalingParameters(
64   const unsigned int inputWidth,    const unsigned int inputHeight,
65   const unsigned int desiredWidth, const unsigned int desiredHeight )
66 {
67   if( desiredWidth > inputWidth || desiredHeight > inputHeight )
68   {
69     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Upscaling not supported (%u, %u -> %u, %u).\n", inputWidth, inputHeight, desiredWidth, desiredHeight );
70   }
71
72   if( desiredWidth == 0u || desiredHeight == 0u )
73   {
74     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Downscaling to a zero-area target is pointless." );
75   }
76
77   if( inputWidth == 0u || inputHeight == 0u )
78   {
79     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Zero area images cannot be scaled" );
80   }
81 }
82
83 /**
84  * @brief Do debug assertions common to all scanline halving functions.
85  * @note Inline and in anon namespace so should boil away in release builds.
86  */
87 inline void DebugAssertScanlineParameters(
88     unsigned char * const pixels,
89     const unsigned int width )
90 {
91   DALI_ASSERT_DEBUG( pixels && "Null pointer." );
92   DALI_ASSERT_DEBUG( width > 1u && "Can't average fewer than two pixels." );
93   DALI_ASSERT_DEBUG( width < 131072u && "Unusually wide image: are you sure you meant to pass that value in?" );
94 }
95
96 /**
97  * @brief Assertions on params to functions averaging pairs of scanlines.
98  */
99 inline void DebugAssertDualScanlineParameters(
100     const unsigned char * const scanline1,
101     const unsigned char * const scanline2,
102     unsigned char* const outputScanline,
103     const size_t widthInComponents )
104 {
105   DALI_ASSERT_DEBUG( scanline1 && "Null pointer." );
106   DALI_ASSERT_DEBUG( scanline2 && "Null pointer." );
107   DALI_ASSERT_DEBUG( outputScanline && "Null pointer." );
108   DALI_ASSERT_DEBUG( ((scanline1 >= scanline2 + widthInComponents) || (scanline2 >= scanline1 + widthInComponents )) && "Scanlines alias." );
109   DALI_ASSERT_DEBUG( ((((void*)outputScanline) >= (void*)(scanline2 + widthInComponents)) || (((void*)scanline2) >= (void*)(scanline1 + widthInComponents))) && "Scanline 2 aliases output." );
110 }
111
112 } // namespace - unnamed
113
114 /**
115  * @brief Implement ImageAttributes::ScaleTofill scaling mode.
116  *
117  * Implement the ImageAttributes::ScaleToFill mode, returning a new bitmap with the aspect ratio specified by the scaling mode.
118  * @note This fakes the scaling with a crop and relies on the GPU scaling at
119  * render time. If the input bitmap was previously maximally downscaled using a
120  * repeated box filter, this is a reasonable approach.
121  * @return The bitmap passed in if no scaling is needed or possible, else a new,
122  * smaller bitmap with the scaling mode applied.
123  */
124 Integration::BitmapPtr ProcessBitmapScaleToFill( Integration::BitmapPtr bitmap, const ImageAttributes& requestedAttributes );
125
126
127 BitmapPtr ApplyAttributesToBitmap( BitmapPtr bitmap, const ImageAttributes& requestedAttributes )
128 {
129   // If a different size than the raw one has been requested, resize the image
130   // maximally using a repeated box filter without making it smaller than the
131   // requested size in either dimension:
132   if( bitmap )
133   {
134     bitmap = DownscaleBitmap( *bitmap, requestedAttributes );
135   }
136
137   // Cut the bitmap according to the desired width and height so that the
138   // resulting bitmap has the same aspect ratio as the desired dimensions:
139   if( bitmap && bitmap->GetPackedPixelsProfile() && requestedAttributes.GetScalingMode() == ImageAttributes::ScaleToFill )
140   {
141     bitmap = ProcessBitmapScaleToFill( bitmap, requestedAttributes );
142   }
143
144   // Examine the image pixels remaining after cropping and scaling to see if all
145   // are opaque, allowing faster rendering, or some have non-1.0 alpha:
146   if( bitmap && bitmap->GetPackedPixelsProfile() && Pixel::HasAlpha( bitmap->GetPixelFormat() ) )
147   {
148     bitmap->GetPackedPixelsProfile()->TestForTransparency();
149   }
150   return bitmap;
151 }
152
153 BitmapPtr ProcessBitmapScaleToFill( BitmapPtr bitmap, const ImageAttributes& requestedAttributes )
154 {
155   const unsigned loadedWidth = bitmap->GetImageWidth();
156   const unsigned loadedHeight = bitmap->GetImageHeight();
157   const unsigned desiredWidth = requestedAttributes.GetWidth();
158   const unsigned desiredHeight = requestedAttributes.GetHeight();
159
160   if( desiredWidth < 1U || desiredHeight < 1U )
161   {
162     DALI_LOG_WARNING( "Image scaling aborted as desired dimensions too small (%u, %u)\n.", desiredWidth, desiredHeight );
163   }
164   else if( loadedWidth != desiredWidth || loadedHeight != desiredHeight )
165   {
166     const Vector2 desiredDims( desiredWidth, desiredHeight );
167
168     // Scale the desired rectangle back to fit inside the rectangle of the loaded bitmap:
169     // There are two candidates (scaled by x, and scaled by y) and we choose the smallest area one.
170     const float widthsRatio = loadedWidth / float(desiredWidth);
171     const Vector2 scaledByWidth = desiredDims * widthsRatio;
172     const float heightsRatio = loadedHeight / float(desiredHeight);
173     const Vector2 scaledByHeight = desiredDims * heightsRatio;
174     // Trim top and bottom if the area of the horizontally-fitted candidate is less, else trim the sides:
175     const bool trimTopAndBottom = scaledByWidth.width * scaledByWidth.height < scaledByHeight.width * scaledByHeight.height;
176     const Vector2 scaledDims = trimTopAndBottom ? scaledByWidth : scaledByHeight;
177
178     // Work out how many pixels to trim from top and bottom, and left and right:
179     // (We only ever do one dimension)
180     const unsigned scanlinesToTrim = trimTopAndBottom ? fabsf( (scaledDims.y - loadedHeight) * 0.5f ) : 0;
181     const unsigned columnsToTrim = trimTopAndBottom ? 0 : fabsf( (scaledDims.x - loadedWidth) * 0.5f );
182
183     DALI_LOG_INFO( gImageOpsLogFilter, Debug::Concise, "Bitmap, desired(%f, %f), loaded(%u,%u), cut_target(%f, %f), trimmed(%u, %u), vertical = %s.\n", desiredDims.x, desiredDims.y, loadedWidth, loadedHeight, scaledDims.x, scaledDims.y, columnsToTrim, scanlinesToTrim, trimTopAndBottom ? "true" : "false" );
184
185     // Make a new bitmap with the central part of the loaded one if required:
186     if( scanlinesToTrim > 0 || columnsToTrim > 0 )
187     {
188       const unsigned newWidth = loadedWidth - 2 * columnsToTrim;
189       const unsigned newHeight = loadedHeight - 2 * scanlinesToTrim;
190       BitmapPtr croppedBitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::DISCARD );
191       Integration::Bitmap::PackedPixelsProfile * packedView = croppedBitmap->GetPackedPixelsProfile();
192       DALI_ASSERT_DEBUG( packedView );
193       const Pixel::Format pixelFormat = bitmap->GetPixelFormat();
194       packedView->ReserveBuffer( pixelFormat, newWidth, newHeight, newWidth, newHeight );
195
196       const unsigned bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
197
198       const PixelBuffer * const srcPixels = bitmap->GetBuffer() + scanlinesToTrim * loadedWidth * bytesPerPixel;
199       PixelBuffer * const destPixels = croppedBitmap->GetBuffer();
200       DALI_ASSERT_DEBUG( srcPixels && destPixels );
201
202       // Optimize to a single memcpy if the left and right edges don't need a crop, else copy a scanline at a time:
203       if( trimTopAndBottom )
204       {
205         memcpy( destPixels, srcPixels, newHeight * newWidth * bytesPerPixel );
206       }
207       else
208       {
209         for( unsigned y = 0; y < newHeight; ++y )
210         {
211           memcpy( &destPixels[y * newWidth * bytesPerPixel], &srcPixels[y * loadedWidth * bytesPerPixel + columnsToTrim * bytesPerPixel], newWidth * bytesPerPixel );
212         }
213       }
214
215       // Overwrite the loaded bitmap with the cropped version:
216       bitmap = croppedBitmap;
217     }
218   }
219
220   return bitmap;
221 }
222
223 namespace
224 {
225
226 /**
227  * @brief Converts a scaling mode to the definition of which dimensions matter when box filtering as a part of that mode.
228  */
229 BoxDimensionTest DimensionTestForScalingMode( ImageAttributes::ScalingMode scalingMode )
230 {
231   BoxDimensionTest dimensionTest;
232   dimensionTest = BoxDimensionTestEither;
233
234   switch( scalingMode )
235   {
236     // Shrink to fit attempts to make one or zero dimensions smaller than the
237     // desired dimensions and one or two dimensions exactly the same as the desired
238     // ones, so as long as one dimension is larger than the desired size, box
239     // filtering can continue even if the second dimension is smaller than the
240     // desired dimensions:
241     case ImageAttributes::ShrinkToFit:
242       dimensionTest = BoxDimensionTestEither;
243       break;
244     // Scale to fill mode keeps both dimensions at least as large as desired:
245     case ImageAttributes::ScaleToFill:
246       dimensionTest = BoxDimensionTestBoth;
247       break;
248     // Y dimension is irrelevant when downscaling in FitWidth mode:
249     case ImageAttributes::FitWidth:
250       dimensionTest = BoxDimensionTestX;
251       break;
252     // X Dimension is ignored by definition in FitHeight mode:
253     case ImageAttributes::FitHeight:
254       dimensionTest = BoxDimensionTestY;
255   }
256
257   return dimensionTest;
258 }
259
260 }
261
262 // The top-level function to return a downscaled version of a bitmap:
263 Integration::BitmapPtr DownscaleBitmap( Integration::Bitmap& bitmap, const ImageAttributes& requestedAttributes )
264 {
265   const unsigned int bitmapWidth  = bitmap.GetImageWidth();
266   const unsigned int bitmapHeight = bitmap.GetImageHeight();
267   const Size requestedSize = requestedAttributes.GetSize();
268
269   // If a different size than the raw one has been requested, resize the image:
270   if( bitmap.GetPackedPixelsProfile() &&
271       (requestedSize.x > 0.0f) && (requestedSize.y > 0.0f) &&
272       (requestedSize.x < bitmapWidth) &&
273       (requestedSize.y < bitmapHeight) )
274   {
275     const Pixel::Format pixelFormat = bitmap.GetPixelFormat();
276     const ImageAttributes::ScalingMode scalingMode = requestedAttributes.GetScalingMode();
277     const ImageAttributes::FilterMode filterMode = requestedAttributes.GetFilterMode();
278
279     // Perform power of 2 iterated 4:1 box filtering if the requested filter mode requires it:
280     if( filterMode == ImageAttributes::Box || filterMode == ImageAttributes::BoxThenNearest || filterMode == ImageAttributes::BoxThenLinear )
281     {
282       // Check the pixel format is one that is supported:
283       if( pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 )
284       {
285         unsigned int shrunkWidth = -1, shrunkHeight = -1;
286         const BoxDimensionTest dimensionTest = DimensionTestForScalingMode( scalingMode );
287
288         if( pixelFormat == Pixel::RGBA8888 )
289         {
290           Internal::Platform::DownscaleInPlacePow2RGBA8888( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
291         }
292         else if( pixelFormat == Pixel::RGB888 )
293         {
294           Internal::Platform::DownscaleInPlacePow2RGB888( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
295         }
296         else if( pixelFormat == Pixel::RGB565 )
297         {
298           Internal::Platform::DownscaleInPlacePow2RGB565( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
299         }
300         else if( pixelFormat == Pixel::LA88 )
301         {
302           Internal::Platform::DownscaleInPlacePow2ComponentPair( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
303         }
304         else if( pixelFormat == Pixel::L8  || pixelFormat == Pixel::A8 )
305         {
306           Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
307         }
308
309         if( shrunkWidth != bitmapWidth && shrunkHeight != bitmapHeight )
310         {
311           // Allocate a pixel buffer to hold the shrunk image:
312           Integration::BitmapPtr shrunk = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::DISCARD );
313           shrunk->GetPackedPixelsProfile()->ReserveBuffer( pixelFormat, shrunkWidth, shrunkHeight, shrunkWidth, shrunkHeight );
314
315           // Copy over the pixels from the downscaled image that was generated in-place in the pixel buffer of the input bitmap:
316           DALI_ASSERT_DEBUG( bitmap.GetBuffer() && "Null loaded bitmap buffer." );
317           DALI_ASSERT_DEBUG( shrunk->GetBuffer() && "Null shrunk bitmap buffer." );
318           memcpy( shrunk->GetBuffer(), bitmap.GetBuffer(), shrunkWidth * shrunkHeight * Pixel::GetBytesPerPixel( pixelFormat ) );
319           return shrunk;
320         }
321       }
322       else
323       {
324         DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not shrunk: unsupported pixel format: %u.\n", unsigned(pixelFormat) );
325       }
326     }
327   }
328   return Integration::BitmapPtr(&bitmap);
329 }
330
331 namespace
332 {
333 /**
334  * @brief Returns whether to keep box filtering based on whether downscaled dimensions will overshoot the desired ones aty the next step.
335  * @param test Which combination of the two dimensions matter for terminating the filtering.
336  * @param scaledWidth The width of the current downscaled image.
337  * @param scaledHeight The height of the current downscaled image.
338  * @param desiredWidth The target width for the downscaling.
339  * @param desiredHeight The target height for the downscaling.
340  */
341 bool ContinueScaling( BoxDimensionTest test, unsigned int scaledWidth, unsigned int scaledHeight, unsigned int desiredWidth, unsigned int desiredHeight )
342 {
343   bool keepScaling = false;
344   const unsigned int nextWidth = scaledWidth >> 1u;
345   const unsigned int nextHeight = scaledHeight >> 1u;
346
347   if( nextWidth >= 1u && nextHeight >= 1u )
348   {
349     switch( test )
350     {
351       case BoxDimensionTestEither:
352         keepScaling = nextWidth >= desiredWidth || nextHeight >= desiredHeight;
353         break;
354       case BoxDimensionTestBoth:
355         keepScaling = nextWidth >= desiredWidth && nextHeight >= desiredHeight;
356         break;
357       case BoxDimensionTestX:
358         keepScaling = nextWidth >= desiredWidth;
359         break;
360       case BoxDimensionTestY:
361         keepScaling = nextHeight >= desiredHeight;
362     }
363   }
364
365   return keepScaling;
366 }
367
368 /**
369  * @brief A shared implementation of the overall iterative downscaling algorithm.
370  *
371  * Specialise this for particular pixel formats by supplying the number of bytes
372  * per pixel and two functions: one for averaging pairs of neighbouring pixels
373  * on a single scanline, and a second for averaging pixels at corresponding
374  * positions on different scanlines.
375  **/
376 template<
377   int BYTES_PER_PIXEL,
378   void (*HalveScanlineInPlace)( unsigned char * const pixels, const unsigned int width ),
379   void (*AverageScanlines) ( const unsigned char * const scanline1, const unsigned char * const __restrict__ scanline2, unsigned char* const outputScanline, const unsigned int width )
380 >
381 void DownscaleInPlacePow2Generic(
382     unsigned char * const pixels,
383     const unsigned int inputWidth, const unsigned int inputHeight,
384     const unsigned int desiredWidth, const unsigned int desiredHeight,
385     BoxDimensionTest dimensionTest,
386     unsigned& outWidth, unsigned& outHeight )
387 {
388   if( pixels == 0 )
389   {
390     return;
391   }
392   ValidateScalingParameters( inputWidth, inputHeight, desiredWidth, desiredHeight );
393
394   // Scale the image until it would be smaller than desired, stopping if the
395   // resulting height or width would be less than 1:
396   unsigned int scaledWidth = inputWidth, scaledHeight = inputHeight;
397   while( ContinueScaling( dimensionTest, scaledWidth, scaledHeight, desiredWidth, desiredHeight ) )
398       //scaledWidth >> 1u >= desiredWidth && scaledHeight >> 1u >= desiredHeight &&
399       //    scaledWidth >> 1u >= 1u           && scaledHeight >> 1u >= 1u )
400   {
401     const unsigned int lastWidth = scaledWidth;
402     scaledWidth  >>= 1u;
403     scaledHeight >>= 1u;
404
405     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Scaling to %u\t%u.\n", scaledWidth, scaledHeight );
406
407     const unsigned int lastScanlinePair = scaledHeight - 1;
408
409     // Scale pairs of scanlines until any spare one at the end is dropped:
410     for( unsigned int y = 0; y <= lastScanlinePair; ++y )
411     {
412       // Scale two scanlines horizontally:
413       HalveScanlineInPlace( &pixels[y * 2 * lastWidth * BYTES_PER_PIXEL], lastWidth );
414       HalveScanlineInPlace( &pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL], lastWidth );
415
416       // Scale vertical pairs of pixels while the last two scanlines are still warm in
417       // the CPU cache(s):
418       // Note, better access patterns for cache-coherence are possible for very large
419       // images but even a 4k RGB888 image will use just 24kB of cache (4k pixels
420       // * 3 Bpp * 2 scanlines) for two scanlines on the first iteration.
421       AverageScanlines(
422           &pixels[y * 2 * lastWidth * BYTES_PER_PIXEL],
423           &pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL],
424           &pixels[y * scaledWidth * BYTES_PER_PIXEL],
425           scaledWidth );
426     }
427   }
428
429   ///@note: we could finish off with one of two mutually exclusive passes, one squashing horizontally as far as possible, and the other vertically, if we knew a following cpu point or bilinear filter would restore the desired aspect ratio.
430   outWidth = scaledWidth;
431   outHeight = scaledHeight;
432 }
433
434 }
435
436 void HalveScanlineInPlaceRGB888(
437     unsigned char * const pixels,
438     const unsigned int width )
439 {
440   DebugAssertScanlineParameters( pixels, width );
441
442   const unsigned int lastPair = EvenDown( width - 2 );
443
444   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
445   {
446     // Load all the byte pixel components we need:
447     const unsigned int c11 = pixels[pixel * 3];
448     const unsigned int c12 = pixels[pixel * 3 + 1];
449     const unsigned int c13 = pixels[pixel * 3 + 2];
450     const unsigned int c21 = pixels[pixel * 3 + 3];
451     const unsigned int c22 = pixels[pixel * 3 + 4];
452     const unsigned int c23 = pixels[pixel * 3 + 5];
453
454     // Save the averaged byte pixel components:
455     pixels[outPixel * 3]     = AverageComponent( c11, c21 );
456     pixels[outPixel * 3 + 1] = AverageComponent( c12, c22 );
457     pixels[outPixel * 3 + 2] = AverageComponent( c13, c23 );
458   }
459 }
460
461 void HalveScanlineInPlaceRGBA8888(
462     unsigned char * const pixels,
463     const unsigned int width )
464 {
465   DebugAssertScanlineParameters( pixels, width );
466   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(pixels) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
467
468   uint32_t* const alignedPixels = reinterpret_cast<uint32_t*>(pixels);
469
470   const unsigned int lastPair = EvenDown( width - 2 );
471
472   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
473   {
474     const uint32_t averaged = AveragePixelRGBA8888( alignedPixels[pixel], alignedPixels[pixel + 1] );
475     alignedPixels[outPixel] = averaged;
476   }
477 }
478
479 void HalveScanlineInPlaceRGB565( unsigned char * pixels, unsigned int width )
480 {
481   DebugAssertScanlineParameters( pixels, width );
482   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(pixels) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
483
484   uint16_t* const alignedPixels = reinterpret_cast<uint16_t*>(pixels);
485
486   const unsigned int lastPair = EvenDown( width - 2 );
487
488   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
489   {
490     const uint32_t averaged = AveragePixelRGB565( alignedPixels[pixel], alignedPixels[pixel + 1] );
491     alignedPixels[outPixel] = averaged;
492   }
493 }
494
495 void HalveScanlineInPlace2Bytes(
496     unsigned char * const pixels,
497     const unsigned int width )
498 {
499   DebugAssertScanlineParameters( pixels, width );
500
501   const unsigned int lastPair = EvenDown( width - 2 );
502
503   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
504   {
505     // Load all the byte pixel components we need:
506     const unsigned int c11 = pixels[pixel * 2];
507     const unsigned int c12 = pixels[pixel * 2 + 1];
508     const unsigned int c21 = pixels[pixel * 2 + 2];
509     const unsigned int c22 = pixels[pixel * 2 + 3];
510
511     // Save the averaged byte pixel components:
512     pixels[outPixel * 2]     = AverageComponent( c11, c21 );
513     pixels[outPixel * 2 + 1] = AverageComponent( c12, c22 );
514   }
515 }
516
517 void HalveScanlineInPlace1Byte(
518     unsigned char * const pixels,
519     const unsigned int width )
520 {
521   DebugAssertScanlineParameters( pixels, width );
522
523   const unsigned int lastPair = EvenDown( width - 2 );
524
525   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
526   {
527     // Load all the byte pixel components we need:
528     const unsigned int c1 = pixels[pixel];
529     const unsigned int c2 = pixels[pixel + 1];
530
531     // Save the averaged byte pixel component:
532     pixels[outPixel] = AverageComponent( c1, c2 );
533   }
534 }
535
536 /**
537  * @ToDo: Optimise for ARM using a 4 bytes at a time loop wrapped around the single ARMV6 instruction: UHADD8  R4, R0, R5. Note, this is not neon. It runs in the normal integer pipeline so there is no downside like a stall moving between integer and copro, or extra power for clocking-up the idle copro.
538  * if (widthInComponents >= 7) { word32* aligned1 = scanline1 + 3 & 3; word32* aligned1_end = scanline1 + widthInPixels & 3; while(aligned1 < aligned1_end) { UHADD8  *aligned1++, *aligned2++, *alignedoutput++ } .. + 0 to 3 spare pixels at each end.
539  */
540 void AverageScanlines1(
541     const unsigned char * const scanline1,
542     const unsigned char * const __restrict__ scanline2,
543     unsigned char* const outputScanline,
544     const unsigned int width )
545 {
546   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width );
547
548   for( unsigned int component = 0; component < width; ++component )
549   {
550     outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
551   }
552 }
553
554 void AverageScanlines2(
555     const unsigned char * const scanline1,
556     const unsigned char * const __restrict__ scanline2,
557     unsigned char* const outputScanline,
558     const unsigned int width )
559 {
560   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 2 );
561
562   for( unsigned int component = 0; component < width * 2; ++component )
563   {
564     outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
565   }
566 }
567
568 void AverageScanlines3(
569     const unsigned char * const scanline1,
570     const unsigned char * const __restrict__ scanline2,
571     unsigned char* const outputScanline,
572     const unsigned int width )
573 {
574   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 3 );
575
576   for( unsigned int component = 0; component < width * 3; ++component )
577   {
578     outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
579   }
580 }
581
582 void AverageScanlinesRGBA8888(
583     const unsigned char * const scanline1,
584     const unsigned char * const __restrict__ scanline2,
585     unsigned char * const outputScanline,
586     const unsigned int width )
587 {
588   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 4 );
589   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline1) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
590   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline2) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
591   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(outputScanline) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
592
593   const uint32_t* const alignedScanline1 = reinterpret_cast<const uint32_t*>(scanline1);
594   const uint32_t* const alignedScanline2 = reinterpret_cast<const uint32_t*>(scanline2);
595   uint32_t* const alignedOutput = reinterpret_cast<uint32_t*>(outputScanline);
596
597   for( unsigned int pixel = 0; pixel < width; ++pixel )
598   {
599     alignedOutput[pixel] = AveragePixelRGBA8888( alignedScanline1[pixel], alignedScanline2[pixel] );
600   }
601 }
602
603 void AverageScanlinesRGB565(
604     const unsigned char * const scanline1,
605     const unsigned char * const __restrict__ scanline2,
606     unsigned char * const outputScanline,
607     const unsigned int width )
608 {
609   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 2 );
610   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline1) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
611   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline2) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
612   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(outputScanline) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
613
614   const uint16_t* const alignedScanline1 = reinterpret_cast<const uint16_t*>(scanline1);
615   const uint16_t* const alignedScanline2 = reinterpret_cast<const uint16_t*>(scanline2);
616   uint16_t* const alignedOutput = reinterpret_cast<uint16_t*>(outputScanline);
617
618   for( unsigned int pixel = 0; pixel < width; ++pixel )
619   {
620     alignedOutput[pixel] = AveragePixelRGB565( alignedScanline1[pixel], alignedScanline2[pixel] );
621   }
622 }
623
624 void DownscaleInPlacePow2RGB888(
625     unsigned char * const pixels,
626     const unsigned int inputWidth, const unsigned int inputHeight,
627     const unsigned int desiredWidth, const unsigned int desiredHeight,
628     BoxDimensionTest dimensionTest,
629     unsigned& outWidth, unsigned& outHeight )
630 {
631   DownscaleInPlacePow2Generic<3, HalveScanlineInPlaceRGB888, AverageScanlines3>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
632 }
633
634 void DownscaleInPlacePow2RGBA8888(
635     unsigned char * const pixels,
636     const unsigned int inputWidth, const unsigned int inputHeight,
637     const unsigned int desiredWidth, const unsigned int desiredHeight,
638     BoxDimensionTest dimensionTest,
639     unsigned& outWidth, unsigned& outHeight )
640 {
641   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(pixels) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
642   DownscaleInPlacePow2Generic<4, HalveScanlineInPlaceRGBA8888, AverageScanlinesRGBA8888>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
643 }
644
645 void DownscaleInPlacePow2RGB565(
646     unsigned char * pixels,
647     unsigned int inputWidth, unsigned int inputHeight,
648     unsigned int desiredWidth, unsigned int desiredHeight,
649     BoxDimensionTest dimensionTest,
650     unsigned int& outWidth, unsigned int& outHeight )
651 {
652   DownscaleInPlacePow2Generic<2, HalveScanlineInPlaceRGB565, AverageScanlinesRGB565>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
653 }
654
655 /**
656  * @copydoc DownscaleInPlacePow2RGB888
657  *
658  * For 2-byte formats such as lum8alpha8, but not packed 16 bit formats like RGB565.
659  */
660 void DownscaleInPlacePow2ComponentPair(
661     unsigned char * const pixels,
662     const unsigned int inputWidth, const unsigned int inputHeight,
663     const unsigned int desiredWidth, const unsigned int desiredHeight,
664     BoxDimensionTest dimensionTest,
665     unsigned& outWidth, unsigned& outHeight )
666 {
667   DownscaleInPlacePow2Generic<2, HalveScanlineInPlace2Bytes, AverageScanlines2>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
668 }
669
670 void DownscaleInPlacePow2SingleBytePerPixel(
671     unsigned char * pixels,
672     unsigned int inputWidth, unsigned int inputHeight,
673     unsigned int desiredWidth, unsigned int desiredHeight,
674     BoxDimensionTest dimensionTest,
675     unsigned int& outWidth, unsigned int& outHeight )
676 {
677   DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
678 }
679
680 } /* namespace Platform */
681 } /* namespace Internal */
682 } /* namespace Dali */