Adaptor: Fix Klocwork issues
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / slp / 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 SlpPlatform
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   attributes.SetPixelFormat( Pixel::RGB888 );
356
357   const unsigned int  bufferWidth  = GetTextureDimension( scaledPreXformWidth );
358   const unsigned int  bufferHeight = GetTextureDimension( scaledPreXformHeight );
359
360   if( transform != JPGFORM_NONE )
361   {
362     // Allow early cancellation before shuffling pixels around on the CPU:
363     client.InterruptionPoint();
364   }
365
366   bool result = false;
367   switch(transform)
368   {
369     case JPGFORM_FLIP_H:
370     case JPGFORM_FLIP_V:
371     case JPGFORM_TRANSPOSE:
372     case JPGFORM_TRANSVERSE: ///!ToDo: None of these are supported!
373     {
374       DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform );
375       break;
376     }
377     case JPGFORM_NONE:
378     {
379       result = true;
380       break;
381     }
382     case JPGFORM_ROT_180:
383     {
384       result = JpegRotate180(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
385       break;
386     }
387     case JPGFORM_ROT_270:
388     {
389       result = JpegRotate270(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
390       break;
391     }
392     case JPGFORM_ROT_90:
393     {
394       result = JpegRotate90(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
395       break;
396     }
397   }
398   return result;
399 }
400
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   std::vector<unsigned char> data(width * height * bpp);
408   unsigned char *dataPtr = &data[0];
409   memcpy(dataPtr, buffer, width * height * bpp);
410   w = ih;
411   ih = iw;
412   iw = w;
413   hw = iw * ih;
414   hw = - hw - 1;
415   switch(bpp)
416   {
417     case 3:
418     {
419       RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + iw - 1;
420       RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
421
422       for(ix = iw; -- ix >= 0;)
423       {
424         for(iy = ih; -- iy >= 0; ++from )
425         {
426           *to = *from;
427           to += iw;
428         }
429         to += hw;
430       }
431       break;
432     }
433
434     default:
435     {
436       return false;
437     }
438   }
439
440   return true;
441 }
442
443 bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp)
444 {
445   int  ix, iw, ih, hw = 0;
446   iw = width;
447   ih = height;
448   hw = iw * ih;
449   ix = hw;
450
451   switch(bpp)
452   {
453     case 3:
454     {
455       RGB888Type tmp;
456       RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) ;
457       RGB888Type* from = reinterpret_cast<RGB888Type*>( buffer ) + hw - 1;
458       for(; --ix >= (hw / 2); ++to, --from)
459       {
460         tmp = *to;
461         *to = *from;
462         *from = tmp;
463       }
464       break;
465     }
466
467     default:
468     {
469       return false;
470     }
471   }
472
473   return true;
474 }
475
476 bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp)
477 {
478   int  w, iw, ih, hw = 0;
479   int ix, iy = 0;
480
481   iw = width;
482   ih = height;
483   std::vector<unsigned char> data(width * height * bpp);
484   unsigned char *dataPtr = &data[0];
485   memcpy(dataPtr, buffer, width * height * bpp);
486   w = ih;
487   ih = iw;
488   iw = w;
489   hw = iw * ih;
490
491   switch(bpp)
492   {
493     case 3:
494     {
495       RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + hw  - iw;
496       RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
497
498       w = -w;
499       hw =  hw + 1;
500       for(ix = iw; -- ix >= 0;)
501       {
502         for(iy = ih; -- iy >= 0;)
503         {
504           *to = *from;
505           from += 1;
506           to += w;
507         }
508         to += hw;
509       }
510       break;
511     }
512     default:
513     {
514       return false;
515     }
516   }
517
518   return true;
519 }
520
521 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)
522 {
523   if( !pixelBuffer )
524   {
525     DALI_LOG_ERROR("Null input buffer\n");
526     return false;
527   }
528
529   // Translate pixel format enum:
530   int jpegPixelFormat = -1;
531
532   switch( pixelFormat )
533   {
534     case Pixel::RGB888:
535     {
536       jpegPixelFormat = TJPF_RGB;
537       break;
538     }
539     case Pixel::RGBA8888:
540     {
541       // Ignore the alpha:
542       jpegPixelFormat = TJPF_RGBX;
543       break;
544     }
545     case Pixel::BGRA8888:
546     {
547       // Ignore the alpha:
548       jpegPixelFormat = TJPF_BGRX;
549       break;
550     }
551     default:
552     {
553       DALI_LOG_ERROR( "Unsupported pixel format for encoding to JPEG." );
554       return false;
555     }
556   }
557
558   // Assert quality is in the documented allowable range of the jpeg-turbo lib:
559   DALI_ASSERT_DEBUG( quality >= 1 );
560   DALI_ASSERT_DEBUG( quality <= 100 );
561   if( quality < 1 )
562   {
563     quality = 1;
564   }
565   if( quality > 100 )
566   {
567     quality = 100;
568   }
569
570   // Initialise a JPEG codec:
571   AutoJpg autoJpg( tjInitCompress() );
572   {
573     if( autoJpg.GetHandle() == NULL )
574     {
575       DALI_LOG_ERROR( "JPEG Compressor init failed: %s\n", tjGetErrorStr() );
576       return false;
577     }
578
579     // Run the compressor:
580     unsigned char* dstBuffer = NULL;
581     unsigned long dstBufferSize = 0;
582     const int flags = 0;
583
584     if( tjCompress2( autoJpg.GetHandle(), const_cast<unsigned char*>(pixelBuffer), width, 0, height, jpegPixelFormat, &dstBuffer, &dstBufferSize, TJSAMP_444, quality, flags ) )
585     {
586       DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr());
587       return false;
588     }
589
590     // Safely wrap the jpeg codec's buffer in case we are about to throw, then
591     // save the pixels to a persistent buffer that we own and let our cleaner
592     // class clean up the buffer as it goes out of scope:
593     AutoJpgMem cleaner(dstBuffer);
594     encodedPixels.resize(dstBufferSize);
595     memcpy(&encodedPixels[0], dstBuffer, dstBufferSize);
596   }
597   return true;
598 }
599
600
601 JPGFORM_CODE ConvertExifOrientation(ExifData* exifData)
602 {
603   JPGFORM_CODE transform = JPGFORM_NONE;
604   ExifEntry * const entry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
605   int orientation = 0;
606   if( entry )
607   {
608     orientation = exif_get_short(entry->data, exif_data_get_byte_order(entry->parent->parent));
609     switch( orientation )
610     {
611       case 1:
612       {
613         transform = JPGFORM_NONE;
614         break;
615       }
616       case 2:
617       {
618         transform = JPGFORM_FLIP_H;
619         break;
620       }
621       case 3:
622       {
623         transform = JPGFORM_FLIP_V;
624         break;
625       }
626       case 4:
627       {
628         transform = JPGFORM_TRANSPOSE;
629         break;
630       }
631       case 5:
632       {
633         transform = JPGFORM_TRANSVERSE;
634         break;
635       }
636       case 6:
637       {
638         transform = JPGFORM_ROT_90;
639         break;
640       }
641       case 7:
642       {
643         transform = JPGFORM_ROT_180;
644         break;
645       }
646       case 8:
647       {
648         transform = JPGFORM_ROT_270;
649         break;
650       }
651       default:
652       {
653         // Try to keep loading the file, but let app developer know there was something fishy:
654         DALI_LOG_WARNING( "Incorrect/Unknown Orientation setting found in EXIF header of JPEG image (%x). Orientation setting will be ignored.", entry );
655         break;
656       }
657     }
658   }
659   return transform;
660 }
661
662 bool TransformSize( int requiredWidth, int requiredHeight, JPGFORM_CODE transform,
663                     int& preXformImageWidth, int& preXformImageHeight,
664                     int& postXformImageWidth, int& postXformImageHeight )
665 {
666   bool success = true;
667
668   if( transform == JPGFORM_ROT_90 || transform == JPGFORM_ROT_270 )
669   {
670     std::swap( requiredWidth, requiredHeight );
671     std::swap( postXformImageWidth, postXformImageHeight );
672   }
673
674   // Rescale image during decode using one of the decoder's built-in rescaling
675   // ratios (expected to be powers of 2), keeping the final image at least as
676   // wide and high as was requested:
677
678   int numFactors = 0;
679   tjscalingfactor* factors = tjGetScalingFactors( &numFactors );
680   if( factors == NULL )
681   {
682     DALI_LOG_WARNING("TurboJpeg tjGetScalingFactors error!");
683     success = false;
684   }
685   else
686   {
687     // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
688     int scaleFactorIndex( 0 );
689     for( int i = 1; i < numFactors; ++i )
690     {
691       // if requested width or height set to 0, ignore value
692       // TJSCALED performs an integer-based ceil operation on (dim*factor)
693       if( (requiredWidth  && TJSCALED(postXformImageWidth , (factors[i])) > requiredWidth) ||
694           (requiredHeight && TJSCALED(postXformImageHeight, (factors[i])) > requiredHeight) )
695       {
696         scaleFactorIndex = i;
697       }
698       else
699       {
700         // This scaling would result in an image that was smaller than requested in both
701         // dimensions, so stop at the previous entry.
702         break;
703       }
704     }
705
706     // Workaround for libjpeg-turbo problem adding a green line on one edge
707     // when downscaling to 1/8 in each dimension. Prefer not to scale to less than
708     // 1/4 in each dimension:
709     if( 2 < scaleFactorIndex )
710     {
711       scaleFactorIndex = 2;
712       DALI_LOG_INFO( gLoaderFilter, Debug::General, "Down-scaling requested for image limited to 1/4.\n" );
713     }
714
715     // Regardless of requested size, downscale to avoid exceeding the maximum texture size
716     for( int i = scaleFactorIndex; i < numFactors; ++i )
717     {
718       // Continue downscaling to below maximum texture size (if possible)
719       scaleFactorIndex = i;
720
721       if( TJSCALED(postXformImageWidth,  (factors[i])) < MAX_TEXTURE_WIDTH &&
722           TJSCALED(postXformImageHeight, (factors[i])) < MAX_TEXTURE_HEIGHT )
723       {
724         // Current scale-factor downscales to below maximum texture size
725         break;
726       }
727     }
728
729     // We have finally chosen the scale-factor, return width/height values
730     if( scaleFactorIndex > 0 )
731     {
732       preXformImageWidth   = TJSCALED(preXformImageWidth,   (factors[scaleFactorIndex]));
733       preXformImageHeight  = TJSCALED(preXformImageHeight,  (factors[scaleFactorIndex]));
734       postXformImageWidth  = TJSCALED(postXformImageWidth,  (factors[scaleFactorIndex]));
735       postXformImageHeight = TJSCALED(postXformImageHeight, (factors[scaleFactorIndex]));
736     }
737   }
738
739   return success;
740 }
741
742 ExifData* LoadExifData( FILE* fp )
743 {
744   ExifData*     exifData=NULL;
745   ExifLoader*   exifLoader;
746   unsigned char dataBuffer[1024];
747
748   if( fseek( fp, 0, SEEK_SET ) )
749   {
750     DALI_LOG_ERROR("Error seeking to start of file\n");
751   }
752   else
753   {
754     exifLoader = exif_loader_new ();
755
756     while( !feof(fp) )
757     {
758       int size = fread( dataBuffer, 1, sizeof( dataBuffer ), fp );
759       if( size <= 0 )
760       {
761         break;
762       }
763       if( ! exif_loader_write( exifLoader, dataBuffer, size ) )
764       {
765         break;
766       }
767     }
768
769     exifData = exif_loader_get_data( exifLoader );
770     exif_loader_unref( exifLoader );
771   }
772
773   return exifData;
774 }
775
776 bool LoadJpegHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &width, unsigned int &height)
777 {
778   unsigned int requiredWidth  = attributes.GetWidth();
779   unsigned int requiredHeight = attributes.GetHeight();
780
781   bool success = false;
782   if( requiredWidth == 0 && requiredHeight == 0 )
783   {
784     success = LoadJpegHeader( fp, width, height );
785   }
786   else
787   {
788     // Double check we get the same width/height from the header
789     unsigned int headerWidth;
790     unsigned int headerHeight;
791     if( LoadJpegHeader( fp, headerWidth, headerHeight ) )
792     {
793       JPGFORM_CODE transform = JPGFORM_NONE;
794
795       if( attributes.GetOrientationCorrection() )
796       {
797         ExifAutoPtr exifData( LoadExifData( fp ) );
798         if( exifData.mData )
799         {
800           transform = ConvertExifOrientation(exifData.mData);
801         }
802
803         int preXformImageWidth = headerWidth;
804         int preXformImageHeight = headerHeight;
805         int postXformImageWidth = headerWidth;
806         int postXformImageHeight = headerHeight;
807
808         success = TransformSize(requiredWidth, requiredHeight, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight);
809         if(success)
810         {
811           width = postXformImageWidth;
812           height = postXformImageHeight;
813         }
814       }
815       else
816       {
817         success = true;
818         width = headerWidth;
819         height = headerHeight;
820       }
821     }
822   }
823   return success;
824 }
825
826
827 } // namespace SlpPlatform
828
829 } // namespace Dali