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