Merge "Fix errors caused by -Wold-style-cast" into devel/master
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / tizen / image-loaders / loader-jpeg-turbo.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // INTERNAL HEADERS
19 #include "loader-jpeg.h"
20 #include <dali/integration-api/bitmap.h>
21 #include "platform-capabilities.h"
22 #include "image-operations.h"
23
24 // EXTERNAL HEADERS
25 #include <libexif/exif-data.h>
26 #include <libexif/exif-loader.h>
27 #include <libexif/exif-tag.h>
28 #include <turbojpeg.h>
29 #include <jpeglib.h>
30 #include <cstring>
31 #include <setjmp.h>
32
33 namespace Dali
34 {
35 using Integration::Bitmap;
36
37 namespace TizenPlatform
38 {
39
40 namespace
41 {
42   const unsigned DECODED_PIXEL_SIZE = 3;
43   const TJPF DECODED_PIXEL_LIBJPEG_TYPE = TJPF_RGB;
44
45   /** Transformations that can be applied to decoded pixels to respect exif orientation
46    *  codes in image headers */
47   enum JPGFORM_CODE
48   {
49     JPGFORM_NONE = 1, /* no transformation 0th-Row = top & 0th-Column = left */
50     JPGFORM_FLIP_H,   /* horizontal flip 0th-Row = top & 0th-Column = right */
51     JPGFORM_FLIP_V,   /* vertical flip   0th-Row = bottom & 0th-Column = right*/
52     JPGFORM_TRANSPOSE, /* transpose across UL-to-LR axis  0th-Row = bottom & 0th-Column = left*/
53     JPGFORM_TRANSVERSE,/* transpose across UR-to-LL axis  0th-Row = left   & 0th-Column = top*/
54     JPGFORM_ROT_90,    /* 90-degree clockwise rotation  0th-Row = right  & 0th-Column = top*/
55     JPGFORM_ROT_180,   /* 180-degree rotation  0th-Row = right  & 0th-Column = bottom*/
56     JPGFORM_ROT_270    /* 270-degree clockwise (or 90 ccw) 0th-Row = left  & 0th-Column = bottom*/
57   };
58
59   struct RGB888Type
60   {
61      char R;
62      char G;
63      char B;
64   };
65
66   /**
67    * @brief Error handling bookeeping for the JPEG Turbo library's
68    * setjmp/longjmp simulated exceptions.
69    */
70   struct JpegErrorState {
71     struct jpeg_error_mgr errorManager;
72     jmp_buf jumpBuffer;
73   };
74
75   /**
76    * @brief Called by the JPEG library when it hits an error.
77    * We jump out of the library so our loader code can return an error.
78    */
79   void  JpegErrorHandler ( j_common_ptr cinfo )
80   {
81     DALI_LOG_ERROR( "JpegErrorHandler(): libjpeg-turbo fatal error in JPEG decoding.\n" );
82     /* cinfo->err really points to a JpegErrorState struct, so coerce pointer */
83     JpegErrorState * myerr = reinterpret_cast<JpegErrorState *>( cinfo->err );
84
85     /* Return control to the setjmp point */
86     longjmp( myerr->jumpBuffer, 1 );
87   }
88
89   void JpegOutputMessageHandler( j_common_ptr cinfo )
90   {
91     /* Stop libjpeg from printing to stderr - Do Nothing */
92   }
93
94   /**
95    * LibJPEG Turbo tjDecompress2 API doesn't distinguish between errors that still allow
96    * the JPEG to be displayed and fatal errors.
97    */
98   bool IsJpegErrorFatal( const std::string& errorMessage )
99   {
100     if( ( errorMessage.find("Corrupt JPEG data") != std::string::npos ) ||
101         ( errorMessage.find("Invalid SOS parameters") != std::string::npos ) )
102     {
103       return false;
104     }
105     return true;
106   }
107
108
109   /** Simple struct to ensure xif data is deleted. */
110   struct ExifAutoPtr
111   {
112     ExifAutoPtr( ExifData* data)
113     :mData( data )
114     {}
115
116     ~ExifAutoPtr()
117     {
118       exif_data_free( mData);
119     }
120     ExifData *mData;
121   };
122
123   /** simple class to enforce clean-up of JPEG structures. */
124   struct AutoJpg
125   {
126     AutoJpg(const tjhandle jpgHandle)
127     : mHnd(jpgHandle)
128     {
129     }
130
131     ~AutoJpg()
132     {
133       // clean up JPG resources
134       tjDestroy( mHnd );
135     }
136
137     tjhandle GetHandle() const
138     {
139       return mHnd ;
140     }
141
142   private:
143     AutoJpg( const AutoJpg& ); //< not defined
144     AutoJpg& operator= ( const AutoJpg& ); //< not defined
145
146     tjhandle mHnd;
147   }; // struct AutoJpg;
148
149   /** RAII wrapper to free memory allocated by the jpeg-turbo library. */
150   struct AutoJpgMem
151   {
152     AutoJpgMem(unsigned char * const tjMem)
153     : mTjMem(tjMem)
154     {
155     }
156
157     ~AutoJpgMem()
158     {
159       tjFree(mTjMem);
160     }
161
162     unsigned char * Get() const
163     {
164       return mTjMem;
165     }
166
167   private:
168     AutoJpgMem( const AutoJpgMem& ); //< not defined
169     AutoJpgMem& operator= ( const AutoJpgMem& ); //< not defined
170
171     unsigned char * const mTjMem;
172   };
173
174   // Workaround to avoid exceeding the maximum texture size
175   const int MAX_TEXTURE_WIDTH  = 4096;
176   const int MAX_TEXTURE_HEIGHT = 4096;
177
178 } // namespace
179
180 bool JpegRotate90 (unsigned char *buffer, int width, int height, int bpp);
181 bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp);
182 bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp);
183 JPGFORM_CODE ConvertExifOrientation(ExifData* exifData);
184 bool TransformSize( int requiredWidth, int requiredHeight,
185                     FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
186                     JPGFORM_CODE transform,
187                     int& preXformImageWidth, int& preXformImageHeight,
188                     int& postXformImageWidth, int& postXformImageHeight );
189
190 bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height )
191 {
192   // using libjpeg API to avoid having to read the whole file in a buffer
193   struct jpeg_decompress_struct cinfo;
194   struct JpegErrorState jerr;
195   cinfo.err = jpeg_std_error( &jerr.errorManager );
196
197   jerr.errorManager.output_message = JpegOutputMessageHandler;
198   jerr.errorManager.error_exit = JpegErrorHandler;
199
200   // On error exit from the JPEG lib, control will pass via JpegErrorHandler
201   // into this branch body for cleanup and error return:
202   if(setjmp(jerr.jumpBuffer))
203   {
204     jpeg_destroy_decompress(&cinfo);
205     return false;
206   }
207
208 // jpeg_create_decompress internally uses C casts
209 #pragma GCC diagnostic push
210 #pragma GCC diagnostic ignored "-Wold-style-cast"
211   jpeg_create_decompress( &cinfo );
212 #pragma GCC diagnostic pop
213
214   jpeg_stdio_src( &cinfo, fp );
215
216   // Check header to see if it is  JPEG file
217   if( jpeg_read_header( &cinfo, TRUE ) != JPEG_HEADER_OK )
218   {
219     width = height = 0;
220     jpeg_destroy_decompress( &cinfo );
221     return false;
222   }
223
224   width = cinfo.image_width;
225   height = cinfo.image_height;
226
227   jpeg_destroy_decompress( &cinfo );
228   return true;
229 }
230
231 bool LoadBitmapFromJpeg( const ImageLoader::Input& input, Integration::Bitmap& bitmap )
232 {
233   const int flags= 0;
234   FILE* const fp = input.file;
235
236   if( fseek(fp,0,SEEK_END) )
237   {
238     DALI_LOG_ERROR("Error seeking to end of file\n");
239     return false;
240   }
241
242   long positionIndicator = ftell(fp);
243   unsigned int jpegBufferSize = 0u;
244   if( positionIndicator > -1L )
245   {
246     jpegBufferSize = static_cast<unsigned int>(positionIndicator);
247   }
248
249   if( 0u == jpegBufferSize )
250   {
251     return false;
252   }
253
254   if( fseek(fp, 0, SEEK_SET) )
255   {
256     DALI_LOG_ERROR("Error seeking to start of file\n");
257     return false;
258   }
259
260   Vector<unsigned char> jpegBuffer;
261   try
262   {
263     jpegBuffer.Resize( jpegBufferSize );
264   }
265   catch(...)
266   {
267     DALI_LOG_ERROR( "Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U );
268     return false;
269   }
270   unsigned char * const jpegBufferPtr = jpegBuffer.Begin();
271
272   // Pull the compressed JPEG image bytes out of a file and into memory:
273   if( fread( jpegBufferPtr, 1, jpegBufferSize, fp ) != jpegBufferSize )
274   {
275     DALI_LOG_WARNING("Error on image file read.\n");
276     return false;
277   }
278
279   if( fseek(fp, 0, SEEK_SET) )
280   {
281     DALI_LOG_ERROR("Error seeking to start of file\n");
282   }
283
284   AutoJpg autoJpg(tjInitDecompress());
285
286   if(autoJpg.GetHandle() == NULL)
287   {
288     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
289     return false;
290   }
291
292   JPGFORM_CODE transform = JPGFORM_NONE;
293
294   if( input.reorientationRequested )
295   {
296     ExifAutoPtr exifData( exif_data_new_from_data(jpegBufferPtr, jpegBufferSize) );
297     if( exifData.mData )
298     {
299       transform = ConvertExifOrientation(exifData.mData);
300     }
301   }
302
303   // Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array:
304   int chrominanceSubsampling = -1;
305   int preXformImageWidth = 0, preXformImageHeight = 0;
306   if( tjDecompressHeader2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling ) == -1 )
307   {
308     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
309     // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
310   }
311
312   if(preXformImageWidth == 0 || preXformImageHeight == 0)
313   {
314     DALI_LOG_WARNING("Invalid Image!\n");
315     return false;
316   }
317
318   int requiredWidth  = input.scalingParameters.dimensions.GetWidth();
319   int requiredHeight = input.scalingParameters.dimensions.GetHeight();
320
321   // If transform is a 90 or 270 degree rotation, the logical width and height
322   // request from the client needs to be adjusted to account by effectively
323   // rotating that too, and the final width and height need to be swapped:
324   int postXformImageWidth = preXformImageWidth;
325   int postXformImageHeight = preXformImageHeight;
326
327
328   int scaledPreXformWidth   = preXformImageWidth;
329   int scaledPreXformHeight  = preXformImageHeight;
330   int scaledPostXformWidth  = postXformImageWidth;
331   int scaledPostXformHeight = postXformImageHeight;
332
333   TransformSize( requiredWidth, requiredHeight,
334                  input.scalingParameters.scalingMode,
335                  input.scalingParameters.samplingMode,
336                  transform,
337                  scaledPreXformWidth, scaledPreXformHeight,
338                  scaledPostXformWidth, scaledPostXformHeight );
339
340   // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
341
342   unsigned char * const bitmapPixelBuffer =  bitmap.GetPackedPixelsProfile()->ReserveBuffer(Pixel::RGB888, scaledPostXformWidth, scaledPostXformHeight);
343
344   if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, 0, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 )
345   {
346     std::string errorString = tjGetErrorStr();
347
348     if( IsJpegErrorFatal( errorString ) )
349     {
350         DALI_LOG_ERROR("%s\n", errorString.c_str());
351         return false;
352     }
353     else
354     {
355         DALI_LOG_WARNING("%s\n", errorString.c_str());
356     }
357   }
358
359   const unsigned int  bufferWidth  = GetTextureDimension( scaledPreXformWidth );
360   const unsigned int  bufferHeight = GetTextureDimension( scaledPreXformHeight );
361
362   bool result = false;
363   switch(transform)
364   {
365     case JPGFORM_NONE:
366     {
367       result = true;
368       break;
369     }
370     // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
371     case JPGFORM_ROT_180:
372     {
373       result = JpegRotate180(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
374       break;
375     }
376     case JPGFORM_ROT_270:
377     {
378       result = JpegRotate270(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
379       break;
380     }
381     case JPGFORM_ROT_90:
382     {
383       result = JpegRotate90(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
384       break;
385     }
386     /// Less-common orientation changes, since they don't correspond to a camera's
387     // physical orientation:
388     case JPGFORM_FLIP_H:
389     case JPGFORM_FLIP_V:
390     case JPGFORM_TRANSPOSE:
391     case JPGFORM_TRANSVERSE:
392     {
393       DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform );
394       break;
395     }
396   }
397   return result;
398 }
399
400 ///@Todo: Move all these rotation functions to portable/image-operations and take "Jpeg" out of their names.
401 bool JpegRotate90(unsigned char *buffer, int width, int height, int bpp)
402 {
403   int  w, iw, ih, hw = 0;
404   int ix, iy = 0;
405   iw = width;
406   ih = height;
407   Vector<unsigned char> data;
408   data.Resize(width * height * bpp);
409   unsigned char *dataPtr = data.Begin();
410   memcpy(dataPtr, buffer, width * height * bpp);
411   w = ih;
412   ih = iw;
413   iw = w;
414   hw = iw * ih;
415   hw = - hw - 1;
416   switch(bpp)
417   {
418     case 3:
419     {
420       RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + iw - 1;
421       RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
422
423       for(ix = iw; -- ix >= 0;)
424       {
425         for(iy = ih; -- iy >= 0; ++from )
426         {
427           *to = *from;
428           to += iw;
429         }
430         to += hw;
431       }
432       break;
433     }
434
435     default:
436     {
437       return false;
438     }
439   }
440
441   return true;
442 }
443
444 bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp)
445 {
446   int  ix, iw, ih, hw = 0;
447   iw = width;
448   ih = height;
449   hw = iw * ih;
450   ix = hw;
451
452   switch(bpp)
453   {
454     case 3:
455     {
456       RGB888Type tmp;
457       RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) ;
458       RGB888Type* from = reinterpret_cast<RGB888Type*>( buffer ) + hw - 1;
459       for(; --ix >= (hw / 2); ++to, --from)
460       {
461         tmp = *to;
462         *to = *from;
463         *from = tmp;
464       }
465       break;
466     }
467
468     default:
469     {
470       return false;
471     }
472   }
473
474   return true;
475 }
476
477 bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp)
478 {
479   int  w, iw, ih, hw = 0;
480   int ix, iy = 0;
481
482   iw = width;
483   ih = height;
484   Vector<unsigned char> data;
485   data.Resize(width * height * bpp);
486   unsigned char *dataPtr = data.Begin();
487   memcpy(dataPtr, buffer, width * height * bpp);
488   w = ih;
489   ih = iw;
490   iw = w;
491   hw = iw * ih;
492
493   switch(bpp)
494   {
495     case 3:
496     {
497       RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + hw  - iw;
498       RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
499
500       w = -w;
501       hw =  hw + 1;
502       for(ix = iw; -- ix >= 0;)
503       {
504         for(iy = ih; -- iy >= 0;)
505         {
506           *to = *from;
507           from += 1;
508           to += w;
509         }
510         to += hw;
511       }
512       break;
513     }
514     default:
515     {
516       return false;
517     }
518   }
519
520   return true;
521 }
522
523 bool EncodeToJpeg( const unsigned char* const pixelBuffer, Vector< unsigned char >& encodedPixels,
524                    const std::size_t width, const std::size_t height, const Pixel::Format pixelFormat, unsigned quality )
525 {
526
527   if( !pixelBuffer )
528   {
529     DALI_LOG_ERROR("Null input buffer\n");
530     return false;
531   }
532
533   // Translate pixel format enum:
534   int jpegPixelFormat = -1;
535
536   switch( pixelFormat )
537   {
538     case Pixel::RGB888:
539     {
540       jpegPixelFormat = TJPF_RGB;
541       break;
542     }
543     case Pixel::RGBA8888:
544     {
545       // Ignore the alpha:
546       jpegPixelFormat = TJPF_RGBX;
547       break;
548     }
549     case Pixel::BGRA8888:
550     {
551       // Ignore the alpha:
552       jpegPixelFormat = TJPF_BGRX;
553       break;
554     }
555     default:
556     {
557       DALI_LOG_ERROR( "Unsupported pixel format for encoding to JPEG.\n" );
558       return false;
559     }
560   }
561
562   // Assert quality is in the documented allowable range of the jpeg-turbo lib:
563   DALI_ASSERT_DEBUG( quality >= 1 );
564   DALI_ASSERT_DEBUG( quality <= 100 );
565   if( quality < 1 )
566   {
567     quality = 1;
568   }
569   if( quality > 100 )
570   {
571     quality = 100;
572   }
573
574   // Initialise a JPEG codec:
575   AutoJpg autoJpg( tjInitCompress() );
576   {
577     if( autoJpg.GetHandle() == NULL )
578     {
579       DALI_LOG_ERROR( "JPEG Compressor init failed: %s\n", tjGetErrorStr() );
580       return false;
581     }
582
583     // Run the compressor:
584     unsigned char* dstBuffer = NULL;
585     unsigned long dstBufferSize = 0;
586     const int flags = 0;
587
588     if( tjCompress2( autoJpg.GetHandle(), const_cast<unsigned char*>(pixelBuffer), width, 0, height, jpegPixelFormat, &dstBuffer, &dstBufferSize, TJSAMP_444, quality, flags ) )
589     {
590       DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr());
591       return false;
592     }
593
594     // Safely wrap the jpeg codec's buffer in case we are about to throw, then
595     // save the pixels to a persistent buffer that we own and let our cleaner
596     // class clean up the buffer as it goes out of scope:
597     AutoJpgMem cleaner( dstBuffer );
598     encodedPixels.Resize( dstBufferSize );
599     memcpy( encodedPixels.Begin(), dstBuffer, dstBufferSize );
600   }
601   return true;
602 }
603
604
605 JPGFORM_CODE ConvertExifOrientation(ExifData* exifData)
606 {
607   JPGFORM_CODE transform = JPGFORM_NONE;
608   ExifEntry * const entry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
609   int orientation = 0;
610   if( entry )
611   {
612     orientation = exif_get_short(entry->data, exif_data_get_byte_order(entry->parent->parent));
613     switch( orientation )
614     {
615       case 1:
616       {
617         transform = JPGFORM_NONE;
618         break;
619       }
620       case 2:
621       {
622         transform = JPGFORM_FLIP_H;
623         break;
624       }
625       case 3:
626       {
627         transform = JPGFORM_FLIP_V;
628         break;
629       }
630       case 4:
631       {
632         transform = JPGFORM_TRANSPOSE;
633         break;
634       }
635       case 5:
636       {
637         transform = JPGFORM_TRANSVERSE;
638         break;
639       }
640       case 6:
641       {
642         transform = JPGFORM_ROT_90;
643         break;
644       }
645       case 7:
646       {
647         transform = JPGFORM_ROT_180;
648         break;
649       }
650       case 8:
651       {
652         transform = JPGFORM_ROT_270;
653         break;
654       }
655       default:
656       {
657         // Try to keep loading the file, but let app developer know there was something fishy:
658         DALI_LOG_WARNING( "Incorrect/Unknown Orientation setting found in EXIF header of JPEG image (%x). Orientation setting will be ignored.\n", entry );
659         break;
660       }
661     }
662   }
663   return transform;
664 }
665
666 bool TransformSize( int requiredWidth, int requiredHeight,
667                     FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
668                     JPGFORM_CODE transform,
669                     int& preXformImageWidth, int& preXformImageHeight,
670                     int& postXformImageWidth, int& postXformImageHeight )
671 {
672   bool success = true;
673
674   if( transform == JPGFORM_ROT_90 || transform == JPGFORM_ROT_270 )
675   {
676     std::swap( requiredWidth, requiredHeight );
677     std::swap( postXformImageWidth, postXformImageHeight );
678   }
679
680   // Apply the special rules for when there are one or two zeros in requested dimensions:
681   const ImageDimensions correctedDesired = Internal::Platform::CalculateDesiredDimensions( ImageDimensions( postXformImageWidth, postXformImageHeight), ImageDimensions( requiredWidth, requiredHeight ) );
682   requiredWidth = correctedDesired.GetWidth();
683   requiredHeight = correctedDesired.GetHeight();
684
685   // Rescale image during decode using one of the decoder's built-in rescaling
686   // ratios (expected to be powers of 2), keeping the final image at least as
687   // wide and high as was requested:
688
689   int numFactors = 0;
690   tjscalingfactor* factors = tjGetScalingFactors( &numFactors );
691   if( factors == NULL )
692   {
693     DALI_LOG_WARNING("TurboJpeg tjGetScalingFactors error!\n");
694     success = false;
695   }
696   else
697   {
698     // Internal jpeg downscaling is the same as our BOX_X sampling modes so only
699     // apply it if the application requested one of those:
700     // (use a switch case here so this code will fail to compile if other modes are added)
701     bool downscale = true;
702     switch( samplingMode )
703     {
704       case SamplingMode::BOX:
705       case SamplingMode::BOX_THEN_NEAREST:
706       case SamplingMode::BOX_THEN_LINEAR:
707       case SamplingMode::DONT_CARE:
708       {
709         downscale = true;
710         break;
711       }
712       case SamplingMode::NO_FILTER:
713       case SamplingMode::NEAREST:
714       case SamplingMode::LINEAR:
715       {
716         downscale = false;
717         break;
718       }
719     }
720
721     int scaleFactorIndex( 0 );
722     if( downscale )
723     {
724       // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
725       for( int i = 1; i < numFactors; ++i )
726       {
727         bool widthLessRequired  = TJSCALED( postXformImageWidth,  factors[i]) < requiredWidth;
728         bool heightLessRequired = TJSCALED( postXformImageHeight, factors[i]) < requiredHeight;
729         // If either scaled dimension is smaller than the desired one, we were done at the last iteration
730         if ( (fittingMode == FittingMode::SCALE_TO_FILL) && (widthLessRequired || heightLessRequired) )
731         {
732           break;
733         }
734         // If both dimensions are smaller than the desired one, we were done at the last iteration:
735         if ( (fittingMode == FittingMode::SHRINK_TO_FIT) && ( widthLessRequired && heightLessRequired ) )
736         {
737           break;
738         }
739         // If the width is smaller than the desired one, we were done at the last iteration:
740         if ( fittingMode == FittingMode::FIT_WIDTH && widthLessRequired )
741         {
742           break;
743         }
744         // If the width is smaller than the desired one, we were done at the last iteration:
745         if ( fittingMode == FittingMode::FIT_HEIGHT && heightLessRequired )
746         {
747           break;
748         }
749         // This factor stays is within our fitting mode constraint so remember it:
750         scaleFactorIndex = i;
751       }
752     }
753
754     // Regardless of requested size, downscale to avoid exceeding the maximum texture size:
755     for( int i = scaleFactorIndex; i < numFactors; ++i )
756     {
757       // Continue downscaling to below maximum texture size (if possible)
758       scaleFactorIndex = i;
759
760       if( TJSCALED(postXformImageWidth,  (factors[i])) < MAX_TEXTURE_WIDTH &&
761           TJSCALED(postXformImageHeight, (factors[i])) < MAX_TEXTURE_HEIGHT )
762       {
763         // Current scale-factor downscales to below maximum texture size
764         break;
765       }
766     }
767
768     // We have finally chosen the scale-factor, return width/height values
769     if( scaleFactorIndex > 0 )
770     {
771       preXformImageWidth   = TJSCALED(preXformImageWidth,   (factors[scaleFactorIndex]));
772       preXformImageHeight  = TJSCALED(preXformImageHeight,  (factors[scaleFactorIndex]));
773       postXformImageWidth  = TJSCALED(postXformImageWidth,  (factors[scaleFactorIndex]));
774       postXformImageHeight = TJSCALED(postXformImageHeight, (factors[scaleFactorIndex]));
775     }
776   }
777
778   return success;
779 }
780
781 ExifData* LoadExifData( FILE* fp )
782 {
783   ExifData*     exifData=NULL;
784   ExifLoader*   exifLoader;
785   unsigned char dataBuffer[1024];
786
787   if( fseek( fp, 0, SEEK_SET ) )
788   {
789     DALI_LOG_ERROR("Error seeking to start of file\n");
790   }
791   else
792   {
793     exifLoader = exif_loader_new ();
794
795     while( !feof(fp) )
796     {
797       int size = fread( dataBuffer, 1, sizeof( dataBuffer ), fp );
798       if( size <= 0 )
799       {
800         break;
801       }
802       if( ! exif_loader_write( exifLoader, dataBuffer, size ) )
803       {
804         break;
805       }
806     }
807
808     exifData = exif_loader_get_data( exifLoader );
809     exif_loader_unref( exifLoader );
810   }
811
812   return exifData;
813 }
814
815 bool LoadJpegHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
816 {
817   unsigned int requiredWidth  = input.scalingParameters.dimensions.GetWidth();
818   unsigned int requiredHeight = input.scalingParameters.dimensions.GetHeight();
819   FILE* const fp = input.file;
820
821   bool success = false;
822   if( requiredWidth == 0 && requiredHeight == 0 )
823   {
824     success = LoadJpegHeader( fp, width, height );
825   }
826   else
827   {
828     // Double check we get the same width/height from the header
829     unsigned int headerWidth;
830     unsigned int headerHeight;
831     if( LoadJpegHeader( fp, headerWidth, headerHeight ) )
832     {
833       JPGFORM_CODE transform = JPGFORM_NONE;
834
835       if( input.reorientationRequested )
836       {
837         ExifAutoPtr exifData( LoadExifData( fp ) );
838         if( exifData.mData )
839         {
840           transform = ConvertExifOrientation(exifData.mData);
841         }
842
843         int preXformImageWidth = headerWidth;
844         int preXformImageHeight = headerHeight;
845         int postXformImageWidth = headerWidth;
846         int postXformImageHeight = headerHeight;
847
848         success = TransformSize( requiredWidth, requiredHeight, input.scalingParameters.scalingMode, input.scalingParameters.samplingMode, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight );
849         if(success)
850         {
851           width = postXformImageWidth;
852           height = postXformImageHeight;
853         }
854       }
855       else
856       {
857         success = true;
858         width = headerWidth;
859         height = headerHeight;
860       }
861     }
862   }
863   return success;
864 }
865
866
867 } // namespace TizenPlatform
868
869 } // namespace Dali