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