}
/**
- * @brief Work out the desired width and height according to the rules documented for the ImageAttributes class.
- *
- * @param[in] bitmapWidth Width of image before processing.
- * @param[in] bitmapHeight Height of image before processing.
- * @param[in] requestedWidth Width of area to scale image into. Can be zero.
- * @param[in] requestedHeight Height of area to scale image into. Can be zero.
- * @return Dimensions of area to scale image into after special rules are applied.
- * @see ImageAttributes
- */
-ImageDimensions CalculateDesiredDimensions( unsigned int bitmapWidth, unsigned int bitmapHeight, unsigned int requestedWidth, unsigned int requestedHeight )
-{
- // If no dimensions have been requested, default to the source ones:
- if( requestedWidth == 0 && requestedHeight == 0 )
- {
- return ImageDimensions( bitmapWidth, bitmapHeight );
- }
-
- // If both dimensions have values requested, use them both:
- if( requestedWidth != 0 && requestedHeight != 0 )
- {
- return ImageDimensions( requestedWidth, requestedHeight );
- }
-
- // If only one of the dimensions has been requested, calculate the other from
- // the requested one and the source image aspect ratio:
-
- if( requestedWidth != 0 )
- {
- return ImageDimensions( requestedWidth, bitmapHeight / float(bitmapWidth) * requestedWidth + 0.5f );
- }
- return ImageDimensions( bitmapWidth / float(bitmapHeight) * requestedHeight + 0.5f, requestedHeight );
-}
-
-/**
* @brief Converts a scaling mode to the definition of which dimensions matter when box filtering as a part of that mode.
*/
BoxDimensionTest DimensionTestForScalingMode( FittingMode::Type fittingMode )
*/
ImageDimensions FitForShrinkToFit( ImageDimensions target, ImageDimensions source )
{
- DALI_ASSERT_DEBUG( true && " " );
// Scale the input by the least extreme of the two dimensions:
const float widthScale = target.GetX() / float(source.GetX());
const float heightScale = target.GetY() / float(source.GetY());
/**
* @brief Work out the dimensions for a uniform scaling of the input to map it
* into the target while effecting SCALE_TO_FILL scaling mode.
- * @note The output dimensions will need either top and bottom or left and right
- * to be cropped away unless the source was pre-cropped to match the destination
- * aspect ratio.
+ * @note An image scaled into the output dimensions will need either top and
+ * bottom or left and right to be cropped away unless the source was pre-cropped
+ * to match the destination aspect ratio.
*/
ImageDimensions FitForScaleToFill( ImageDimensions target, ImageDimensions source )
{
return newBitmap;
}
+/**
+ * @brief Work out the desired width and height, accounting for zeros.
+ *
+ * @param[in] bitmapWidth Width of image before processing.
+ * @param[in] bitmapHeight Height of image before processing.
+ * @param[in] requestedWidth Width of area to scale image into. Can be zero.
+ * @param[in] requestedHeight Height of area to scale image into. Can be zero.
+ * @return Dimensions of area to scale image into after special rules are applied.
+ */
+ImageDimensions CalculateDesiredDimensions( unsigned int bitmapWidth, unsigned int bitmapHeight, unsigned int requestedWidth, unsigned int requestedHeight )
+{
+ // If no dimensions have been requested, default to the source ones:
+ if( requestedWidth == 0 && requestedHeight == 0 )
+ {
+ return ImageDimensions( bitmapWidth, bitmapHeight );
+ }
+
+ // If both dimensions have values requested, use them both:
+ if( requestedWidth != 0 && requestedHeight != 0 )
+ {
+ return ImageDimensions( requestedWidth, requestedHeight );
+ }
+
+ // Only one of the dimensions has been requested. Calculate the other from
+ // the requested one and the source image aspect ratio:
+ if( requestedWidth != 0 )
+ {
+ return ImageDimensions( requestedWidth, bitmapHeight / float(bitmapWidth) * requestedWidth + 0.5f );
+ }
+ return ImageDimensions( bitmapWidth / float(bitmapHeight) * requestedHeight + 0.5f, requestedHeight );
+}
+
} // namespace - unnamed
+ImageDimensions CalculateDesiredDimensions( ImageDimensions rawDimensions, ImageDimensions requestedDimensions )
+{
+ return CalculateDesiredDimensions( rawDimensions.GetWidth(), rawDimensions.GetHeight(), requestedDimensions.GetWidth(), requestedDimensions.GetHeight() ) ;
+}
+
/**
* @brief Implement ScaleTofill scaling mode cropping.
*
#include <dali/integration-api/bitmap.h>
#include <resource-loader/debug/resource-loader-debug.h>
#include "platform-capabilities.h"
+#include "image-operations.h"
// EXTERNAL HEADERS
#include <libexif/exif-data.h>
#include <cstring>
#include <setjmp.h>
-
namespace Dali
{
using Integration::Bitmap;
bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp);
bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp);
JPGFORM_CODE ConvertExifOrientation(ExifData* exifData);
-bool TransformSize( int requiredWidth, int requiredHeight, JPGFORM_CODE transform,
+bool TransformSize( int requiredWidth, int requiredHeight,
+ FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
+ JPGFORM_CODE transform,
int& preXformImageWidth, int& preXformImageHeight,
int& postXformImageWidth, int& postXformImageHeight );
int scaledPostXformWidth = postXformImageWidth;
int scaledPostXformHeight = postXformImageHeight;
- TransformSize( requiredWidth, requiredHeight, transform,
+ TransformSize( requiredWidth, requiredHeight,
+ input.scalingParameters.scalingMode,
+ input.scalingParameters.samplingMode,
+ transform,
scaledPreXformWidth, scaledPreXformHeight,
scaledPostXformWidth, scaledPostXformHeight );
// Allow early cancellation before decoding:
client.InterruptionPoint();
- const int pitch = scaledPreXformWidth * DECODED_PIXEL_SIZE;
- if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, pitch, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 )
+ if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, 0, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 )
{
DALI_LOG_ERROR("%s\n", tjGetErrorStr());
return false;
bool result = false;
switch(transform)
{
- case JPGFORM_FLIP_H:
- case JPGFORM_FLIP_V:
- case JPGFORM_TRANSPOSE:
- case JPGFORM_TRANSVERSE: ///!ToDo: None of these are supported!
- {
- DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform );
- break;
- }
case JPGFORM_NONE:
{
result = true;
break;
}
+ // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
case JPGFORM_ROT_180:
{
result = JpegRotate180(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
result = JpegRotate90(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
break;
}
+ /// Less-common orientation changes, since they don't correspond to a camera's
+ // physical orientation:
+ case JPGFORM_FLIP_H:
+ case JPGFORM_FLIP_V:
+ case JPGFORM_TRANSPOSE:
+ case JPGFORM_TRANSVERSE:
+ {
+ DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform );
+ break;
+ }
}
return result;
}
return transform;
}
-bool TransformSize( int requiredWidth, int requiredHeight, JPGFORM_CODE transform,
+bool TransformSize( int requiredWidth, int requiredHeight,
+ FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
+ JPGFORM_CODE transform,
int& preXformImageWidth, int& preXformImageHeight,
int& postXformImageWidth, int& postXformImageHeight )
{
std::swap( postXformImageWidth, postXformImageHeight );
}
+ // Apply the special rules for when there are one or two zeros in requested dimensions:
+ const ImageDimensions correctedDesired = Internal::Platform::CalculateDesiredDimensions( ImageDimensions( postXformImageWidth, postXformImageHeight), ImageDimensions( requiredWidth, requiredHeight ) );
+ requiredWidth = correctedDesired.GetWidth();
+ requiredHeight = correctedDesired.GetHeight();
+
// Rescale image during decode using one of the decoder's built-in rescaling
// ratios (expected to be powers of 2), keeping the final image at least as
// wide and high as was requested:
}
else
{
- // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
- int scaleFactorIndex( 0 );
- for( int i = 1; i < numFactors; ++i )
+ // Internal jpeg downscaling is the same as our BOX_X sampling modes so only
+ // apply it if the application requested one of those:
+ // (use a switch case here so this code will fail to compile if other modes are added)
+ bool downscale = true;
+ switch( samplingMode )
{
- // if requested width or height set to 0, ignore value
- // TJSCALED performs an integer-based ceil operation on (dim*factor)
- if( (requiredWidth && TJSCALED(postXformImageWidth , (factors[i])) > requiredWidth) ||
- (requiredHeight && TJSCALED(postXformImageHeight, (factors[i])) > requiredHeight) )
+ case SamplingMode::BOX:
+ case SamplingMode::BOX_THEN_NEAREST:
+ case SamplingMode::BOX_THEN_LINEAR:
+ case SamplingMode::DONT_CARE:
{
- scaleFactorIndex = i;
+ downscale = true;
+ break;
}
- else
+ case SamplingMode::NO_FILTER:
+ case SamplingMode::NEAREST:
+ case SamplingMode::LINEAR:
{
- // This scaling would result in an image that was smaller than requested in both
- // dimensions, so stop at the previous entry.
+ downscale = false;
break;
}
}
- // Workaround for libjpeg-turbo problem adding a green line on one edge
- // when downscaling to 1/8 in each dimension. Prefer not to scale to less than
- // 1/4 in each dimension:
- if( 2 < scaleFactorIndex )
+ int scaleFactorIndex( 0 );
+ if( downscale )
{
- scaleFactorIndex = 2;
- DALI_LOG_INFO( gLoaderFilter, Debug::General, "Down-scaling requested for image limited to 1/4.\n" );
+ // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
+ for( int i = 1; i < numFactors; ++i )
+ {
+ bool widthLessRequired = TJSCALED( postXformImageWidth, factors[i]) < requiredWidth;
+ bool heightLessRequired = TJSCALED( postXformImageHeight, factors[i]) < requiredHeight;
+ switch( fittingMode )
+ {
+ // If either scaled dimension is smaller than the desired one, we were done at the last iteration:
+ case FittingMode::SCALE_TO_FILL:
+ {
+ if ( widthLessRequired || heightLessRequired )
+ {
+ break;
+ }
+ }
+ // If both dimensions are smaller than the desired one, we were done at the last iteration:
+ case FittingMode::SHRINK_TO_FIT:
+ {
+ if ( widthLessRequired && heightLessRequired )
+ {
+ break;
+ }
+ }
+ // If the width is smaller than the desired one, we were done at the last iteration:
+ case FittingMode::FIT_WIDTH:
+ {
+ if ( widthLessRequired )
+ {
+ break;
+ }
+ }
+ // If the width is smaller than the desired one, we were done at the last iteration:
+ case FittingMode::FIT_HEIGHT:
+ {
+ if ( heightLessRequired )
+ {
+ break;
+ }
+ }
+
+ // This factor stays is within our fitting mode constraint so remember it:
+ scaleFactorIndex = i;
+ }
+ }
}
- // Regardless of requested size, downscale to avoid exceeding the maximum texture size
+ // Regardless of requested size, downscale to avoid exceeding the maximum texture size:
for( int i = scaleFactorIndex; i < numFactors; ++i )
{
// Continue downscaling to below maximum texture size (if possible)
int postXformImageWidth = headerWidth;
int postXformImageHeight = headerHeight;
- success = TransformSize(requiredWidth, requiredHeight, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight);
+ success = TransformSize( requiredWidth, requiredHeight, input.scalingParameters.scalingMode, input.scalingParameters.samplingMode, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight );
if(success)
{
width = postXformImageWidth;