2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
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>
37 using Integration::Bitmap;
38 using Integration::BitmapPtr;
39 typedef unsigned char PixelBuffer;
41 #if defined(DEBUG_ENABLED)
43 * Disable logging of image operations or make it verbose from the commandline
44 * as follows (e.g., for dali demo app):
46 * LOG_IMAGE_OPERATIONS=0 dali-demo #< off
47 * LOG_IMAGE_OPERATIONS=3 dali-demo #< on, verbose
50 Debug::Filter* gImageOpsLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_IMAGE_OPERATIONS" );
53 /** @return The greatest even number less than or equal to the argument. */
54 inline unsigned int EvenDown( const unsigned int a )
56 const unsigned int evened = a & ~1u;
61 * @brief Log bad parameters.
63 void ValidateScalingParameters(
64 const unsigned int inputWidth, const unsigned int inputHeight,
65 const unsigned int desiredWidth, const unsigned int desiredHeight )
67 if( desiredWidth > inputWidth || desiredHeight > inputHeight )
69 DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Upscaling not supported (%u, %u -> %u, %u).\n", inputWidth, inputHeight, desiredWidth, desiredHeight );
72 if( desiredWidth == 0u || desiredHeight == 0u )
74 DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Downscaling to a zero-area target is pointless." );
77 if( inputWidth == 0u || inputHeight == 0u )
79 DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Zero area images cannot be scaled" );
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.
87 inline void DebugAssertScanlineParameters(
88 unsigned char * const pixels,
89 const unsigned int width )
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?" );
97 * @brief Assertions on params to functions averaging pairs of scanlines.
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 )
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." );
112 } // namespace - unnamed
115 * @brief Implement ImageAttributes::ScaleTofill scaling mode.
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.
124 Integration::BitmapPtr ProcessBitmapScaleToFill( Integration::BitmapPtr bitmap, const ImageAttributes& requestedAttributes );
127 BitmapPtr ApplyAttributesToBitmap( BitmapPtr bitmap, const ImageAttributes& requestedAttributes )
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:
134 bitmap = DownscaleBitmap( *bitmap, requestedAttributes );
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 )
141 bitmap = ProcessBitmapScaleToFill( bitmap, requestedAttributes );
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() ) )
148 bitmap->GetPackedPixelsProfile()->TestForTransparency();
153 BitmapPtr ProcessBitmapScaleToFill( BitmapPtr bitmap, const ImageAttributes& requestedAttributes )
155 const unsigned loadedWidth = bitmap->GetImageWidth();
156 const unsigned loadedHeight = bitmap->GetImageHeight();
157 const unsigned desiredWidth = requestedAttributes.GetWidth();
158 const unsigned desiredHeight = requestedAttributes.GetHeight();
160 if( desiredWidth < 1U || desiredHeight < 1U )
162 DALI_LOG_WARNING( "Image scaling aborted as desired dimensions too small (%u, %u)\n.", desiredWidth, desiredHeight );
164 else if( loadedWidth != desiredWidth || loadedHeight != desiredHeight )
166 const Vector2 desiredDims( desiredWidth, desiredHeight );
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;
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 );
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" );
185 // Make a new bitmap with the central part of the loaded one if required:
186 if( scanlinesToTrim > 0 || columnsToTrim > 0 )
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 );
196 const unsigned bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
198 const PixelBuffer * const srcPixels = bitmap->GetBuffer() + scanlinesToTrim * loadedWidth * bytesPerPixel;
199 PixelBuffer * const destPixels = croppedBitmap->GetBuffer();
200 DALI_ASSERT_DEBUG( srcPixels && destPixels );
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 )
205 memcpy( destPixels, srcPixels, newHeight * newWidth * bytesPerPixel );
209 for( unsigned y = 0; y < newHeight; ++y )
211 memcpy( &destPixels[y * newWidth * bytesPerPixel], &srcPixels[y * loadedWidth * bytesPerPixel + columnsToTrim * bytesPerPixel], newWidth * bytesPerPixel );
215 // Overwrite the loaded bitmap with the cropped version:
216 bitmap = croppedBitmap;
227 * @brief Converts a scaling mode to the definition of which dimensions matter when box filtering as a part of that mode.
229 BoxDimensionTest DimensionTestForScalingMode( ImageAttributes::ScalingMode scalingMode )
231 BoxDimensionTest dimensionTest;
232 dimensionTest = BoxDimensionTestEither;
234 switch( scalingMode )
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;
244 // Scale to fill mode keeps both dimensions at least as large as desired:
245 case ImageAttributes::ScaleToFill:
246 dimensionTest = BoxDimensionTestBoth;
248 // Y dimension is irrelevant when downscaling in FitWidth mode:
249 case ImageAttributes::FitWidth:
250 dimensionTest = BoxDimensionTestX;
252 // X Dimension is ignored by definition in FitHeight mode:
253 case ImageAttributes::FitHeight:
254 dimensionTest = BoxDimensionTestY;
257 return dimensionTest;
262 // The top-level function to return a downscaled version of a bitmap:
263 Integration::BitmapPtr DownscaleBitmap( Integration::Bitmap& bitmap, const ImageAttributes& requestedAttributes )
265 const unsigned int bitmapWidth = bitmap.GetImageWidth();
266 const unsigned int bitmapHeight = bitmap.GetImageHeight();
267 const Size requestedSize = requestedAttributes.GetSize();
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) )
275 const Pixel::Format pixelFormat = bitmap.GetPixelFormat();
276 const ImageAttributes::ScalingMode scalingMode = requestedAttributes.GetScalingMode();
277 const ImageAttributes::FilterMode filterMode = requestedAttributes.GetFilterMode();
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 )
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 )
285 unsigned int shrunkWidth = -1, shrunkHeight = -1;
286 const BoxDimensionTest dimensionTest = DimensionTestForScalingMode( scalingMode );
288 if( pixelFormat == Pixel::RGBA8888 )
290 Internal::Platform::DownscaleInPlacePow2RGBA8888( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
292 else if( pixelFormat == Pixel::RGB888 )
294 Internal::Platform::DownscaleInPlacePow2RGB888( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
296 else if( pixelFormat == Pixel::RGB565 )
298 Internal::Platform::DownscaleInPlacePow2RGB565( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
300 else if( pixelFormat == Pixel::LA88 )
302 Internal::Platform::DownscaleInPlacePow2ComponentPair( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
304 else if( pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 )
306 Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel( bitmap.GetBuffer(), bitmapWidth, bitmapHeight, requestedSize.x, requestedSize.y, dimensionTest, shrunkWidth, shrunkHeight );
309 if( shrunkWidth != bitmapWidth && shrunkHeight != bitmapHeight )
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 );
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 ) );
324 DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not shrunk: unsupported pixel format: %u.\n", unsigned(pixelFormat) );
328 return Integration::BitmapPtr(&bitmap);
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.
341 bool ContinueScaling( BoxDimensionTest test, unsigned int scaledWidth, unsigned int scaledHeight, unsigned int desiredWidth, unsigned int desiredHeight )
343 bool keepScaling = false;
344 const unsigned int nextWidth = scaledWidth >> 1u;
345 const unsigned int nextHeight = scaledHeight >> 1u;
347 if( nextWidth >= 1u && nextHeight >= 1u )
351 case BoxDimensionTestEither:
352 keepScaling = nextWidth >= desiredWidth || nextHeight >= desiredHeight;
354 case BoxDimensionTestBoth:
355 keepScaling = nextWidth >= desiredWidth && nextHeight >= desiredHeight;
357 case BoxDimensionTestX:
358 keepScaling = nextWidth >= desiredWidth;
360 case BoxDimensionTestY:
361 keepScaling = nextHeight >= desiredHeight;
369 * @brief A shared implementation of the overall iterative downscaling algorithm.
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.
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 )
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 )
392 ValidateScalingParameters( inputWidth, inputHeight, desiredWidth, desiredHeight );
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 )
401 const unsigned int lastWidth = scaledWidth;
405 DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Scaling to %u\t%u.\n", scaledWidth, scaledHeight );
407 const unsigned int lastScanlinePair = scaledHeight - 1;
409 // Scale pairs of scanlines until any spare one at the end is dropped:
410 for( unsigned int y = 0; y <= lastScanlinePair; ++y )
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 );
416 // Scale vertical pairs of pixels while the last two scanlines are still warm in
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.
422 &pixels[y * 2 * lastWidth * BYTES_PER_PIXEL],
423 &pixels[(y * 2 + 1) * lastWidth * BYTES_PER_PIXEL],
424 &pixels[y * scaledWidth * BYTES_PER_PIXEL],
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;
436 void HalveScanlineInPlaceRGB888(
437 unsigned char * const pixels,
438 const unsigned int width )
440 DebugAssertScanlineParameters( pixels, width );
442 const unsigned int lastPair = EvenDown( width - 2 );
444 for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
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];
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 );
461 void HalveScanlineInPlaceRGBA8888(
462 unsigned char * const pixels,
463 const unsigned int width )
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." );
468 uint32_t* const alignedPixels = reinterpret_cast<uint32_t*>(pixels);
470 const unsigned int lastPair = EvenDown( width - 2 );
472 for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
474 const uint32_t averaged = AveragePixelRGBA8888( alignedPixels[pixel], alignedPixels[pixel + 1] );
475 alignedPixels[outPixel] = averaged;
479 void HalveScanlineInPlaceRGB565( unsigned char * pixels, unsigned int width )
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." );
484 uint16_t* const alignedPixels = reinterpret_cast<uint16_t*>(pixels);
486 const unsigned int lastPair = EvenDown( width - 2 );
488 for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
490 const uint32_t averaged = AveragePixelRGB565( alignedPixels[pixel], alignedPixels[pixel + 1] );
491 alignedPixels[outPixel] = averaged;
495 void HalveScanlineInPlace2Bytes(
496 unsigned char * const pixels,
497 const unsigned int width )
499 DebugAssertScanlineParameters( pixels, width );
501 const unsigned int lastPair = EvenDown( width - 2 );
503 for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
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];
511 // Save the averaged byte pixel components:
512 pixels[outPixel * 2] = AverageComponent( c11, c21 );
513 pixels[outPixel * 2 + 1] = AverageComponent( c12, c22 );
517 void HalveScanlineInPlace1Byte(
518 unsigned char * const pixels,
519 const unsigned int width )
521 DebugAssertScanlineParameters( pixels, width );
523 const unsigned int lastPair = EvenDown( width - 2 );
525 for( unsigned int pixel = 0, outPixel = 0; pixel <= lastPair; pixel += 2, ++outPixel )
527 // Load all the byte pixel components we need:
528 const unsigned int c1 = pixels[pixel];
529 const unsigned int c2 = pixels[pixel + 1];
531 // Save the averaged byte pixel component:
532 pixels[outPixel] = AverageComponent( c1, c2 );
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.
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 )
546 DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width );
548 for( unsigned int component = 0; component < width; ++component )
550 outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
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 )
560 DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 2 );
562 for( unsigned int component = 0; component < width * 2; ++component )
564 outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
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 )
574 DebugAssertDualScanlineParameters( scanline1, scanline2, outputScanline, width * 3 );
576 for( unsigned int component = 0; component < width * 3; ++component )
578 outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
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 )
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." );
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);
597 for( unsigned int pixel = 0; pixel < width; ++pixel )
599 alignedOutput[pixel] = AveragePixelRGBA8888( alignedScanline1[pixel], alignedScanline2[pixel] );
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 )
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." );
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);
618 for( unsigned int pixel = 0; pixel < width; ++pixel )
620 alignedOutput[pixel] = AveragePixelRGB565( alignedScanline1[pixel], alignedScanline2[pixel] );
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 )
631 DownscaleInPlacePow2Generic<3, HalveScanlineInPlaceRGB888, AverageScanlines3>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
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 )
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 );
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 )
652 DownscaleInPlacePow2Generic<2, HalveScanlineInPlaceRGB565, AverageScanlinesRGB565>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
656 * @copydoc DownscaleInPlacePow2RGB888
658 * For 2-byte formats such as lum8alpha8, but not packed 16 bit formats like RGB565.
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 )
667 DownscaleInPlacePow2Generic<2, HalveScanlineInPlace2Bytes, AverageScanlines2>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
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 )
677 DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>( pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight );
680 } /* namespace Platform */
681 } /* namespace Internal */
682 } /* namespace Dali */