Added missing newline chars to logging commands
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / portable / image-operations.cpp
1 /*
2  * Copyright (c) 2015 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 #include "image-operations.h"
19
20 // EXTERNAL INCLUDES
21 #include <cstring>
22 #include <stddef.h>
23 #include <cmath>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/math/vector2.h>
26
27 // INTERNAL INCLUDES
28
29 namespace Dali
30 {
31 namespace Internal
32 {
33 namespace Platform
34 {
35
36 namespace
37 {
38
39 // The BORDER_FILL_VALUE is a single byte value that is used for horizontal and vertical borders.
40 // A value of 0x00 gives us transparency for pixel buffers with an alpha channel, or black otherwise.
41 // We can optionally use a Vector4 color here, but at reduced fill speed.
42 const uint8_t BORDER_FILL_VALUE( 0x00 );
43 // A maximum size limit for newly created bitmaps. ( 1u << 16 ) - 1 is chosen as we are using 16bit words for dimensions.
44 const unsigned int MAXIMUM_TARGET_BITMAP_SIZE( ( 1u << 16 ) - 1 );
45
46 using Integration::Bitmap;
47 using Integration::BitmapPtr;
48 typedef unsigned char PixelBuffer;
49
50 /**
51  * @brief 4 byte pixel structure.
52  */
53 struct Pixel4Bytes
54 {
55   uint8_t r;
56   uint8_t g;
57   uint8_t b;
58   uint8_t a;
59 } __attribute__((packed, aligned(4))); //< Tell the compiler it is okay to use a single 32 bit load.
60
61 /**
62  * @brief RGB888 pixel structure.
63  */
64 struct Pixel3Bytes
65 {
66   uint8_t r;
67   uint8_t g;
68   uint8_t b;
69 } __attribute__((packed, aligned(1)));
70
71 /**
72  * @brief RGB565 pixel typedefed from a short.
73  *
74  * Access fields by manual shifting and masking.
75  */
76 typedef uint16_t PixelRGB565;
77
78 /**
79  * @brief a Pixel composed of two independent byte components.
80  */
81 struct Pixel2Bytes
82 {
83   uint8_t l;
84   uint8_t a;
85 } __attribute__((packed, aligned(2))); //< Tell the compiler it is okay to use a single 16 bit load.
86
87
88 #if defined(DEBUG_ENABLED)
89 /**
90  * Disable logging of image operations or make it verbose from the commandline
91  * as follows (e.g., for dali demo app):
92  * <code>
93  * LOG_IMAGE_OPERATIONS=0 dali-demo #< off
94  * LOG_IMAGE_OPERATIONS=3 dali-demo #< on, verbose
95  * </code>
96  */
97 Debug::Filter* gImageOpsLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_IMAGE_OPERATIONS" );
98 #endif
99
100 /** @return The greatest even number less than or equal to the argument. */
101 inline unsigned int EvenDown( const unsigned int a )
102 {
103   const unsigned int evened = a & ~1u;
104   return evened;
105 }
106
107 /**
108  * @brief Log bad parameters.
109  */
110 void ValidateScalingParameters( const unsigned int inputWidth,
111                                 const unsigned int inputHeight,
112                                 const unsigned int desiredWidth,
113                                 const unsigned int desiredHeight )
114 {
115   if( desiredWidth > inputWidth || desiredHeight > inputHeight )
116   {
117     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Upscaling not supported (%u, %u -> %u, %u).\n", inputWidth, inputHeight, desiredWidth, desiredHeight );
118   }
119
120   if( desiredWidth == 0u || desiredHeight == 0u )
121   {
122     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Downscaling to a zero-area target is pointless.\n" );
123   }
124
125   if( inputWidth == 0u || inputHeight == 0u )
126   {
127     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Zero area images cannot be scaled\n" );
128   }
129 }
130
131 /**
132  * @brief Do debug assertions common to all scanline halving functions.
133  * @note Inline and in anon namespace so should boil away in release builds.
134  */
135 inline void DebugAssertScanlineParameters( const uint8_t * const pixels, const unsigned int width )
136 {
137   DALI_ASSERT_DEBUG( pixels && "Null pointer." );
138   DALI_ASSERT_DEBUG( width > 1u && "Can't average fewer than two pixels." );
139   DALI_ASSERT_DEBUG( width < 131072u && "Unusually wide image: are you sure you meant to pass that value in?" );
140 }
141
142 /**
143  * @brief Assertions on params to functions averaging pairs of scanlines.
144  * @note Inline as intended to boil away in release.
145  */
146 inline void DebugAssertDualScanlineParameters( const uint8_t * const scanline1,
147                                                const uint8_t * const scanline2,
148                                                uint8_t* const outputScanline,
149                                                const size_t widthInComponents )
150 {
151   DALI_ASSERT_DEBUG( scanline1 && "Null pointer." );
152   DALI_ASSERT_DEBUG( scanline2 && "Null pointer." );
153   DALI_ASSERT_DEBUG( outputScanline && "Null pointer." );
154   DALI_ASSERT_DEBUG( ((scanline1 >= scanline2 + widthInComponents) || (scanline2 >= scanline1 + widthInComponents )) && "Scanlines alias." );
155   DALI_ASSERT_DEBUG( ((((void*)outputScanline) >= (void*)(scanline2 + widthInComponents)) || (((void*)scanline2) >= (void*)(scanline1 + widthInComponents))) && "Scanline 2 aliases output." );
156 }
157
158 /**
159  * @brief Converts a scaling mode to the definition of which dimensions matter when box filtering as a part of that mode.
160  */
161 BoxDimensionTest DimensionTestForScalingMode( FittingMode::Type fittingMode )
162 {
163   BoxDimensionTest dimensionTest;
164   dimensionTest = BoxDimensionTestEither;
165
166   switch( fittingMode )
167   {
168     // Shrink to fit attempts to make one or zero dimensions smaller than the
169     // desired dimensions and one or two dimensions exactly the same as the desired
170     // ones, so as long as one dimension is larger than the desired size, box
171     // filtering can continue even if the second dimension is smaller than the
172     // desired dimensions:
173     case FittingMode::SHRINK_TO_FIT:
174     {
175       dimensionTest = BoxDimensionTestEither;
176       break;
177     }
178     // Scale to fill mode keeps both dimensions at least as large as desired:
179     case FittingMode::SCALE_TO_FILL:
180     {
181       dimensionTest = BoxDimensionTestBoth;
182       break;
183     }
184     // Y dimension is irrelevant when downscaling in FIT_WIDTH mode:
185     case FittingMode::FIT_WIDTH:
186     {
187       dimensionTest = BoxDimensionTestX;
188       break;
189     }
190     // X Dimension is ignored by definition in FIT_HEIGHT mode:
191     case FittingMode::FIT_HEIGHT:
192     {
193       dimensionTest = BoxDimensionTestY;
194       break;
195     }
196   }
197
198   return dimensionTest;
199 }
200
201 /**
202  * @brief Work out the dimensions for a uniform scaling of the input to map it
203  * into the target while effecting ShinkToFit scaling mode.
204  */
205 ImageDimensions FitForShrinkToFit( ImageDimensions target, ImageDimensions source )
206 {
207   // Scale the input by the least extreme of the two dimensions:
208   const float widthScale  = target.GetX() / float(source.GetX());
209   const float heightScale = target.GetY() / float(source.GetY());
210   const float scale = widthScale < heightScale ? widthScale : heightScale;
211
212   // Do no scaling at all if the result would increase area:
213   if( scale >= 1.0f )
214   {
215     return source;
216   }
217
218   return ImageDimensions( source.GetX() * scale + 0.5f, source.GetY() * scale + 0.5f );
219 }
220
221 /**
222  * @brief Work out the dimensions for a uniform scaling of the input to map it
223  * into the target while effecting SCALE_TO_FILL scaling mode.
224  * @note An image scaled into the output dimensions will need either top and
225  * bottom or left and right to be cropped away unless the source was pre-cropped
226  * to match the destination aspect ratio.
227  */
228 ImageDimensions FitForScaleToFill( ImageDimensions target, ImageDimensions source )
229 {
230   DALI_ASSERT_DEBUG( source.GetX() > 0 && source.GetY() > 0  && "Zero-area rectangles should not be passed-in" );
231   // Scale the input by the least extreme of the two dimensions:
232   const float widthScale  = target.GetX() / float(source.GetX());
233   const float heightScale = target.GetY() / float(source.GetY());
234   const float scale = widthScale > heightScale ? widthScale : heightScale;
235
236   // Do no scaling at all if the result would increase area:
237   if( scale >= 1.0f )
238   {
239     return source;
240   }
241
242   return ImageDimensions( source.GetX() * scale + 0.5f, source.GetY() * scale + 0.5f );
243 }
244
245 /**
246  * @brief Work out the dimensions for a uniform scaling of the input to map it
247  * into the target while effecting FIT_WIDTH scaling mode.
248  */
249 ImageDimensions FitForFitWidth( ImageDimensions target, ImageDimensions source )
250 {
251   DALI_ASSERT_DEBUG( source.GetX() > 0 && "Cant fit a zero-dimension rectangle." );
252   const float scale  = target.GetX() / float(source.GetX());
253
254   // Do no scaling at all if the result would increase area:
255   if( scale >= 1.0f )
256   {
257    return source;
258   }
259   return ImageDimensions( source.GetX() * scale + 0.5f, source.GetY() * scale + 0.5f );
260 }
261
262 /**
263  * @brief Work out the dimensions for a uniform scaling of the input to map it
264  * into the target while effecting FIT_HEIGHT scaling mode.
265  */
266 ImageDimensions FitForFitHeight( ImageDimensions target, ImageDimensions source )
267 {
268   DALI_ASSERT_DEBUG( source.GetY() > 0 && "Cant fit a zero-dimension rectangle." );
269   const float scale = target.GetY() / float(source.GetY());
270
271   // Do no scaling at all if the result would increase area:
272   if( scale >= 1.0f )
273   {
274     return source;
275   }
276
277   return ImageDimensions( source.GetX() * scale + 0.5f, source.GetY() * scale + 0.5f );
278 }
279
280 /**
281  * @brief Generate the rectangle to use as the target of a pixel sampling pass
282  * (e.g., nearest or linear).
283  */
284 ImageDimensions FitToScalingMode( ImageDimensions requestedSize, ImageDimensions sourceSize, FittingMode::Type fittingMode )
285 {
286   ImageDimensions fitDimensions;
287   switch( fittingMode )
288   {
289     case FittingMode::SHRINK_TO_FIT:
290     {
291       fitDimensions = FitForShrinkToFit( requestedSize, sourceSize );
292       break;
293     }
294     case FittingMode::SCALE_TO_FILL:
295     {
296       fitDimensions = FitForScaleToFill( requestedSize, sourceSize );
297       break;
298     }
299     case FittingMode::FIT_WIDTH:
300     {
301       fitDimensions = FitForFitWidth( requestedSize, sourceSize );
302       break;
303     }
304     case FittingMode::FIT_HEIGHT:
305     {
306       fitDimensions = FitForFitHeight( requestedSize, sourceSize );
307       break;
308     }
309   }
310
311   return fitDimensions;
312 }
313
314 /**
315  * @brief Calculate the number of lines on the X and Y axis that need to be
316  * either added or removed with repect to the specified fitting mode.
317  * (e.g., nearest or linear).
318  * @param[in]     sourceSize      The size of the source image
319  * @param[in]     fittingMode     The fitting mode to use
320  * @param[in/out] requestedSize   The target size that the image will be fitted to.
321  *                                If the source image is smaller than the requested size, the source is not scaled up.
322  *                                So we reduce the target size while keeping aspect by lowering resolution.
323  * @param[out]    scanlinesToCrop The number of scanlines to remove from the image (can be negative to represent Y borders required)
324  * @param[out]    columnsToCrop   The number of columns to remove from the image (can be negative to represent X borders required)
325  */
326 void CalculateBordersFromFittingMode(  ImageDimensions sourceSize, FittingMode::Type fittingMode, ImageDimensions& requestedSize, int& scanlinesToCrop, int& columnsToCrop )
327 {
328   const unsigned int sourceWidth( sourceSize.GetWidth() );
329   const unsigned int sourceHeight( sourceSize.GetHeight() );
330   const float targetAspect( static_cast< float >( requestedSize.GetWidth() ) / static_cast< float >( requestedSize.GetHeight() ) );
331   int finalWidth = 0;
332   int finalHeight = 0;
333
334   switch( fittingMode )
335   {
336     case FittingMode::FIT_WIDTH:
337     {
338       finalWidth = sourceWidth;
339       finalHeight = static_cast< float >( sourceWidth ) / targetAspect;
340
341       columnsToCrop = 0;
342       scanlinesToCrop = -( finalHeight - sourceHeight );
343       break;
344     }
345
346     case FittingMode::FIT_HEIGHT:
347     {
348       finalWidth = static_cast< float >( sourceHeight ) * targetAspect;
349       finalHeight = sourceHeight;
350
351       columnsToCrop = -( finalWidth - sourceWidth );
352       scanlinesToCrop = 0;
353       break;
354     }
355
356     case FittingMode::SHRINK_TO_FIT:
357     {
358       const float sourceAspect( static_cast< float >( sourceWidth ) / static_cast< float >( sourceHeight ) );
359       if( sourceAspect > targetAspect )
360       {
361         finalWidth = sourceWidth;
362         finalHeight = static_cast< float >( sourceWidth ) / targetAspect;
363
364         columnsToCrop = 0;
365         scanlinesToCrop = -( finalHeight - sourceHeight );
366       }
367       else
368       {
369         finalWidth = static_cast< float >( sourceHeight ) * targetAspect;
370         finalHeight = sourceHeight;
371
372         columnsToCrop = -( finalWidth - sourceWidth );
373         scanlinesToCrop = 0;
374       }
375       break;
376     }
377
378     case FittingMode::SCALE_TO_FILL:
379     {
380       const float sourceAspect( static_cast< float >( sourceWidth ) / static_cast< float >( sourceHeight ) );
381       if( sourceAspect > targetAspect )
382       {
383         finalWidth = static_cast< float >( sourceHeight ) * targetAspect;
384         finalHeight = sourceHeight;
385
386         columnsToCrop = -( finalWidth - sourceWidth );
387         scanlinesToCrop = 0;
388       }
389       else
390       {
391         finalWidth = sourceWidth;
392         finalHeight = static_cast< float >( sourceWidth ) / targetAspect;
393
394         columnsToCrop = 0;
395         scanlinesToCrop = -( finalHeight - sourceHeight );
396       }
397       break;
398     }
399   }
400
401   requestedSize.SetWidth( finalWidth );
402   requestedSize.SetHeight( finalHeight );
403 }
404
405 /**
406  * @brief Construct a bitmap with format and dimensions requested.
407  */
408 BitmapPtr MakeEmptyBitmap( Pixel::Format pixelFormat, unsigned int width, unsigned int height )
409 {
410   DALI_ASSERT_DEBUG( Pixel::GetBytesPerPixel(pixelFormat) && "Compressed formats not supported." );
411
412   // Allocate a pixel buffer to hold the image passed in:
413   Integration::BitmapPtr newBitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
414   newBitmap->GetPackedPixelsProfile()->ReserveBuffer( pixelFormat, width, height, width, height );
415   return newBitmap;
416 }
417
418 /**
419  * @brief Construct a bitmap object from a copy of the pixel array passed in.
420  */
421 BitmapPtr MakeBitmap( const uint8_t * const pixels, Pixel::Format pixelFormat, unsigned int width, unsigned int height )
422 {
423   DALI_ASSERT_DEBUG( pixels && "Null bitmap buffer to copy." );
424
425   // Allocate a pixel buffer to hold the image passed in:
426   Integration::BitmapPtr newBitmap = MakeEmptyBitmap( pixelFormat, width, height );
427
428   // Copy over the pixels from the downscaled image that was generated in-place in the pixel buffer of the input bitmap:
429   memcpy( newBitmap->GetBuffer(), pixels, width * height * Pixel::GetBytesPerPixel( pixelFormat ) );
430   return newBitmap;
431 }
432
433 /**
434  * @brief Work out the desired width and height, accounting for zeros.
435  *
436  * @param[in] bitmapWidth Width of image before processing.
437  * @param[in] bitmapHeight Height of image before processing.
438  * @param[in] requestedWidth Width of area to scale image into. Can be zero.
439  * @param[in] requestedHeight Height of area to scale image into. Can be zero.
440  * @return Dimensions of area to scale image into after special rules are applied.
441  */
442 ImageDimensions CalculateDesiredDimensions( unsigned int bitmapWidth, unsigned int bitmapHeight, unsigned int requestedWidth, unsigned int requestedHeight )
443 {
444   // If no dimensions have been requested, default to the source ones:
445   if( requestedWidth == 0 && requestedHeight == 0 )
446   {
447     return ImageDimensions( bitmapWidth, bitmapHeight );
448   }
449
450   // If both dimensions have values requested, use them both:
451   if( requestedWidth != 0 && requestedHeight != 0 )
452   {
453     return ImageDimensions( requestedWidth, requestedHeight );
454   }
455
456   // Only one of the dimensions has been requested. Calculate the other from
457   // the requested one and the source image aspect ratio:
458   if( requestedWidth != 0 )
459   {
460     return ImageDimensions( requestedWidth, bitmapHeight / float(bitmapWidth) * requestedWidth + 0.5f );
461   }
462   return ImageDimensions( bitmapWidth / float(bitmapHeight) * requestedHeight + 0.5f, requestedHeight );
463 }
464
465 } // namespace - unnamed
466
467 ImageDimensions CalculateDesiredDimensions( ImageDimensions rawDimensions, ImageDimensions requestedDimensions )
468 {
469   return CalculateDesiredDimensions( rawDimensions.GetWidth(), rawDimensions.GetHeight(), requestedDimensions.GetWidth(), requestedDimensions.GetHeight() ) ;
470 }
471
472 /**
473  * @brief Apply cropping and padding for specified fitting mode.
474  *
475  * Once the bitmap has been (optionally) downscaled to an appropriate size, this method performs alterations
476  * based on the fitting mode.
477  *
478  * This will add vertical or horizontal borders if necessary.
479  * Crop the source image data vertically or horizontally if necessary.
480  * The aspect of the source image is preserved.
481  * If the source image is smaller than the desired size, the algorithm will modify the the newly created
482  *   bitmaps dimensions to only be as large as necessary, as a memory saving optimization. This will cause
483  *   GPU scaling to be performed at render time giving the same result with less texture traversal.
484  *
485  * @param[in] bitmap            The source bitmap to perform modifications on.
486  * @param[in] desiredDimensions The target dimensions to aim to fill based on the fitting mode.
487  * @param[in] fittingMode       The fitting mode to use.
488  *
489  * @return                      A new bitmap with the padding and cropping required for fitting mode applied.
490  *                              If no modification is needed or possible, the passed in bitmap is returned.
491  */
492 Integration::BitmapPtr CropAndPadForFittingMode( Integration::BitmapPtr bitmap, ImageDimensions desiredDimensions, FittingMode::Type fittingMode );
493
494 /**
495  * @brief Adds horizontal or vertical borders to the source image.
496  *
497  * @param[in] targetPixels     The destination image pointer to draw the borders on.
498  * @param[in] bytesPerPixel    The number of bytes per pixel of the target pixel buffer.
499  * @param[in] targetDimensions The dimensions of the destination image.
500  * @param[in] padDimensions    The columns and scanlines to pad with borders.
501  */
502 void AddBorders( PixelBuffer *targetPixels, const unsigned int bytesPerPixel, const ImageDimensions targetDimensions, const ImageDimensions padDimensions );
503
504 BitmapPtr ApplyAttributesToBitmap( BitmapPtr bitmap, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode )
505 {
506   if( bitmap )
507   {
508     // Calculate the desired box, accounting for a possible zero component:
509     const ImageDimensions desiredDimensions  = CalculateDesiredDimensions( bitmap->GetImageWidth(), bitmap->GetImageHeight(), dimensions.GetWidth(), dimensions.GetHeight() );
510
511     // If a different size than the raw one has been requested, resize the image
512     // maximally using a repeated box filter without making it smaller than the
513     // requested size in either dimension:
514     bitmap = DownscaleBitmap( *bitmap, desiredDimensions, fittingMode, samplingMode );
515
516     // Cut the bitmap according to the desired width and height so that the
517     // resulting bitmap has the same aspect ratio as the desired dimensions.
518     // Add crop and add borders if necessary depending on fitting mode.
519     if( bitmap && bitmap->GetPackedPixelsProfile() )
520     {
521       bitmap = CropAndPadForFittingMode( bitmap, desiredDimensions, fittingMode );
522     }
523
524     // Examine the image pixels remaining after cropping and scaling to see if all
525     // are opaque, allowing faster rendering, or some have non-1.0 alpha:
526     if( bitmap && bitmap->GetPackedPixelsProfile() && Pixel::HasAlpha( bitmap->GetPixelFormat() ) )
527     {
528       bitmap->GetPackedPixelsProfile()->TestForTransparency();
529     }
530   }
531
532   return bitmap;
533 }
534
535 BitmapPtr CropAndPadForFittingMode( BitmapPtr bitmap, ImageDimensions desiredDimensions, FittingMode::Type fittingMode )
536 {
537   const unsigned int inputWidth = bitmap->GetImageWidth();
538   const unsigned int inputHeight = bitmap->GetImageHeight();
539
540   if( desiredDimensions.GetWidth() < 1u || desiredDimensions.GetHeight() < 1u )
541   {
542     DALI_LOG_WARNING( "Image scaling aborted as desired dimensions too small (%u, %u).\n", desiredDimensions.GetWidth(), desiredDimensions.GetHeight() );
543   }
544   else if( inputWidth != desiredDimensions.GetWidth() || inputHeight != desiredDimensions.GetHeight() )
545   {
546     // Calculate any padding or cropping that needs to be done based on the fitting mode.
547     // Note: If the desired size is larger than the original image, the desired size will be
548     // reduced while maintaining the aspect, in order to save unnecessary memory usage.
549     int scanlinesToCrop = 0;
550     int columnsToCrop = 0;
551
552     CalculateBordersFromFittingMode( ImageDimensions( inputWidth, inputHeight ), fittingMode, desiredDimensions, scanlinesToCrop, columnsToCrop );
553
554     unsigned int desiredWidth( desiredDimensions.GetWidth() );
555     unsigned int desiredHeight( desiredDimensions.GetHeight() );
556
557     // Action the changes by making a new bitmap with the central part of the loaded one if required.
558     if( scanlinesToCrop != 0 || columnsToCrop != 0 )
559     {
560       // Split the adding and removing of scanlines and columns into separate variables,
561       // so we can use one piece of generic code to action the changes.
562       unsigned int scanlinesToPad = 0;
563       unsigned int columnsToPad = 0;
564       if( scanlinesToCrop < 0 )
565       {
566         scanlinesToPad = -scanlinesToCrop;
567         scanlinesToCrop = 0;
568       }
569       if( columnsToCrop < 0 )
570       {
571         columnsToPad = -columnsToCrop;
572         columnsToCrop = 0;
573       }
574
575       // If there is no filtering, then the final image size can become very large, exit if larger than maximum.
576       if( ( desiredWidth > MAXIMUM_TARGET_BITMAP_SIZE ) || ( desiredHeight > MAXIMUM_TARGET_BITMAP_SIZE ) ||
577           ( columnsToPad > MAXIMUM_TARGET_BITMAP_SIZE ) || ( scanlinesToPad > MAXIMUM_TARGET_BITMAP_SIZE ) )
578       {
579         DALI_LOG_WARNING( "Image scaling aborted as final dimensions too large (%u, %u).\n", desiredWidth, desiredHeight );
580         return bitmap;
581       }
582
583       // Create a new bitmap with the desired size.
584       BitmapPtr croppedBitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
585       Integration::Bitmap::PackedPixelsProfile *packedView = croppedBitmap->GetPackedPixelsProfile();
586       DALI_ASSERT_DEBUG( packedView );
587       const Pixel::Format pixelFormat = bitmap->GetPixelFormat();
588       packedView->ReserveBuffer( pixelFormat, desiredWidth, desiredHeight, desiredWidth, desiredHeight );
589
590       // Add some pre-calculated offsets to the bitmap pointers so this is not done within a loop.
591       // The cropping is added to the source pointer, and the padding is added to the destination.
592       const unsigned int bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
593       const PixelBuffer * const sourcePixels = bitmap->GetBuffer() + ( ( ( ( scanlinesToCrop / 2 ) * inputWidth ) + ( columnsToCrop / 2 ) ) * bytesPerPixel );
594       PixelBuffer * const targetPixels = croppedBitmap->GetBuffer();
595       PixelBuffer * const targetPixelsActive = targetPixels + ( ( ( ( scanlinesToPad / 2 ) * desiredWidth ) + ( columnsToPad / 2 ) ) * bytesPerPixel );
596       DALI_ASSERT_DEBUG( sourcePixels && targetPixels );
597
598       // Copy the image data to the new bitmap.
599       // Optimize to a single memcpy if the left and right edges don't need a crop or a pad.
600       unsigned int outputSpan( desiredWidth * bytesPerPixel );
601       if( columnsToCrop == 0 && columnsToPad == 0 )
602       {
603         memcpy( targetPixelsActive, sourcePixels, ( desiredHeight - scanlinesToPad ) * outputSpan );
604       }
605       else
606       {
607         // The width needs to change (due to either a crop or a pad), so we copy a scanline at a time.
608         // Precalculate any constants to optimize the inner loop.
609         const unsigned int inputSpan( inputWidth * bytesPerPixel );
610         const unsigned int copySpan( ( desiredWidth - columnsToPad ) * bytesPerPixel );
611         const unsigned int scanlinesToCopy( desiredHeight - scanlinesToPad );
612
613         for( unsigned int y = 0; y < scanlinesToCopy; ++y )
614         {
615           memcpy( &targetPixelsActive[ y * outputSpan ], &sourcePixels[ y * inputSpan ], copySpan );
616         }
617       }
618
619       // Add vertical or horizontal borders to the final image (if required).
620       desiredDimensions.SetWidth( desiredWidth );
621       desiredDimensions.SetHeight( desiredHeight );
622       AddBorders( croppedBitmap->GetBuffer(), bytesPerPixel, desiredDimensions, ImageDimensions( columnsToPad, scanlinesToPad ) );
623       // Overwrite the loaded bitmap with the cropped version
624       bitmap = croppedBitmap;
625     }
626   }
627
628   return bitmap;
629 }
630
631 void AddBorders( PixelBuffer *targetPixels, const unsigned int bytesPerPixel, const ImageDimensions targetDimensions, const ImageDimensions padDimensions )
632 {
633   // Assign ints for faster access.
634   unsigned int desiredWidth( targetDimensions.GetWidth() );
635   unsigned int desiredHeight( targetDimensions.GetHeight() );
636   unsigned int columnsToPad( padDimensions.GetWidth() );
637   unsigned int scanlinesToPad( padDimensions.GetHeight() );
638   unsigned int outputSpan( desiredWidth * bytesPerPixel );
639
640   // Add letterboxing (symmetrical borders) if needed.
641   if( scanlinesToPad > 0 )
642   {
643     // Add a top border. Note: This is (deliberately) rounded down if padding is an odd number.
644     memset( targetPixels, BORDER_FILL_VALUE, ( scanlinesToPad / 2 ) * outputSpan );
645
646     // We subtract scanlinesToPad/2 from scanlinesToPad so that we have the correct
647     // offset for odd numbers (as the top border is 1 pixel smaller in these cases.
648     unsigned int bottomBorderHeight = scanlinesToPad - ( scanlinesToPad / 2 );
649
650     // Bottom border.
651     memset( &targetPixels[ ( desiredHeight - bottomBorderHeight ) * outputSpan ], BORDER_FILL_VALUE, bottomBorderHeight * outputSpan );
652   }
653   else if( columnsToPad > 0 )
654   {
655     // Add a left and right border.
656     // Left:
657     // Pre-calculate span size outside of loop.
658     unsigned int leftBorderSpanWidth( ( columnsToPad / 2 ) * bytesPerPixel );
659     for( unsigned int y = 0; y < desiredHeight; ++y )
660     {
661       memset( &targetPixels[ y * outputSpan ], BORDER_FILL_VALUE, leftBorderSpanWidth );
662     }
663
664     // Right:
665     // Pre-calculate the initial x offset as it is always the same for a small optimization.
666     // We subtract columnsToPad/2 from columnsToPad so that we have the correct
667     // offset for odd numbers (as the left border is 1 pixel smaller in these cases.
668     unsigned int rightBorderWidth = columnsToPad - ( columnsToPad / 2 );
669     PixelBuffer * const destPixelsRightBorder( targetPixels + ( ( desiredWidth - rightBorderWidth ) * bytesPerPixel ) );
670     unsigned int rightBorderSpanWidth = rightBorderWidth * bytesPerPixel;
671
672     for( unsigned int y = 0; y < desiredHeight; ++y )
673     {
674       memset( &destPixelsRightBorder[ y * outputSpan ], BORDER_FILL_VALUE, rightBorderSpanWidth );
675     }
676   }
677 }
678
679 Integration::BitmapPtr DownscaleBitmap( Integration::Bitmap& bitmap,
680                                         ImageDimensions desired,
681                                         FittingMode::Type fittingMode,
682                                         SamplingMode::Type samplingMode )
683 {
684   // Source dimensions as loaded from resources (e.g. filesystem):
685   const unsigned int bitmapWidth  = bitmap.GetImageWidth();
686   const unsigned int bitmapHeight = bitmap.GetImageHeight();
687   // Desired dimensions (the rectangle to fit the source image to):
688   const unsigned int desiredWidth = desired.GetWidth();
689   const unsigned int desiredHeight = desired.GetHeight();
690
691   BitmapPtr outputBitmap( &bitmap );
692
693   // If a different size than the raw one has been requested, resize the image:
694   if( bitmap.GetPackedPixelsProfile() &&
695       (desiredWidth > 0.0f) && (desiredHeight > 0.0f) &&
696       ((desiredWidth < bitmapWidth) || (desiredHeight < bitmapHeight)) )
697   {
698     const Pixel::Format pixelFormat = bitmap.GetPixelFormat();
699
700     // Do the fast power of 2 iterated box filter to get to roughly the right side if the filter mode requests that:
701     unsigned int shrunkWidth = -1, shrunkHeight = -1;
702     DownscaleInPlacePow2( bitmap.GetBuffer(), pixelFormat, bitmapWidth, bitmapHeight, desiredWidth, desiredHeight, fittingMode, samplingMode, shrunkWidth, shrunkHeight );
703
704     // Work out the dimensions of the downscaled bitmap, given the scaling mode and desired dimensions:
705     const ImageDimensions filteredDimensions = FitToScalingMode( ImageDimensions( desiredWidth, desiredHeight ), ImageDimensions( shrunkWidth, shrunkHeight ), fittingMode );
706     const unsigned int filteredWidth = filteredDimensions.GetWidth();
707     const unsigned int filteredHeight = filteredDimensions.GetHeight();
708
709     // Run a filter to scale down the bitmap if it needs it:
710     bool filtered = false;
711     if( filteredWidth < shrunkWidth || filteredHeight < shrunkHeight )
712     {
713       if( samplingMode == SamplingMode::LINEAR || samplingMode == SamplingMode::BOX_THEN_LINEAR ||
714           samplingMode == SamplingMode::NEAREST || samplingMode == SamplingMode::BOX_THEN_NEAREST )
715       {
716         outputBitmap = MakeEmptyBitmap( pixelFormat, filteredWidth, filteredHeight );
717         if( outputBitmap )
718         {
719           if( samplingMode == SamplingMode::LINEAR || samplingMode == SamplingMode::BOX_THEN_LINEAR )
720           {
721             LinearSample( bitmap.GetBuffer(), ImageDimensions(shrunkWidth, shrunkHeight), pixelFormat, outputBitmap->GetBuffer(), filteredDimensions );
722           }
723           else
724           {
725             PointSample( bitmap.GetBuffer(), shrunkWidth, shrunkHeight, pixelFormat, outputBitmap->GetBuffer(), filteredWidth, filteredHeight );
726           }
727           filtered = true;
728         }
729       }
730     }
731     // Copy out the 2^x downscaled, box-filtered pixels if no secondary filter (point or linear) was applied:
732     if( filtered == false && ( shrunkWidth < bitmapWidth || shrunkHeight < bitmapHeight ) )
733     {
734       outputBitmap = MakeBitmap( bitmap.GetBuffer(), pixelFormat, shrunkWidth, shrunkHeight );
735     }
736   }
737
738   return outputBitmap;
739 }
740
741 namespace
742 {
743 /**
744  * @brief Returns whether to keep box filtering based on whether downscaled dimensions will overshoot the desired ones aty the next step.
745  * @param test Which combination of the two dimensions matter for terminating the filtering.
746  * @param scaledWidth The width of the current downscaled image.
747  * @param scaledHeight The height of the current downscaled image.
748  * @param desiredWidth The target width for the downscaling.
749  * @param desiredHeight The target height for the downscaling.
750  */
751 bool ContinueScaling( BoxDimensionTest test, unsigned int scaledWidth, unsigned int scaledHeight, unsigned int desiredWidth, unsigned int desiredHeight )
752 {
753   bool keepScaling = false;
754   const unsigned int nextWidth = scaledWidth >> 1u;
755   const unsigned int nextHeight = scaledHeight >> 1u;
756
757   if( nextWidth >= 1u && nextHeight >= 1u )
758   {
759     switch( test )
760     {
761       case BoxDimensionTestEither:
762       {
763         keepScaling = nextWidth >= desiredWidth || nextHeight >= desiredHeight;
764         break;
765       }
766       case BoxDimensionTestBoth:
767       {
768         keepScaling = nextWidth >= desiredWidth && nextHeight >= desiredHeight;
769         break;
770       }
771       case BoxDimensionTestX:
772       {
773         keepScaling = nextWidth >= desiredWidth;
774         break;
775       }
776       case BoxDimensionTestY:
777       {
778         keepScaling = nextHeight >= desiredHeight;
779         break;
780       }
781     }
782   }
783
784   return keepScaling;
785 }
786
787 /**
788  * @brief A shared implementation of the overall iterative box filter
789  * downscaling algorithm.
790  *
791  * Specialise this for particular pixel formats by supplying the number of bytes
792  * per pixel and two functions: one for averaging pairs of neighbouring pixels
793  * on a single scanline, and a second for averaging pixels at corresponding
794  * positions on different scanlines.
795  **/
796 template<
797   int BYTES_PER_PIXEL,
798   void (*HalveScanlineInPlace)( unsigned char * const pixels, const unsigned int width ),
799   void (*AverageScanlines) ( const unsigned char * const scanline1, const unsigned char * const __restrict__ scanline2, unsigned char* const outputScanline, const unsigned int width )
800 >
801 void DownscaleInPlacePow2Generic( unsigned char * const pixels,
802                                   const unsigned int inputWidth,
803                                   const unsigned int inputHeight,
804                                   const unsigned int desiredWidth,
805                                   const unsigned int desiredHeight,
806                                   BoxDimensionTest dimensionTest,
807                                   unsigned& outWidth,
808                                   unsigned& outHeight )
809 {
810   if( pixels == 0 )
811   {
812     return;
813   }
814   ValidateScalingParameters( inputWidth, inputHeight, desiredWidth, desiredHeight );
815
816   // Scale the image until it would be smaller than desired, stopping if the
817   // resulting height or width would be less than 1:
818   unsigned int scaledWidth = inputWidth, scaledHeight = inputHeight;
819   while( ContinueScaling( dimensionTest, scaledWidth, scaledHeight, desiredWidth, desiredHeight ) )
820   {
821     const unsigned int lastWidth = scaledWidth;
822     scaledWidth  >>= 1u;
823     scaledHeight >>= 1u;
824
825     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Scaling to %u\t%u.\n", scaledWidth, scaledHeight );
826
827     const unsigned int lastScanlinePair = scaledHeight - 1;
828
829     // Scale pairs of scanlines until any spare one at the end is dropped:
830     for( unsigned int y = 0; y <= lastScanlinePair; ++y )
831     {
832       // Scale two scanlines horizontally:
833       HalveScanlineInPlace( &pixels[y * 2 * lastWidth * BYTES_PER_PIXEL], lastWidth );
834       HalveScanlineInPlace( &pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL], lastWidth );
835
836       // Scale vertical pairs of pixels while the last two scanlines are still warm in
837       // the CPU cache(s):
838       // Note, better access patterns for cache-coherence are possible for very large
839       // images but even a 4k wide RGB888 image will use just 24kB of cache (4k pixels
840       // * 3 Bpp * 2 scanlines) for two scanlines on the first iteration.
841       AverageScanlines(
842           &pixels[y * 2 * lastWidth * BYTES_PER_PIXEL],
843           &pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL],
844           &pixels[y * scaledWidth * BYTES_PER_PIXEL],
845           scaledWidth );
846     }
847   }
848
849   ///@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.
850   outWidth = scaledWidth;
851   outHeight = scaledHeight;
852 }
853
854 }
855
856 void HalveScanlineInPlaceRGB888( unsigned char * const pixels, const unsigned int width )
857 {
858   DebugAssertScanlineParameters( pixels, width );
859
860   const unsigned int lastPair = EvenDown( width - 2 );
861
862   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
863   {
864     // Load all the byte pixel components we need:
865     const unsigned int c11 = pixels[pixel * 3];
866     const unsigned int c12 = pixels[pixel * 3 + 1];
867     const unsigned int c13 = pixels[pixel * 3 + 2];
868     const unsigned int c21 = pixels[pixel * 3 + 3];
869     const unsigned int c22 = pixels[pixel * 3 + 4];
870     const unsigned int c23 = pixels[pixel * 3 + 5];
871
872     // Save the averaged byte pixel components:
873     pixels[outPixel * 3]     = AverageComponent( c11, c21 );
874     pixels[outPixel * 3 + 1] = AverageComponent( c12, c22 );
875     pixels[outPixel * 3 + 2] = AverageComponent( c13, c23 );
876   }
877 }
878
879 void HalveScanlineInPlaceRGBA8888( unsigned char * const pixels, const unsigned int width )
880 {
881   DebugAssertScanlineParameters( pixels, width );
882   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(pixels) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
883
884   uint32_t* const alignedPixels = reinterpret_cast<uint32_t*>(pixels);
885
886   const unsigned int lastPair = EvenDown( width - 2 );
887
888   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
889   {
890     const uint32_t averaged = AveragePixelRGBA8888( alignedPixels[pixel], alignedPixels[pixel + 1] );
891     alignedPixels[outPixel] = averaged;
892   }
893 }
894
895 void HalveScanlineInPlaceRGB565( unsigned char * pixels, unsigned int width )
896 {
897   DebugAssertScanlineParameters( pixels, width );
898   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(pixels) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
899
900   uint16_t* const alignedPixels = reinterpret_cast<uint16_t*>(pixels);
901
902   const unsigned int lastPair = EvenDown( width - 2 );
903
904   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
905   {
906     const uint32_t averaged = AveragePixelRGB565( alignedPixels[pixel], alignedPixels[pixel + 1] );
907     alignedPixels[outPixel] = averaged;
908   }
909 }
910
911 void HalveScanlineInPlace2Bytes( unsigned char * const pixels, const unsigned int width )
912 {
913   DebugAssertScanlineParameters( pixels, width );
914
915   const unsigned int lastPair = EvenDown( width - 2 );
916
917   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
918   {
919     // Load all the byte pixel components we need:
920     const unsigned int c11 = pixels[pixel * 2];
921     const unsigned int c12 = pixels[pixel * 2 + 1];
922     const unsigned int c21 = pixels[pixel * 2 + 2];
923     const unsigned int c22 = pixels[pixel * 2 + 3];
924
925     // Save the averaged byte pixel components:
926     pixels[outPixel * 2]     = AverageComponent( c11, c21 );
927     pixels[outPixel * 2 + 1] = AverageComponent( c12, c22 );
928   }
929 }
930
931 void HalveScanlineInPlace1Byte( unsigned char * const pixels, const unsigned int width )
932 {
933   DebugAssertScanlineParameters( pixels, width );
934
935   const unsigned int lastPair = EvenDown( width - 2 );
936
937   for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
938   {
939     // Load all the byte pixel components we need:
940     const unsigned int c1 = pixels[pixel];
941     const unsigned int c2 = pixels[pixel + 1];
942
943     // Save the averaged byte pixel component:
944     pixels[outPixel] = AverageComponent( c1, c2 );
945   }
946 }
947
948 /**
949  * @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.
950  * 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.
951  */
952 void AverageScanlines1( const unsigned char * const scanline1,
953                         const unsigned char * const __restrict__ scanline2,
954                         unsigned char* const outputScanline,
955                         const unsigned int width )
956 {
957   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width );
958
959   for( unsigned int component = 0; component < width; ++component )
960   {
961     outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
962   }
963 }
964
965 void AverageScanlines2( const unsigned char * const scanline1,
966                         const unsigned char * const __restrict__ scanline2,
967                         unsigned char* const outputScanline,
968                         const unsigned int width )
969 {
970   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 2 );
971
972   for( unsigned int component = 0; component < width * 2; ++component )
973   {
974     outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
975   }
976 }
977
978 void AverageScanlines3( const unsigned char * const scanline1,
979                         const unsigned char * const __restrict__ scanline2,
980                         unsigned char* const outputScanline,
981                         const unsigned int width )
982 {
983   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 3 );
984
985   for( unsigned int component = 0; component < width * 3; ++component )
986   {
987     outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
988   }
989 }
990
991 void AverageScanlinesRGBA8888( const unsigned char * const scanline1,
992                                const unsigned char * const __restrict__ scanline2,
993                                unsigned char * const outputScanline,
994                                const unsigned int width )
995 {
996   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 4 );
997   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline1) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
998   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline2) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
999   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(outputScanline) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
1000
1001   const uint32_t* const alignedScanline1 = reinterpret_cast<const uint32_t*>(scanline1);
1002   const uint32_t* const alignedScanline2 = reinterpret_cast<const uint32_t*>(scanline2);
1003   uint32_t* const alignedOutput = reinterpret_cast<uint32_t*>(outputScanline);
1004
1005   for( unsigned int pixel = 0; pixel < width; ++pixel )
1006   {
1007     alignedOutput[pixel] = AveragePixelRGBA8888( alignedScanline1[pixel], alignedScanline2[pixel] );
1008   }
1009 }
1010
1011 void AverageScanlinesRGB565( const unsigned char * const scanline1,
1012                              const unsigned char * const __restrict__ scanline2,
1013                              unsigned char * const outputScanline,
1014                              const unsigned int width )
1015 {
1016   DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 2 );
1017   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline1) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
1018   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(scanline2) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
1019   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(outputScanline) & 1u) == 0u) && "Pointer should be 2-byte aligned for performance on some platforms." );
1020
1021   const uint16_t* const alignedScanline1 = reinterpret_cast<const uint16_t*>(scanline1);
1022   const uint16_t* const alignedScanline2 = reinterpret_cast<const uint16_t*>(scanline2);
1023   uint16_t* const alignedOutput = reinterpret_cast<uint16_t*>(outputScanline);
1024
1025   for( unsigned int pixel = 0; pixel < width; ++pixel )
1026   {
1027     alignedOutput[pixel] = AveragePixelRGB565( alignedScanline1[pixel], alignedScanline2[pixel] );
1028   }
1029 }
1030
1031 /// Dispatch to pixel format appropriate box filter downscaling functions.
1032 void DownscaleInPlacePow2( unsigned char * const pixels,
1033                            Pixel::Format pixelFormat,
1034                            unsigned int inputWidth,
1035                            unsigned int inputHeight,
1036                            unsigned int desiredWidth,
1037                            unsigned int desiredHeight,
1038                            FittingMode::Type fittingMode,
1039                            SamplingMode::Type samplingMode,
1040                            unsigned& outWidth,
1041                            unsigned& outHeight )
1042 {
1043   outWidth = inputWidth;
1044   outHeight = inputHeight;
1045   // Perform power of 2 iterated 4:1 box filtering if the requested filter mode requires it:
1046   if( samplingMode == SamplingMode::BOX || samplingMode == SamplingMode::BOX_THEN_NEAREST || samplingMode == SamplingMode::BOX_THEN_LINEAR )
1047   {
1048     // Check the pixel format is one that is supported:
1049     if( pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 )
1050     {
1051       const BoxDimensionTest dimensionTest = DimensionTestForScalingMode( fittingMode );
1052
1053       if( pixelFormat == Pixel::RGBA8888 )
1054       {
1055         Internal::Platform::DownscaleInPlacePow2RGBA8888( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1056       }
1057       else if( pixelFormat == Pixel::RGB888 )
1058       {
1059         Internal::Platform::DownscaleInPlacePow2RGB888( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1060       }
1061       else if( pixelFormat == Pixel::RGB565 )
1062       {
1063         Internal::Platform::DownscaleInPlacePow2RGB565( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1064       }
1065       else if( pixelFormat == Pixel::LA88 )
1066       {
1067         Internal::Platform::DownscaleInPlacePow2ComponentPair( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1068       }
1069       else if( pixelFormat == Pixel::L8  || pixelFormat == Pixel::A8 )
1070       {
1071         Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1072       }
1073       else
1074       {
1075         DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." );
1076       }
1077     }
1078   }
1079   else
1080   {
1081     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not shrunk: unsupported pixel format: %u.\n", unsigned(pixelFormat) );
1082   }
1083 }
1084
1085 void DownscaleInPlacePow2RGB888( unsigned char *pixels,
1086                                  unsigned int inputWidth,
1087                                  unsigned int inputHeight,
1088                                  unsigned int desiredWidth,
1089                                  unsigned int desiredHeight,
1090                                  BoxDimensionTest dimensionTest,
1091                                  unsigned& outWidth,
1092                                  unsigned& outHeight )
1093 {
1094   DownscaleInPlacePow2Generic<3, HalveScanlineInPlaceRGB888, AverageScanlines3>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1095 }
1096
1097 void DownscaleInPlacePow2RGBA8888( unsigned char * pixels,
1098                                    unsigned int inputWidth,
1099                                    unsigned int inputHeight,
1100                                    unsigned int desiredWidth,
1101                                    unsigned int desiredHeight,
1102                                    BoxDimensionTest dimensionTest,
1103                                    unsigned& outWidth,
1104                                    unsigned& outHeight )
1105 {
1106   DALI_ASSERT_DEBUG( ((reinterpret_cast<ptrdiff_t>(pixels) & 3u) == 0u) && "Pointer should be 4-byte aligned for performance on some platforms." );
1107   DownscaleInPlacePow2Generic<4, HalveScanlineInPlaceRGBA8888, AverageScanlinesRGBA8888>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1108 }
1109
1110 void DownscaleInPlacePow2RGB565( unsigned char * pixels,
1111                                  unsigned int inputWidth,
1112                                  unsigned int inputHeight,
1113                                  unsigned int desiredWidth,
1114                                  unsigned int desiredHeight,
1115                                  BoxDimensionTest dimensionTest,
1116                                  unsigned int& outWidth,
1117                                  unsigned int& outHeight )
1118 {
1119   DownscaleInPlacePow2Generic<2, HalveScanlineInPlaceRGB565, AverageScanlinesRGB565>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1120 }
1121
1122 /**
1123  * @copydoc DownscaleInPlacePow2RGB888
1124  *
1125  * For 2-byte formats such as lum8alpha8, but not packed 16 bit formats like RGB565.
1126  */
1127 void DownscaleInPlacePow2ComponentPair( unsigned char *pixels,
1128                                         unsigned int inputWidth,
1129                                         unsigned int inputHeight,
1130                                         unsigned int desiredWidth,
1131                                         unsigned int desiredHeight,
1132                                         BoxDimensionTest dimensionTest,
1133                                         unsigned& outWidth,
1134                                         unsigned& outHeight )
1135 {
1136   DownscaleInPlacePow2Generic<2, HalveScanlineInPlace2Bytes, AverageScanlines2>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1137 }
1138
1139 void DownscaleInPlacePow2SingleBytePerPixel( unsigned char * pixels,
1140                                              unsigned int inputWidth,
1141                                              unsigned int inputHeight,
1142                                              unsigned int desiredWidth,
1143                                              unsigned int desiredHeight,
1144                                              BoxDimensionTest dimensionTest,
1145                                              unsigned int& outWidth,
1146                                              unsigned int& outHeight )
1147 {
1148   DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
1149 }
1150
1151 namespace
1152 {
1153
1154 /**
1155  * @brief Point sample an image to a new resolution (like GL_NEAREST).
1156  *
1157  * Template is used purely as a type-safe code generator in this one
1158  * compilation unit. Generated code is inlined into type-specific wrapper
1159  * functions below which are exported to rest of module.
1160  */
1161 template<typename PIXEL>
1162 inline void PointSampleAddressablePixels( const uint8_t * inPixels,
1163                                    unsigned int inputWidth,
1164                                    unsigned int inputHeight,
1165                                    uint8_t * outPixels,
1166                                    unsigned int desiredWidth,
1167                                    unsigned int desiredHeight )
1168 {
1169   DALI_ASSERT_DEBUG( ((desiredWidth <= inputWidth && desiredHeight <= inputHeight) ||
1170       outPixels >= inPixels + inputWidth * inputHeight * sizeof(PIXEL) || outPixels <= inPixels - desiredWidth * desiredHeight * sizeof(PIXEL)) &&
1171       "The input and output buffers must not overlap for an upscaling.");
1172   DALI_ASSERT_DEBUG( ((uint64_t) inPixels)  % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
1173   DALI_ASSERT_DEBUG( ((uint64_t) outPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
1174
1175   if( inputWidth < 1u || inputHeight < 1u || desiredWidth < 1u || desiredHeight < 1u )
1176   {
1177     return;
1178   }
1179   const PIXEL* const inAligned = reinterpret_cast<const PIXEL*>(inPixels);
1180   PIXEL* const       outAligned = reinterpret_cast<PIXEL*>(outPixels);
1181   const unsigned int deltaX = (inputWidth  << 16u) / desiredWidth;
1182   const unsigned int deltaY = (inputHeight << 16u) / desiredHeight;
1183
1184   unsigned int inY = 0;
1185   for( unsigned int outY = 0; outY < desiredHeight; ++outY )
1186   {
1187     // Round fixed point y coordinate to nearest integer:
1188     const unsigned int integerY = (inY + (1u << 15u)) >> 16u;
1189     const PIXEL* const inScanline = &inAligned[inputWidth * integerY];
1190     PIXEL* const outScanline = &outAligned[desiredWidth * outY];
1191
1192     DALI_ASSERT_DEBUG( integerY < inputHeight );
1193     DALI_ASSERT_DEBUG( reinterpret_cast<const uint8_t*>(inScanline) < ( inPixels + inputWidth * inputHeight * sizeof(PIXEL) ) );
1194     DALI_ASSERT_DEBUG( reinterpret_cast<uint8_t*>(outScanline) < ( outPixels + desiredWidth * desiredHeight * sizeof(PIXEL) ) );
1195
1196     unsigned int inX = 0;
1197     for( unsigned int outX = 0; outX < desiredWidth; ++outX )
1198     {
1199       // Round the fixed-point x coordinate to an integer:
1200       const unsigned int integerX = (inX + (1u << 15u)) >> 16u;
1201       const PIXEL* const inPixelAddress = &inScanline[integerX];
1202       const PIXEL pixel = *inPixelAddress;
1203       outScanline[outX] = pixel;
1204       inX += deltaX;
1205     }
1206     inY += deltaY;
1207   }
1208 }
1209
1210 }
1211
1212 // RGBA8888
1213 void PointSample4BPP( const unsigned char * inPixels,
1214                       unsigned int inputWidth,
1215                       unsigned int inputHeight,
1216                       unsigned char * outPixels,
1217                       unsigned int desiredWidth,
1218                       unsigned int desiredHeight )
1219 {
1220   PointSampleAddressablePixels<uint32_t>( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1221 }
1222
1223 // RGB565, LA88
1224 void PointSample2BPP( const unsigned char * inPixels,
1225                       unsigned int inputWidth,
1226                       unsigned int inputHeight,
1227                       unsigned char * outPixels,
1228                       unsigned int desiredWidth,
1229                       unsigned int desiredHeight )
1230 {
1231   PointSampleAddressablePixels<uint16_t>( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1232 }
1233
1234 // L8, A8
1235 void PointSample1BPP( const unsigned char * inPixels,
1236                       unsigned int inputWidth,
1237                       unsigned int inputHeight,
1238                       unsigned char * outPixels,
1239                       unsigned int desiredWidth,
1240                       unsigned int desiredHeight )
1241 {
1242   PointSampleAddressablePixels<uint8_t>( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1243 }
1244
1245 /* RGB888
1246  * RGB888 is a special case as its pixels are not aligned addressable units.
1247  */
1248 void PointSample3BPP( const uint8_t * inPixels,
1249                       unsigned int inputWidth,
1250                       unsigned int inputHeight,
1251                       uint8_t * outPixels,
1252                       unsigned int desiredWidth,
1253                       unsigned int desiredHeight )
1254 {
1255   if( inputWidth < 1u || inputHeight < 1u || desiredWidth < 1u || desiredHeight < 1u )
1256   {
1257     return;
1258   }
1259   const unsigned int BYTES_PER_PIXEL = 3;
1260
1261   // Generate fixed-point 16.16 deltas in input image coordinates:
1262   const unsigned int deltaX = (inputWidth  << 16u) / desiredWidth;
1263   const unsigned int deltaY = (inputHeight << 16u) / desiredHeight;
1264
1265   // Step through output image in whole integer pixel steps while tracking the
1266   // corresponding locations in the input image using 16.16 fixed-point
1267   // coordinates:
1268   unsigned int inY = 0; //< 16.16 fixed-point input image y-coord.
1269   for( unsigned int outY = 0; outY < desiredHeight; ++outY )
1270   {
1271     const unsigned int integerY = (inY + (1u << 15u)) >> 16u;
1272     const uint8_t* const inScanline = &inPixels[inputWidth * integerY * BYTES_PER_PIXEL];
1273     uint8_t* const outScanline = &outPixels[desiredWidth * outY * BYTES_PER_PIXEL];
1274     unsigned int inX = 0; //< 16.16 fixed-point input image x-coord.
1275
1276     for( unsigned int outX = 0; outX < desiredWidth * BYTES_PER_PIXEL; outX += BYTES_PER_PIXEL )
1277     {
1278       // Round the fixed-point input coordinate to the address of the input pixel to sample:
1279       const unsigned int integerX = (inX + (1u << 15u)) >> 16u;
1280       const uint8_t* const inPixelAddress = &inScanline[integerX * BYTES_PER_PIXEL];
1281
1282       // Issue loads for all pixel color components up-front:
1283       const unsigned int c0 = inPixelAddress[0];
1284       const unsigned int c1 = inPixelAddress[1];
1285       const unsigned int c2 = inPixelAddress[2];
1286       ///@ToDo: Optimise - Benchmark one 32bit load that will be unaligned 2/3 of the time + 3 rotate and masks, versus these three aligned byte loads, versus using an RGB packed, aligned(1) struct and letting compiler pick a strategy.
1287
1288       // Output the pixel components:
1289       outScanline[outX]     = c0;
1290       outScanline[outX + 1] = c1;
1291       outScanline[outX + 2] = c2;
1292
1293       // Increment the fixed-point input coordinate:
1294       inX += deltaX;
1295     }
1296
1297     inY += deltaY;
1298   }
1299 }
1300
1301 // Dispatch to a format-appropriate point sampling function:
1302 void PointSample( const unsigned char * inPixels,
1303                   unsigned int inputWidth,
1304                   unsigned int inputHeight,
1305                   Pixel::Format pixelFormat,
1306                   unsigned char * outPixels,
1307                   unsigned int desiredWidth,
1308                   unsigned int desiredHeight )
1309 {
1310   // Check the pixel format is one that is supported:
1311   if( pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 )
1312   {
1313     if( pixelFormat == Pixel::RGB888 )
1314     {
1315       PointSample3BPP( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1316     }
1317     else if( pixelFormat == Pixel::RGBA8888 )
1318     {
1319       PointSample4BPP( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1320     }
1321     else if( pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88 )
1322     {
1323       PointSample2BPP( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1324     }
1325     else if( pixelFormat == Pixel::L8  || pixelFormat == Pixel::A8 )
1326     {
1327       PointSample1BPP( inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight );
1328     }
1329     else
1330     {
1331       DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." );
1332     }
1333   }
1334   else
1335   {
1336     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not point sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat) );
1337   }
1338 }
1339
1340 // Linear sampling group below
1341
1342 namespace
1343 {
1344
1345 /** @brief Blend 4 pixels together using horizontal and vertical weights. */
1346 inline uint8_t BilinearFilter1BPPByte( uint8_t tl, uint8_t tr, uint8_t bl, uint8_t br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
1347 {
1348   return BilinearFilter1Component( tl, tr, bl, br, fractBlendHorizontal, fractBlendVertical );
1349 }
1350
1351 /** @copydoc BilinearFilter1BPPByte */
1352 inline Pixel2Bytes BilinearFilter2Bytes( Pixel2Bytes tl, Pixel2Bytes tr, Pixel2Bytes bl, Pixel2Bytes br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
1353 {
1354   Pixel2Bytes pixel;
1355   pixel.l = BilinearFilter1Component( tl.l, tr.l, bl.l, br.l, fractBlendHorizontal, fractBlendVertical );
1356   pixel.a = BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical );
1357   return pixel;
1358 }
1359
1360 /** @copydoc BilinearFilter1BPPByte */
1361 inline Pixel3Bytes BilinearFilterRGB888( Pixel3Bytes tl, Pixel3Bytes tr, Pixel3Bytes bl, Pixel3Bytes br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
1362 {
1363   Pixel3Bytes pixel;
1364   pixel.r = BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical );
1365   pixel.g = BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical );
1366   pixel.b = BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical );
1367   return pixel;
1368 }
1369
1370 /** @copydoc BilinearFilter1BPPByte */
1371 inline PixelRGB565 BilinearFilterRGB565( PixelRGB565 tl, PixelRGB565 tr, PixelRGB565 bl, PixelRGB565 br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
1372 {
1373   const PixelRGB565 pixel = (BilinearFilter1Component( tl >> 11u, tr >> 11u, bl >> 11u, br >> 11u, fractBlendHorizontal, fractBlendVertical ) << 11u) +
1374                             (BilinearFilter1Component( (tl >> 5u) & 63u, (tr >> 5u) & 63u, (bl >> 5u) & 63u, (br >> 5u) & 63u, fractBlendHorizontal, fractBlendVertical ) << 5u) +
1375                              BilinearFilter1Component( tl & 31u, tr & 31u, bl & 31u, br & 31u, fractBlendHorizontal, fractBlendVertical );
1376   return pixel;
1377 }
1378
1379 /** @copydoc BilinearFilter1BPPByte */
1380 inline Pixel4Bytes BilinearFilter4Bytes( Pixel4Bytes tl, Pixel4Bytes tr, Pixel4Bytes bl, Pixel4Bytes br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
1381 {
1382   Pixel4Bytes pixel;
1383   pixel.r = BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical );
1384   pixel.g = BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical );
1385   pixel.b = BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical );
1386   pixel.a = BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical );
1387   return pixel;
1388 }
1389
1390 /**
1391  * @brief Generic version of bilinear sampling image resize function.
1392  * @note Limited to one compilation unit and exposed through type-specific
1393  * wrapper functions below.
1394  */
1395 template<
1396   typename PIXEL,
1397   PIXEL (*BilinearFilter) ( PIXEL tl, PIXEL tr, PIXEL bl, PIXEL br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical ),
1398   bool DEBUG_ASSERT_ALIGNMENT
1399 >
1400 inline void LinearSampleGeneric( const unsigned char * __restrict__ inPixels,
1401                        ImageDimensions inputDimensions,
1402                        unsigned char * __restrict__ outPixels,
1403                        ImageDimensions desiredDimensions )
1404 {
1405   const unsigned int inputWidth = inputDimensions.GetWidth();
1406   const unsigned int inputHeight = inputDimensions.GetHeight();
1407   const unsigned int desiredWidth = desiredDimensions.GetWidth();
1408   const unsigned int desiredHeight = desiredDimensions.GetHeight();
1409
1410   DALI_ASSERT_DEBUG( ((outPixels >= inPixels + inputWidth   * inputHeight   * sizeof(PIXEL)) ||
1411                       (inPixels >= outPixels + desiredWidth * desiredHeight * sizeof(PIXEL))) &&
1412                      "Input and output buffers cannot overlap.");
1413   if( DEBUG_ASSERT_ALIGNMENT )
1414   {
1415     DALI_ASSERT_DEBUG( ((uint64_t) inPixels)  % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
1416     DALI_ASSERT_DEBUG( ((uint64_t) outPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
1417   }
1418
1419   if( inputWidth < 1u || inputHeight < 1u || desiredWidth < 1u || desiredHeight < 1u )
1420   {
1421     return;
1422   }
1423   const PIXEL* const inAligned = reinterpret_cast<const PIXEL*>(inPixels);
1424   PIXEL* const       outAligned = reinterpret_cast<PIXEL*>(outPixels);
1425   const unsigned int deltaX = (inputWidth  << 16u) / desiredWidth;
1426   const unsigned int deltaY = (inputHeight << 16u) / desiredHeight;
1427
1428   unsigned int inY = 0;
1429   for( unsigned int outY = 0; outY < desiredHeight; ++outY )
1430   {
1431     PIXEL* const outScanline = &outAligned[desiredWidth * outY];
1432
1433     // Find the two scanlines to blend and the weight to blend with:
1434     const unsigned int integerY1 = inY >> 16u;
1435     const unsigned int integerY2 = integerY1 >= inputHeight ? integerY1 : integerY1 + 1;
1436     const unsigned int inputYWeight = inY & 65535u;
1437
1438     DALI_ASSERT_DEBUG( integerY1 < inputHeight );
1439     DALI_ASSERT_DEBUG( integerY2 < inputHeight );
1440
1441     const PIXEL* const inScanline1 = &inAligned[inputWidth * integerY1];
1442     const PIXEL* const inScanline2 = &inAligned[inputWidth * integerY2];
1443
1444     unsigned int inX = 0;
1445     for( unsigned int outX = 0; outX < desiredWidth; ++outX )
1446     {
1447       // Work out the two pixel scanline offsets for this cluster of four samples:
1448       const unsigned int integerX1 = inX >> 16u;
1449       const unsigned int integerX2 = integerX1 >= inputWidth ? integerX1 : integerX1 + 1;
1450
1451       // Execute the loads:
1452       const PIXEL pixel1 = inScanline1[integerX1];
1453       const PIXEL pixel2 = inScanline2[integerX1];
1454       const PIXEL pixel3 = inScanline1[integerX2];
1455       const PIXEL pixel4 = inScanline2[integerX2];
1456       ///@ToDo Optimise - for 1 and 2  and 4 byte types to execute a single 2, 4, or 8 byte load per pair (caveat clamping) and let half of them be unaligned.
1457
1458       // Weighted bilinear filter:
1459       const unsigned int inputXWeight = inX & 65535u;
1460       outScanline[outX] = BilinearFilter( pixel1, pixel3, pixel2, pixel4, inputXWeight, inputYWeight );
1461
1462       inX += deltaX;
1463     }
1464     inY += deltaY;
1465   }
1466 }
1467
1468 }
1469
1470 // Format-specific linear scaling instantiations:
1471
1472 void LinearSample1BPP( const unsigned char * __restrict__ inPixels,
1473                        ImageDimensions inputDimensions,
1474                        unsigned char * __restrict__ outPixels,
1475                        ImageDimensions desiredDimensions )
1476 {
1477   LinearSampleGeneric<uint8_t, BilinearFilter1BPPByte, false>( inPixels, inputDimensions, outPixels, desiredDimensions );
1478 }
1479
1480 void LinearSample2BPP( const unsigned char * __restrict__ inPixels,
1481                        ImageDimensions inputDimensions,
1482                        unsigned char * __restrict__ outPixels,
1483                        ImageDimensions desiredDimensions )
1484 {
1485   LinearSampleGeneric<Pixel2Bytes, BilinearFilter2Bytes, true>( inPixels, inputDimensions, outPixels, desiredDimensions );
1486 }
1487
1488 void LinearSampleRGB565( const unsigned char * __restrict__ inPixels,
1489                        ImageDimensions inputDimensions,
1490                        unsigned char * __restrict__ outPixels,
1491                        ImageDimensions desiredDimensions )
1492 {
1493   LinearSampleGeneric<PixelRGB565, BilinearFilterRGB565, true>( inPixels, inputDimensions, outPixels, desiredDimensions );
1494 }
1495
1496 void LinearSample3BPP( const unsigned char * __restrict__ inPixels,
1497                        ImageDimensions inputDimensions,
1498                        unsigned char * __restrict__ outPixels,
1499                        ImageDimensions desiredDimensions )
1500 {
1501   LinearSampleGeneric<Pixel3Bytes, BilinearFilterRGB888, false>( inPixels, inputDimensions, outPixels, desiredDimensions );
1502 }
1503
1504 void LinearSample4BPP( const unsigned char * __restrict__ inPixels,
1505                        ImageDimensions inputDimensions,
1506                        unsigned char * __restrict__ outPixels,
1507                        ImageDimensions desiredDimensions )
1508 {
1509   LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>( inPixels, inputDimensions, outPixels, desiredDimensions );
1510 }
1511
1512 // Dispatch to a format-appropriate linear sampling function:
1513 void LinearSample( const unsigned char * __restrict__ inPixels,
1514                    ImageDimensions inDimensions,
1515                    Pixel::Format pixelFormat,
1516                    unsigned char * __restrict__ outPixels,
1517                    ImageDimensions outDimensions )
1518 {
1519   // Check the pixel format is one that is supported:
1520   if( pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565 )
1521   {
1522     if( pixelFormat == Pixel::RGB888 )
1523     {
1524       LinearSample3BPP( inPixels, inDimensions, outPixels, outDimensions );
1525     }
1526     else if( pixelFormat == Pixel::RGBA8888 )
1527     {
1528       LinearSample4BPP( inPixels, inDimensions, outPixels, outDimensions );
1529     }
1530     else if( pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 )
1531     {
1532       LinearSample1BPP( inPixels, inDimensions, outPixels, outDimensions );
1533     }
1534     else if( pixelFormat == Pixel::LA88 )
1535     {
1536       LinearSample2BPP( inPixels, inDimensions, outPixels, outDimensions );
1537     }
1538     else if ( pixelFormat == Pixel::RGB565 )
1539     {
1540       LinearSampleRGB565( inPixels, inDimensions, outPixels, outDimensions );
1541     }
1542     else
1543     {
1544       DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." );
1545     }
1546   }
1547   else
1548   {
1549     DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat) );
1550   }
1551 }
1552
1553 } /* namespace Platform */
1554 } /* namespace Internal */
1555 } /* namespace Dali */