X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=platform-abstractions%2Ftizen%2Fimage-loaders%2Floader-jpeg-turbo.cpp;h=3141c91e57118cfb910034fa1394cb8047484b00;hb=f4b05fe51ddba1d7d2035103ef3cfcd1d4741d3c;hp=6066192cfcc7f9c28e482aa98cdcfad0f89b3e63;hpb=04514c0cd7a6ca0cfa711746862c6cb4d424a6b4;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/platform-abstractions/tizen/image-loaders/loader-jpeg-turbo.cpp b/platform-abstractions/tizen/image-loaders/loader-jpeg-turbo.cpp index 6066192..3141c91 100755 --- a/platform-abstractions/tizen/image-loaders/loader-jpeg-turbo.cpp +++ b/platform-abstractions/tizen/image-loaders/loader-jpeg-turbo.cpp @@ -15,13 +15,14 @@ * */ -// INTERNAL HEADERS + // CLASS HEADER #include "loader-jpeg.h" -#include -#include "platform-capabilities.h" -#include "image-operations.h" // EXTERNAL HEADERS +#include +#include +#include +#include #include #include #include @@ -30,160 +31,459 @@ #include #include -namespace Dali -{ -using Integration::Bitmap; +#include +#include +#include -namespace TizenPlatform -{ + +// INTERNAL HEADERS +#include "platform-capabilities.h" +#include "image-operations.h" +#include +#include namespace { - const unsigned DECODED_PIXEL_SIZE = 3; - const TJPF DECODED_PIXEL_LIBJPEG_TYPE = TJPF_RGB; +using Dali::Vector; +namespace Pixel = Dali::Pixel; +using PixelArray = unsigned char*; +const unsigned int DECODED_L8 = 1; +const unsigned int DECODED_RGB888 = 3; +const unsigned int DECODED_RGBA8888 = 4; + +/** Transformations that can be applied to decoded pixels to respect exif orientation + * codes in image headers */ +enum class JpegTransform +{ + NONE, //< no transformation 0th-Row = top & 0th-Column = left + FLIP_HORIZONTAL, //< horizontal flip 0th-Row = top & 0th-Column = right + FLIP_VERTICAL, //< vertical flip 0th-Row = bottom & 0th-Column = right + TRANSPOSE, //< transpose across UL-to-LR axis 0th-Row = bottom & 0th-Column = left + TRANSVERSE, //< transpose across UR-to-LL axis 0th-Row = left & 0th-Column = top + ROTATE_90, //< 90-degree clockwise rotation 0th-Row = right & 0th-Column = top + ROTATE_180, //< 180-degree rotation 0th-Row = right & 0th-Column = bottom + ROTATE_270, //< 270-degree clockwise (or 90 ccw) 0th-Row = left & 0th-Column = bottom +}; + +/** + * @brief Error handling bookeeping for the JPEG Turbo library's + * setjmp/longjmp simulated exceptions. + */ +struct JpegErrorState +{ + struct jpeg_error_mgr errorManager; + jmp_buf jumpBuffer; +}; + +/** + * @brief Called by the JPEG library when it hits an error. + * We jump out of the library so our loader code can return an error. + */ +void JpegErrorHandler ( j_common_ptr cinfo ) +{ + DALI_LOG_ERROR( "JpegErrorHandler(): libjpeg-turbo fatal error in JPEG decoding.\n" ); + /* cinfo->err really points to a JpegErrorState struct, so coerce pointer */ + JpegErrorState * myerr = reinterpret_cast( cinfo->err ); - /** Transformations that can be applied to decoded pixels to respect exif orientation - * codes in image headers */ - enum JPGFORM_CODE - { - JPGFORM_NONE = 1, /* no transformation 0th-Row = top & 0th-Column = left */ - JPGFORM_FLIP_H, /* horizontal flip 0th-Row = top & 0th-Column = right */ - JPGFORM_FLIP_V, /* vertical flip 0th-Row = bottom & 0th-Column = right*/ - JPGFORM_TRANSPOSE, /* transpose across UL-to-LR axis 0th-Row = bottom & 0th-Column = left*/ - JPGFORM_TRANSVERSE,/* transpose across UR-to-LL axis 0th-Row = left & 0th-Column = top*/ - JPGFORM_ROT_90, /* 90-degree clockwise rotation 0th-Row = right & 0th-Column = top*/ - JPGFORM_ROT_180, /* 180-degree rotation 0th-Row = right & 0th-Column = bottom*/ - JPGFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) 0th-Row = left & 0th-Column = bottom*/ - }; + /* Return control to the setjmp point */ + longjmp( myerr->jumpBuffer, 1 ); +} - struct RGB888Type +void JpegOutputMessageHandler( j_common_ptr cinfo ) +{ + /* Stop libjpeg from printing to stderr - Do Nothing */ +} + +/** + * LibJPEG Turbo tjDecompress2 API doesn't distinguish between errors that still allow + * the JPEG to be displayed and fatal errors. + */ +bool IsJpegErrorFatal( const std::string& errorMessage ) +{ + if( ( errorMessage.find("Corrupt JPEG data") != std::string::npos ) || + ( errorMessage.find("Invalid SOS parameters") != std::string::npos ) || + ( errorMessage.find("Invalid JPEG file structure") != std::string::npos ) || + ( errorMessage.find("Unsupported JPEG process") != std::string::npos ) || + ( errorMessage.find("Unsupported marker type") != std::string::npos ) || + ( errorMessage.find("Bogus marker length") != std::string::npos ) || + ( errorMessage.find("Bogus DQT index") != std::string::npos ) || + ( errorMessage.find("Bogus Huffman table definition") != std::string::npos )) { - char R; - char G; - char B; - }; + return false; + } + return true; +} - /** - * @brief Error handling bookeeping for the JPEG Turbo library's - * setjmp/longjmp simulated exceptions. - */ - struct JpegErrorState { - struct jpeg_error_mgr errorManager; - jmp_buf jumpBuffer; - }; +// helpers for safe exif memory handling +using ExifHandle = std::unique_ptr; - /** - * @brief Called by the JPEG library when it hits an error. - * We jump out of the library so our loader code can return an error. - */ - void JpegErrorHandler ( j_common_ptr cinfo ) - { - DALI_LOG_ERROR( "JpegErrorHandler(): libjpeg-turbo fatal error in JPEG decoding.\n" ); - /* cinfo->err really points to a JpegErrorState struct, so coerce pointer */ - JpegErrorState * myerr = reinterpret_cast( cinfo->err ); +ExifHandle MakeNullExifData() +{ + return ExifHandle{nullptr, exif_data_free}; +} - /* Return control to the setjmp point */ - longjmp( myerr->jumpBuffer, 1 ); - } +ExifHandle MakeExifDataFromData(unsigned char* data, unsigned int size) +{ + return ExifHandle{exif_data_new_from_data(data, size), exif_data_free}; +} + +// Helpers for safe Jpeg memory handling +using JpegHandle = std::unique_ptr; - void JpegOutputMessageHandler( j_common_ptr cinfo ) +JpegHandle MakeJpegCompressor() +{ + return JpegHandle{tjInitCompress(), tjDestroy}; +} + +JpegHandle MakeJpegDecompressor() +{ + return JpegHandle{tjInitDecompress(), tjDestroy}; +} + +using JpegMemoryHandle = std::unique_ptr; + +JpegMemoryHandle MakeJpegMemory() +{ + return JpegMemoryHandle{nullptr, tjFree}; +} + +template +class UniquePointerSetter final +{ +public: + UniquePointerSetter(std::unique_ptr& uniquePointer) + : mUniquePointer(uniquePointer), + mRawPointer(nullptr) + {} + + /// @brief Pointer to Pointer cast operator + operator T** () { return &mRawPointer; } + + /// @brief Destructor, reset the unique_ptr + ~UniquePointerSetter() { mUniquePointer.reset(mRawPointer); } + +private: + std::unique_ptr& mUniquePointer; + T* mRawPointer; +}; + +template +UniquePointerSetter SetPointer(std::unique_ptr& uniquePointer) +{ + return UniquePointerSetter{uniquePointer}; +} + +using TransformFunction = std::function; +using TransformFunctionArray = std::array; // 1, 3 and 4 bytes per pixel + +/// @brief Select the transform function depending on the pixel format +TransformFunction GetTransformFunction(const TransformFunctionArray& functions, + Pixel::Format pixelFormat) +{ + auto function = TransformFunction{}; + + int decodedPixelSize = Pixel::GetBytesPerPixel(pixelFormat); + switch( decodedPixelSize ) { - /* Stop libjpeg from printing to stderr - Do Nothing */ + case DECODED_L8: + { + function = functions[0]; + break; + } + case DECODED_RGB888: + { + function = functions[1]; + break; + } + case DECODED_RGBA8888: + { + function = functions[2]; + break; + } + default: + { + DALI_LOG_ERROR("Transform operation not supported on this Pixel::Format!"); + function = functions[1]; + break; + } } + return function; +} - /** - * LibJPEG Turbo tjDecompress2 API doesn't distinguish between errors that still allow - * the JPEG to be displayed and fatal errors. - */ - bool IsJpegErrorFatal( const std::string& errorMessage ) +// Storing Exif fields as properties +template +R ConvertExifNumeric( const ExifEntry& entry ) +{ + return static_cast((*reinterpret_cast(entry.data))); +} + +void AddExifFieldPropertyMap( Dali::Property::Map& out, const ExifEntry& entry, ExifIfd ifd ) +{ + auto shortName = std::string(exif_tag_get_name_in_ifd(entry.tag, ifd )); + switch( entry.format ) { - if( ( errorMessage.find("Corrupt JPEG data") != std::string::npos ) || - ( errorMessage.find("Invalid SOS parameters") != std::string::npos ) ) + case EXIF_FORMAT_ASCII: { - return false; + out.Insert( shortName, std::string(reinterpret_cast(entry.data)) ); + break; + } + case EXIF_FORMAT_SHORT: + { + out.Insert( shortName, ConvertExifNumeric(entry) ); + break; + } + case EXIF_FORMAT_LONG: + { + out.Insert( shortName, ConvertExifNumeric(entry) ); + break; + } + case EXIF_FORMAT_SSHORT: + { + out.Insert( shortName, ConvertExifNumeric(entry) ); + break; + } + case EXIF_FORMAT_SLONG: + { + out.Insert( shortName, ConvertExifNumeric(entry) ); + break; + } + case EXIF_FORMAT_FLOAT: + { + out.Insert (shortName, ConvertExifNumeric(entry) ); + break; + } + case EXIF_FORMAT_DOUBLE: + { + out.Insert( shortName, ConvertExifNumeric(entry) ); + break; + } + case EXIF_FORMAT_RATIONAL: + { + auto values = reinterpret_cast( entry.data ); + Dali::Property::Array array; + array.Add( static_cast(values[0]) ); + array.Add( static_cast(values[1]) ); + out.Insert(shortName, array); + break; + } + case EXIF_FORMAT_SBYTE: + { + out.Insert(shortName, "EXIF_FORMAT_SBYTE Unsupported"); + break; + } + case EXIF_FORMAT_BYTE: + { + out.Insert(shortName, "EXIF_FORMAT_BYTE Unsupported"); + break; + } + case EXIF_FORMAT_SRATIONAL: + { + auto values = reinterpret_cast( entry.data ); + Dali::Property::Array array; + array.Add(values[0]); + array.Add(values[1]); + out.Insert(shortName, array); + break; + } + case EXIF_FORMAT_UNDEFINED: + default: + { + std::stringstream ss; + ss << "EXIF_FORMAT_UNDEFINED, size: " << entry.size << ", components: " << entry.components; + out.Insert( shortName, ss.str()); } - return true; } +} +/// @brief Apply a transform to a buffer +bool Transform(const TransformFunctionArray& transformFunctions, + PixelArray buffer, + int width, + int height, + Pixel::Format pixelFormat ) +{ + auto transformFunction = GetTransformFunction(transformFunctions, pixelFormat); + if(transformFunction) + { + transformFunction(buffer, width, height); + } + return bool(transformFunction); +} + +/// @brief Auxiliar type to represent pixel data with different number of bytes +template +struct PixelType +{ + char _[N]; +}; + +template +void FlipVertical(PixelArray buffer, int width, int height) +{ + // Destination pixel, set as the first pixel of screen + auto to = reinterpret_cast*>( buffer ); + + // Source pixel, as the image is flipped horizontally and vertically, + // the source pixel is the end of the buffer of size width * height + auto from = reinterpret_cast*>(buffer) + width * height - 1; - /** Simple struct to ensure xif data is deleted. */ - struct ExifAutoPtr + for (auto ix = 0, endLoop = (width * height) / 2; ix < endLoop; ++ix, ++to, --from) { - ExifAutoPtr( ExifData* data) - :mData( data ) - {} + std::swap(*from, *to); + } +} - ~ExifAutoPtr() +template +void FlipHorizontal(PixelArray buffer, int width, int height) +{ + for(auto iy = 0; iy < height; ++iy) + { + //Set the destination pixel as the beginning of the row + auto to = reinterpret_cast*>(buffer) + width * iy; + //Set the source pixel as the end of the row to flip in X axis + auto from = reinterpret_cast*>(buffer) + width * (iy + 1) - 1; + for(auto ix = 0; ix < width / 2; ++ix, ++to, --from) { - exif_data_free( mData); + std::swap(*from, *to); } - ExifData *mData; - }; + } +} - /** simple class to enforce clean-up of JPEG structures. */ - struct AutoJpg +template +void Transpose(PixelArray buffer, int width, int height) +{ + //Transform vertically only + for(auto iy = 0; iy < height / 2; ++iy) { - AutoJpg(const tjhandle jpgHandle) - : mHnd(jpgHandle) + for(auto ix = 0; ix < width; ++ix) { + auto to = reinterpret_cast*>(buffer) + iy * width + ix; + auto from = reinterpret_cast*>(buffer) + (height - 1 - iy) * width + ix; + std::swap(*from, *to); } + } +} - ~AutoJpg() - { - // clean up JPG resources - tjDestroy( mHnd ); - } +template +void Transverse(PixelArray buffer, int width, int height) +{ + using PixelT = PixelType; + Vector data; + data.Resize( width * height ); + auto dataPtr = data.Begin(); + + auto original = reinterpret_cast(buffer); + std::copy(original, original + width * height, dataPtr); - tjhandle GetHandle() const + auto to = original; + for( auto iy = 0; iy < width; ++iy ) + { + for( auto ix = 0; ix < height; ++ix, ++to ) { - return mHnd ; + auto from = dataPtr + ix * width + iy; + *to = *from; } + } +} + - private: - AutoJpg( const AutoJpg& ); //< not defined - AutoJpg& operator= ( const AutoJpg& ); //< not defined +template +void Rotate90(PixelArray buffer, int width, int height) +{ + using PixelT = PixelType; + Vector data; + data.Resize(width * height); + auto dataPtr = data.Begin(); + + auto original = reinterpret_cast(buffer); + std::copy(original, original + width * height, dataPtr); + + std::swap(width, height); + auto hw = width * height; + hw = - hw - 1; - tjhandle mHnd; - }; // struct AutoJpg; + auto to = original + width - 1; + auto from = dataPtr; - /** RAII wrapper to free memory allocated by the jpeg-turbo library. */ - struct AutoJpgMem + for(auto ix = width; --ix >= 0;) { - AutoJpgMem(unsigned char * const tjMem) - : mTjMem(tjMem) + for(auto iy = height; --iy >= 0; ++from) { + *to = *from; + to += width; } + to += hw; + } +} - ~AutoJpgMem() - { - tjFree(mTjMem); - } +template +void Rotate180(PixelArray buffer, int width, int height) +{ + using PixelT = PixelType; + Vector data; + data.Resize(width * height); + auto dataPtr = data.Begin(); + + auto original = reinterpret_cast(buffer); + std::copy(original, original + width * height, dataPtr); - unsigned char * Get() const + auto to = original; + for( auto iy = 0; iy < width; iy++ ) + { + for( auto ix = 0; ix < height; ix++ ) { - return mTjMem; + auto from = dataPtr + (height - ix) * width - 1 - iy; + *to = *from; + ++to; } + } +} + + +template +void Rotate270(PixelArray buffer, int width, int height) +{ + using PixelT = PixelType; + Vector data; + data.Resize(width * height); + auto dataPtr = data.Begin(); - private: - AutoJpgMem( const AutoJpgMem& ); //< not defined - AutoJpgMem& operator= ( const AutoJpgMem& ); //< not defined + auto original = reinterpret_cast(buffer); + std::copy(original, original + width * height, dataPtr); - unsigned char * const mTjMem; - }; + auto w = height; + std::swap(width, height); + auto hw = width * height; - // Workaround to avoid exceeding the maximum texture size - const int MAX_TEXTURE_WIDTH = 4096; - const int MAX_TEXTURE_HEIGHT = 4096; + auto* to = original + hw - width; + auto* from = dataPtr; + + w = -w; + hw = hw + 1; + for(auto ix = width; --ix >= 0;) + { + for(auto iy = height; --iy >= 0;) + { + *to = *from; + ++from; + to += w; + } + to += hw; + } +} } // namespace -bool JpegRotate90 (unsigned char *buffer, int width, int height, int bpp); -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); +namespace Dali +{ + +namespace TizenPlatform +{ + +JpegTransform ConvertExifOrientation(ExifData* exifData); bool TransformSize( int requiredWidth, int requiredHeight, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, - JPGFORM_CODE transform, + JpegTransform transform, int& preXformImageWidth, int& preXformImageHeight, int& postXformImageWidth, int& postXformImageHeight ); @@ -205,7 +505,11 @@ bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height ) return false; } +// jpeg_create_decompress internally uses C casts +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" jpeg_create_decompress( &cinfo ); +#pragma GCC diagnostic pop jpeg_stdio_src( &cinfo, fp ); @@ -224,7 +528,7 @@ bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height ) return true; } -bool LoadBitmapFromJpeg( const ImageLoader::Input& input, Integration::Bitmap& bitmap ) +bool LoadBitmapFromJpeg( const ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap ) { const int flags= 0; FILE* const fp = input.file; @@ -277,33 +581,61 @@ bool LoadBitmapFromJpeg( const ImageLoader::Input& input, Integration::Bitmap& b DALI_LOG_ERROR("Error seeking to start of file\n"); } - AutoJpg autoJpg(tjInitDecompress()); + auto jpeg = MakeJpegDecompressor(); - if(autoJpg.GetHandle() == NULL) + if(!jpeg) { DALI_LOG_ERROR("%s\n", tjGetErrorStr()); return false; } - JPGFORM_CODE transform = JPGFORM_NONE; + auto transform = JpegTransform::NONE; + + // extract exif data + auto exifData = MakeExifDataFromData(jpegBufferPtr, jpegBufferSize); + + if( exifData && input.reorientationRequested ) + { + transform = ConvertExifOrientation(exifData.get()); + } + + std::unique_ptr exifMap; + exifMap.reset( new Property::Map() ); - if( input.reorientationRequested ) + for( auto k = 0u; k < EXIF_IFD_COUNT; ++k ) { - ExifAutoPtr exifData( exif_data_new_from_data(jpegBufferPtr, jpegBufferSize) ); - if( exifData.mData ) + auto content = exifData->ifd[k]; + for (auto i = 0u; i < content->count; ++i) { - transform = ConvertExifOrientation(exifData.mData); + auto &&tag = content->entries[i]; + const char *shortName = exif_tag_get_name_in_ifd(tag->tag, static_cast(k)); + if(shortName) + { + AddExifFieldPropertyMap(*exifMap, *tag, static_cast(k)); + } } } // Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array: int chrominanceSubsampling = -1; int preXformImageWidth = 0, preXformImageHeight = 0; - if( tjDecompressHeader2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling ) == -1 ) + + // In Ubuntu, the turbojpeg version is not correct. so build error occurs. + // Temporarily separate Ubuntu and other profiles. +#ifndef DALI_PROFILE_UBUNTU + int jpegColorspace = -1; + if( tjDecompressHeader3( jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling, &jpegColorspace ) == -1 ) { DALI_LOG_ERROR("%s\n", tjGetErrorStr()); // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type. } +#else + if( tjDecompressHeader2( jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling ) == -1 ) + { + DALI_LOG_ERROR("%s\n", tjGetErrorStr()); + // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type. + } +#endif if(preXformImageWidth == 0 || preXformImageHeight == 0) { @@ -333,22 +665,63 @@ bool LoadBitmapFromJpeg( const ImageLoader::Input& input, Integration::Bitmap& b scaledPreXformWidth, scaledPreXformHeight, scaledPostXformWidth, scaledPostXformHeight ); + + // Colorspace conversion options + TJPF pixelLibJpegType = TJPF_RGB; + Pixel::Format pixelFormat = Pixel::RGB888; +#ifndef DALI_PROFILE_UBUNTU + switch (jpegColorspace) + { + case TJCS_RGB: + // YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission. + // YCbCr images must be converted to RGB before they can actually be displayed. + case TJCS_YCbCr: + { + pixelLibJpegType = TJPF_RGB; + pixelFormat = Pixel::RGB888; + break; + } + case TJCS_GRAY: + { + pixelLibJpegType = TJPF_GRAY; + pixelFormat = Pixel::L8; + break; + } + case TJCS_CMYK: + case TJCS_YCCK: + { + pixelLibJpegType = TJPF_CMYK; + pixelFormat = Pixel::RGBA8888; + break; + } + default: + { + pixelLibJpegType = TJPF_RGB; + pixelFormat = Pixel::RGB888; + break; + } + } +#endif // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer: + bitmap = Dali::Devel::PixelBuffer::New(scaledPostXformWidth, scaledPostXformHeight, pixelFormat); - unsigned char * const bitmapPixelBuffer = bitmap.GetPackedPixelsProfile()->ReserveBuffer(Pixel::RGB888, scaledPostXformWidth, scaledPostXformHeight); + // set metadata + GetImplementation(bitmap).SetMetadata( std::move(exifMap) ); - if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, 0, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 ) + auto bitmapPixelBuffer = bitmap.GetBuffer(); + + if( tjDecompress2( jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast( bitmapPixelBuffer ), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags ) == -1 ) { std::string errorString = tjGetErrorStr(); if( IsJpegErrorFatal( errorString ) ) { - DALI_LOG_ERROR("%s\n", errorString.c_str()); - return false; + DALI_LOG_ERROR("%s\n", errorString.c_str()); + return false; } else { - DALI_LOG_WARNING("%s\n", errorString.c_str()); + DALI_LOG_WARNING("%s\n", errorString.c_str()); } } @@ -358,162 +731,91 @@ bool LoadBitmapFromJpeg( const ImageLoader::Input& input, Integration::Bitmap& b bool result = false; switch(transform) { - case JPGFORM_NONE: + case JpegTransform::NONE: { result = true; break; } // 3 orientation changes for a camera held perpendicular to the ground or upside-down: - case JPGFORM_ROT_180: + case JpegTransform::ROTATE_180: { - result = JpegRotate180(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE); + static auto rotate180Functions = TransformFunctionArray { + &Rotate180<1>, + &Rotate180<3>, + &Rotate180<4>, + }; + result = Transform(rotate180Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); break; } - case JPGFORM_ROT_270: + case JpegTransform::ROTATE_270: { - result = JpegRotate270(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE); + static auto rotate270Functions = TransformFunctionArray { + &Rotate270<1>, + &Rotate270<3>, + &Rotate270<4>, + }; + result = Transform(rotate270Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); break; } - case JPGFORM_ROT_90: + case JpegTransform::ROTATE_90: { - result = JpegRotate90(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE); + static auto rotate90Functions = TransformFunctionArray { + &Rotate90<1>, + &Rotate90<3>, + &Rotate90<4>, + }; + result = Transform(rotate90Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); 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: + case JpegTransform::FLIP_VERTICAL: { - DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform ); + static auto flipVerticalFunctions = TransformFunctionArray { + &FlipVertical<1>, + &FlipVertical<3>, + &FlipVertical<4>, + }; + result = Transform(flipVerticalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); break; } - } - return result; -} - -///@Todo: Move all these rotation functions to portable/image-operations and take "Jpeg" out of their names. -bool JpegRotate90(unsigned char *buffer, int width, int height, int bpp) -{ - int w, iw, ih, hw = 0; - int ix, iy = 0; - iw = width; - ih = height; - Vector data; - data.Resize(width * height * bpp); - unsigned char *dataPtr = data.Begin(); - memcpy(dataPtr, buffer, width * height * bpp); - w = ih; - ih = iw; - iw = w; - hw = iw * ih; - hw = - hw - 1; - switch(bpp) - { - case 3: + // Less-common orientation changes, since they don't correspond to a camera's physical orientation: + case JpegTransform::FLIP_HORIZONTAL: { - RGB888Type* to = reinterpret_cast(buffer) + iw - 1; - RGB888Type* from = reinterpret_cast( dataPtr ); - - for(ix = iw; -- ix >= 0;) - { - for(iy = ih; -- iy >= 0; ++from ) - { - *to = *from; - to += iw; - } - to += hw; - } + static auto flipHorizontalFunctions = TransformFunctionArray { + &FlipHorizontal<1>, + &FlipHorizontal<3>, + &FlipHorizontal<4>, + }; + result = Transform(flipHorizontalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); break; } - - default: + case JpegTransform::TRANSPOSE: { - return false; - } - } - - return true; -} - -bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp) -{ - int ix, iw, ih, hw = 0; - iw = width; - ih = height; - hw = iw * ih; - ix = hw; - - switch(bpp) - { - case 3: - { - RGB888Type tmp; - RGB888Type* to = reinterpret_cast(buffer) ; - RGB888Type* from = reinterpret_cast( buffer ) + hw - 1; - for(; --ix >= (hw / 2); ++to, --from) - { - tmp = *to; - *to = *from; - *from = tmp; - } + static auto transposeFunctions = TransformFunctionArray { + &Transpose<1>, + &Transpose<3>, + &Transpose<4>, + }; + result = Transform(transposeFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); break; } - - default: + case JpegTransform::TRANSVERSE: { - return false; - } - } - - return true; -} - -bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp) -{ - int w, iw, ih, hw = 0; - int ix, iy = 0; - - iw = width; - ih = height; - Vector data; - data.Resize(width * height * bpp); - unsigned char *dataPtr = data.Begin(); - memcpy(dataPtr, buffer, width * height * bpp); - w = ih; - ih = iw; - iw = w; - hw = iw * ih; - - switch(bpp) - { - case 3: - { - RGB888Type* to = reinterpret_cast(buffer) + hw - iw; - RGB888Type* from = reinterpret_cast( dataPtr ); - - w = -w; - hw = hw + 1; - for(ix = iw; -- ix >= 0;) - { - for(iy = ih; -- iy >= 0;) - { - *to = *from; - from += 1; - to += w; - } - to += hw; - } + static auto transverseFunctions = TransformFunctionArray { + &Transverse<1>, + &Transverse<3>, + &Transverse<4>, + }; + result = Transform(transverseFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat ); break; } default: { - return false; + DALI_LOG_ERROR( "Unsupported JPEG Orientation transformation: %x.\n", transform ); + break; } } - return true; + return result; } bool EncodeToJpeg( const unsigned char* const pixelBuffer, Vector< unsigned char >& encodedPixels, @@ -568,39 +870,44 @@ bool EncodeToJpeg( const unsigned char* const pixelBuffer, Vector< unsigned char } // Initialise a JPEG codec: - AutoJpg autoJpg( tjInitCompress() ); { - if( autoJpg.GetHandle() == NULL ) + auto jpeg = MakeJpegCompressor(); + if( jpeg ) { DALI_LOG_ERROR( "JPEG Compressor init failed: %s\n", tjGetErrorStr() ); return false; } + + // Safely wrap the jpeg codec's buffer in case we are about to throw, then + // save the pixels to a persistent buffer that we own and let our cleaner + // class clean up the buffer as it goes out of scope: + auto dstBuffer = MakeJpegMemory(); + // Run the compressor: - unsigned char* dstBuffer = NULL; unsigned long dstBufferSize = 0; const int flags = 0; - if( tjCompress2( autoJpg.GetHandle(), const_cast(pixelBuffer), width, 0, height, jpegPixelFormat, &dstBuffer, &dstBufferSize, TJSAMP_444, quality, flags ) ) + if( tjCompress2( jpeg.get(), + const_cast(pixelBuffer), + width, 0, height, + jpegPixelFormat, SetPointer(dstBuffer), &dstBufferSize, + TJSAMP_444, quality, flags ) ) { DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr()); return false; } - // Safely wrap the jpeg codec's buffer in case we are about to throw, then - // save the pixels to a persistent buffer that we own and let our cleaner - // class clean up the buffer as it goes out of scope: - AutoJpgMem cleaner( dstBuffer ); encodedPixels.Resize( dstBufferSize ); - memcpy( encodedPixels.Begin(), dstBuffer, dstBufferSize ); + memcpy( encodedPixels.Begin(), dstBuffer.get(), dstBufferSize ); } return true; } -JPGFORM_CODE ConvertExifOrientation(ExifData* exifData) +JpegTransform ConvertExifOrientation(ExifData* exifData) { - JPGFORM_CODE transform = JPGFORM_NONE; + auto transform = JpegTransform::NONE; ExifEntry * const entry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION); int orientation = 0; if( entry ) @@ -610,42 +917,42 @@ JPGFORM_CODE ConvertExifOrientation(ExifData* exifData) { case 1: { - transform = JPGFORM_NONE; + transform = JpegTransform::NONE; break; } case 2: { - transform = JPGFORM_FLIP_H; + transform = JpegTransform::FLIP_HORIZONTAL; break; } case 3: { - transform = JPGFORM_FLIP_V; + transform = JpegTransform::FLIP_VERTICAL; break; } case 4: { - transform = JPGFORM_TRANSPOSE; + transform = JpegTransform::TRANSPOSE; break; } case 5: { - transform = JPGFORM_TRANSVERSE; + transform = JpegTransform::TRANSVERSE; break; } case 6: { - transform = JPGFORM_ROT_90; + transform = JpegTransform::ROTATE_90; break; } case 7: { - transform = JPGFORM_ROT_180; + transform = JpegTransform::ROTATE_180; break; } case 8: { - transform = JPGFORM_ROT_270; + transform = JpegTransform::ROTATE_270; break; } default: @@ -661,13 +968,13 @@ JPGFORM_CODE ConvertExifOrientation(ExifData* exifData) bool TransformSize( int requiredWidth, int requiredHeight, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, - JPGFORM_CODE transform, + JpegTransform transform, int& preXformImageWidth, int& preXformImageHeight, int& postXformImageWidth, int& postXformImageHeight ) { bool success = true; - if( transform == JPGFORM_ROT_90 || transform == JPGFORM_ROT_270 ) + if( transform == JpegTransform::ROTATE_90 || transform == JpegTransform::ROTATE_270 || transform == JpegTransform::ROTATE_180 || transform == JpegTransform::TRANSVERSE) { std::swap( requiredWidth, requiredHeight ); std::swap( postXformImageWidth, postXformImageHeight ); @@ -753,8 +1060,8 @@ bool TransformSize( int requiredWidth, int requiredHeight, // Continue downscaling to below maximum texture size (if possible) scaleFactorIndex = i; - if( TJSCALED(postXformImageWidth, (factors[i])) < MAX_TEXTURE_WIDTH && - TJSCALED(postXformImageHeight, (factors[i])) < MAX_TEXTURE_HEIGHT ) + if( TJSCALED(postXformImageWidth, (factors[i])) < static_cast< int >( Dali::GetMaxTextureSize() ) && + TJSCALED(postXformImageHeight, (factors[i])) < static_cast< int >( Dali::GetMaxTextureSize() ) ) { // Current scale-factor downscales to below maximum texture size break; @@ -774,10 +1081,9 @@ bool TransformSize( int requiredWidth, int requiredHeight, return success; } -ExifData* LoadExifData( FILE* fp ) +ExifHandle LoadExifData( FILE* fp ) { - ExifData* exifData=NULL; - ExifLoader* exifLoader; + auto exifData = MakeNullExifData(); unsigned char dataBuffer[1024]; if( fseek( fp, 0, SEEK_SET ) ) @@ -786,7 +1092,8 @@ ExifData* LoadExifData( FILE* fp ) } else { - exifLoader = exif_loader_new (); + auto exifLoader = std::unique_ptr{ + exif_loader_new(), exif_loader_unref }; while( !feof(fp) ) { @@ -795,14 +1102,13 @@ ExifData* LoadExifData( FILE* fp ) { break; } - if( ! exif_loader_write( exifLoader, dataBuffer, size ) ) + if( ! exif_loader_write( exifLoader.get(), dataBuffer, size ) ) { break; } } - exifData = exif_loader_get_data( exifLoader ); - exif_loader_unref( exifLoader ); + exifData.reset( exif_loader_get_data( exifLoader.get() ) ); } return exifData; @@ -826,14 +1132,14 @@ bool LoadJpegHeader( const ImageLoader::Input& input, unsigned int& width, unsig unsigned int headerHeight; if( LoadJpegHeader( fp, headerWidth, headerHeight ) ) { - JPGFORM_CODE transform = JPGFORM_NONE; + auto transform = JpegTransform::NONE; if( input.reorientationRequested ) { - ExifAutoPtr exifData( LoadExifData( fp ) ); - if( exifData.mData ) + auto exifData = LoadExifData( fp ); + if( exifData ) { - transform = ConvertExifOrientation(exifData.mData); + transform = ConvertExifOrientation(exifData.get()); } int preXformImageWidth = headerWidth;