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