Dali 3D User Interface Engine
loader-jpeg-turbo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 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"
23 #include "platform-capabilities.h"
24 #include "image-operations.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 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 
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 
72  struct JpegErrorState {
73  struct jpeg_error_mgr errorManager;
74  jmp_buf jumpBuffer;
75  };
76 
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 
97  struct ExifAutoPtr
98  {
99  ExifAutoPtr( ExifData* data)
100  :mData( data )
101  {}
102 
104  {
105  exif_data_free( mData);
106  }
107  ExifData *mData;
108  };
109 
111  struct AutoJpg
112  {
113  AutoJpg(const tjhandle jpgHandle)
114  : mHnd(jpgHandle)
115  {
116  }
117 
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 
137  struct AutoJpgMem
138  {
139  AutoJpgMem(unsigned char * const tjMem)
140  : mTjMem(tjMem)
141  {
142  }
143 
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,
172  FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
173  JPGFORM_CODE transform,
174  int& preXformImageWidth, int& preXformImageHeight,
175  int& postXformImageWidth, int& postXformImageHeight );
176 
177 bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height )
178 {
179  // using libjpeg API to avoid having to read the whole file in a buffer
180  struct jpeg_decompress_struct cinfo;
181  struct JpegErrorState jerr;
182  cinfo.err = jpeg_std_error( &jerr.errorManager );
183 
184  jerr.errorManager.output_message = JpegOutputMessageHandler;
185  jerr.errorManager.error_exit = JpegErrorHandler;
186 
187  // On error exit from the JPEG lib, control will pass via JpegErrorHandler
188  // into this branch body for cleanup and error return:
189  if(setjmp(jerr.jumpBuffer))
190  {
191  jpeg_destroy_decompress(&cinfo);
192  return false;
193  }
194 
195  jpeg_create_decompress( &cinfo );
196 
197  jpeg_stdio_src( &cinfo, fp );
198 
199  // Check header to see if it is JPEG file
200  if( jpeg_read_header( &cinfo, TRUE ) != JPEG_HEADER_OK )
201  {
202  width = height = 0;
203  jpeg_destroy_decompress( &cinfo );
204  return false;
205  }
206 
207  width = cinfo.image_width;
208  height = cinfo.image_height;
209 
210  jpeg_destroy_decompress( &cinfo );
211  return true;
212 }
213 
215 {
216  const int flags= 0;
217  FILE* const fp = input.file;
218 
219  if( fseek(fp,0,SEEK_END) )
220  {
221  DALI_LOG_ERROR("Error seeking to end of file\n");
222  return false;
223  }
224 
225  long positionIndicator = ftell(fp);
226  unsigned int jpegBufferSize = 0u;
227  if( positionIndicator > -1L )
228  {
229  jpegBufferSize = static_cast<unsigned int>(positionIndicator);
230  }
231 
232  if( 0u == jpegBufferSize )
233  {
234  return false;
235  }
236 
237  if( fseek(fp, 0, SEEK_SET) )
238  {
239  DALI_LOG_ERROR("Error seeking to start of file\n");
240  return false;
241  }
242 
243  Vector<unsigned char> jpegBuffer;
244  try
245  {
246  jpegBuffer.Reserve( jpegBufferSize );
247  }
248  catch(...)
249  {
250  DALI_LOG_ERROR( "Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U );
251  return false;
252  }
253  unsigned char * const jpegBufferPtr = jpegBuffer.Begin();
254 
255  // Pull the compressed JPEG image bytes out of a file and into memory:
256  if( fread( jpegBufferPtr, 1, jpegBufferSize, fp ) != jpegBufferSize )
257  {
258  DALI_LOG_WARNING("Error on image file read.");
259  return false;
260  }
261 
262  if( fseek(fp, 0, SEEK_SET) )
263  {
264  DALI_LOG_ERROR("Error seeking to start of file\n");
265  }
266 
267  // Allow early cancellation between the load and the decompress:
268  client.InterruptionPoint();
269 
270  AutoJpg autoJpg(tjInitDecompress());
271 
272  if(autoJpg.GetHandle() == NULL)
273  {
274  DALI_LOG_ERROR("%s\n", tjGetErrorStr());
275  return false;
276  }
277 
278  JPGFORM_CODE transform = JPGFORM_NONE;
279 
280  if( input.reorientationRequested )
281  {
282  ExifAutoPtr exifData( exif_data_new_from_data(jpegBufferPtr, jpegBufferSize) );
283  if( exifData.mData )
284  {
285  transform = ConvertExifOrientation(exifData.mData);
286  }
287  }
288 
289  // Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array:
290  int chrominanceSubsampling = -1;
291  int preXformImageWidth = 0, preXformImageHeight = 0;
292  if( tjDecompressHeader2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling ) == -1 )
293  {
294  DALI_LOG_ERROR("%s\n", tjGetErrorStr());
295  // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
296  }
297 
298  if(preXformImageWidth == 0 || preXformImageHeight == 0)
299  {
300  DALI_LOG_WARNING("Invalid Image!");
301  return false;
302  }
303 
304  int requiredWidth = input.scalingParameters.dimensions.GetWidth();
305  int requiredHeight = input.scalingParameters.dimensions.GetHeight();
306 
307  // If transform is a 90 or 270 degree rotation, the logical width and height
308  // request from the client needs to be adjusted to account by effectively
309  // rotating that too, and the final width and height need to be swapped:
310  int postXformImageWidth = preXformImageWidth;
311  int postXformImageHeight = preXformImageHeight;
312 
313 
314  int scaledPreXformWidth = preXformImageWidth;
315  int scaledPreXformHeight = preXformImageHeight;
316  int scaledPostXformWidth = postXformImageWidth;
317  int scaledPostXformHeight = postXformImageHeight;
318 
319  TransformSize( requiredWidth, requiredHeight,
322  transform,
323  scaledPreXformWidth, scaledPreXformHeight,
324  scaledPostXformWidth, scaledPostXformHeight );
325 
326  // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
327 
328  unsigned char * const bitmapPixelBuffer = bitmap.GetPackedPixelsProfile()->ReserveBuffer(Pixel::RGB888, scaledPostXformWidth, scaledPostXformHeight);
329 
330  // Allow early cancellation before decoding:
331  client.InterruptionPoint();
332 
333  if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, 0, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 )
334  {
335  DALI_LOG_ERROR("%s\n", tjGetErrorStr());
336  return false;
337  }
338 
339  const unsigned int bufferWidth = GetTextureDimension( scaledPreXformWidth );
340  const unsigned int bufferHeight = GetTextureDimension( scaledPreXformHeight );
341 
342  if( transform != JPGFORM_NONE )
343  {
344  // Allow early cancellation before shuffling pixels around on the CPU:
345  client.InterruptionPoint();
346  }
347 
348  bool result = false;
349  switch(transform)
350  {
351  case JPGFORM_NONE:
352  {
353  result = true;
354  break;
355  }
356  // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
357  case JPGFORM_ROT_180:
358  {
359  result = JpegRotate180(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
360  break;
361  }
362  case JPGFORM_ROT_270:
363  {
364  result = JpegRotate270(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
365  break;
366  }
367  case JPGFORM_ROT_90:
368  {
369  result = JpegRotate90(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
370  break;
371  }
373  // physical orientation:
374  case JPGFORM_FLIP_H:
375  case JPGFORM_FLIP_V:
376  case JPGFORM_TRANSPOSE:
377  case JPGFORM_TRANSVERSE:
378  {
379  DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform );
380  break;
381  }
382  }
383  return result;
384 }
385 
387 bool JpegRotate90(unsigned char *buffer, int width, int height, int bpp)
388 {
389  int w, iw, ih, hw = 0;
390  int ix, iy = 0;
391  iw = width;
392  ih = height;
394  data.Reserve(width * height * bpp);
395  unsigned char *dataPtr = data.Begin();
396  memcpy(dataPtr, buffer, width * height * bpp);
397  w = ih;
398  ih = iw;
399  iw = w;
400  hw = iw * ih;
401  hw = - hw - 1;
402  switch(bpp)
403  {
404  case 3:
405  {
406  RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + iw - 1;
407  RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
408 
409  for(ix = iw; -- ix >= 0;)
410  {
411  for(iy = ih; -- iy >= 0; ++from )
412  {
413  *to = *from;
414  to += iw;
415  }
416  to += hw;
417  }
418  break;
419  }
420 
421  default:
422  {
423  return false;
424  }
425  }
426 
427  return true;
428 }
429 
430 bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp)
431 {
432  int ix, iw, ih, hw = 0;
433  iw = width;
434  ih = height;
435  hw = iw * ih;
436  ix = hw;
437 
438  switch(bpp)
439  {
440  case 3:
441  {
442  RGB888Type tmp;
443  RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) ;
444  RGB888Type* from = reinterpret_cast<RGB888Type*>( buffer ) + hw - 1;
445  for(; --ix >= (hw / 2); ++to, --from)
446  {
447  tmp = *to;
448  *to = *from;
449  *from = tmp;
450  }
451  break;
452  }
453 
454  default:
455  {
456  return false;
457  }
458  }
459 
460  return true;
461 }
462 
463 bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp)
464 {
465  int w, iw, ih, hw = 0;
466  int ix, iy = 0;
467 
468  iw = width;
469  ih = height;
471  data.Reserve(width * height * bpp);
472  unsigned char *dataPtr = data.Begin();
473  memcpy(dataPtr, buffer, width * height * bpp);
474  w = ih;
475  ih = iw;
476  iw = w;
477  hw = iw * ih;
478 
479  switch(bpp)
480  {
481  case 3:
482  {
483  RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + hw - iw;
484  RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
485 
486  w = -w;
487  hw = hw + 1;
488  for(ix = iw; -- ix >= 0;)
489  {
490  for(iy = ih; -- iy >= 0;)
491  {
492  *to = *from;
493  from += 1;
494  to += w;
495  }
496  to += hw;
497  }
498  break;
499  }
500  default:
501  {
502  return false;
503  }
504  }
505 
506  return true;
507 }
508 
509 bool EncodeToJpeg( const unsigned char* const pixelBuffer, Vector< unsigned char >& encodedPixels,
510  const std::size_t width, const std::size_t height, const Pixel::Format pixelFormat, unsigned quality )
511 {
512  if( !pixelBuffer )
513  {
514  DALI_LOG_ERROR("Null input buffer\n");
515  return false;
516  }
517 
518  // Translate pixel format enum:
519  int jpegPixelFormat = -1;
520 
521  switch( pixelFormat )
522  {
523  case Pixel::RGB888:
524  {
525  jpegPixelFormat = TJPF_RGB;
526  break;
527  }
528  case Pixel::RGBA8888:
529  {
530  // Ignore the alpha:
531  jpegPixelFormat = TJPF_RGBX;
532  break;
533  }
534  case Pixel::BGRA8888:
535  {
536  // Ignore the alpha:
537  jpegPixelFormat = TJPF_BGRX;
538  break;
539  }
540  default:
541  {
542  DALI_LOG_ERROR( "Unsupported pixel format for encoding to JPEG." );
543  return false;
544  }
545  }
546 
547  // Assert quality is in the documented allowable range of the jpeg-turbo lib:
548  DALI_ASSERT_DEBUG( quality >= 1 );
549  DALI_ASSERT_DEBUG( quality <= 100 );
550  if( quality < 1 )
551  {
552  quality = 1;
553  }
554  if( quality > 100 )
555  {
556  quality = 100;
557  }
558 
559  // Initialise a JPEG codec:
560  AutoJpg autoJpg( tjInitCompress() );
561  {
562  if( autoJpg.GetHandle() == NULL )
563  {
564  DALI_LOG_ERROR( "JPEG Compressor init failed: %s\n", tjGetErrorStr() );
565  return false;
566  }
567 
568  // Run the compressor:
569  unsigned char* dstBuffer = NULL;
570  unsigned long dstBufferSize = 0;
571  const int flags = 0;
572 
573  if( tjCompress2( autoJpg.GetHandle(), const_cast<unsigned char*>(pixelBuffer), width, 0, height, jpegPixelFormat, &dstBuffer, &dstBufferSize, TJSAMP_444, quality, flags ) )
574  {
575  DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr());
576  return false;
577  }
578 
579  // Safely wrap the jpeg codec's buffer in case we are about to throw, then
580  // save the pixels to a persistent buffer that we own and let our cleaner
581  // class clean up the buffer as it goes out of scope:
582  AutoJpgMem cleaner( dstBuffer );
583  encodedPixels.Reserve( dstBufferSize );
584  memcpy( encodedPixels.Begin(), dstBuffer, dstBufferSize );
585  }
586  return true;
587 }
588 
589 
591 {
592  JPGFORM_CODE transform = JPGFORM_NONE;
593  ExifEntry * const entry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
594  int orientation = 0;
595  if( entry )
596  {
597  orientation = exif_get_short(entry->data, exif_data_get_byte_order(entry->parent->parent));
598  switch( orientation )
599  {
600  case 1:
601  {
602  transform = JPGFORM_NONE;
603  break;
604  }
605  case 2:
606  {
607  transform = JPGFORM_FLIP_H;
608  break;
609  }
610  case 3:
611  {
612  transform = JPGFORM_FLIP_V;
613  break;
614  }
615  case 4:
616  {
617  transform = JPGFORM_TRANSPOSE;
618  break;
619  }
620  case 5:
621  {
622  transform = JPGFORM_TRANSVERSE;
623  break;
624  }
625  case 6:
626  {
627  transform = JPGFORM_ROT_90;
628  break;
629  }
630  case 7:
631  {
632  transform = JPGFORM_ROT_180;
633  break;
634  }
635  case 8:
636  {
637  transform = JPGFORM_ROT_270;
638  break;
639  }
640  default:
641  {
642  // Try to keep loading the file, but let app developer know there was something fishy:
643  DALI_LOG_WARNING( "Incorrect/Unknown Orientation setting found in EXIF header of JPEG image (%x). Orientation setting will be ignored.", entry );
644  break;
645  }
646  }
647  }
648  return transform;
649 }
650 
651 bool TransformSize( int requiredWidth, int requiredHeight,
652  FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
653  JPGFORM_CODE transform,
654  int& preXformImageWidth, int& preXformImageHeight,
655  int& postXformImageWidth, int& postXformImageHeight )
656 {
657  bool success = true;
658 
659  if( transform == JPGFORM_ROT_90 || transform == JPGFORM_ROT_270 )
660  {
661  std::swap( requiredWidth, requiredHeight );
662  std::swap( postXformImageWidth, postXformImageHeight );
663  }
664 
665  // Apply the special rules for when there are one or two zeros in requested dimensions:
666  const ImageDimensions correctedDesired = Internal::Platform::CalculateDesiredDimensions( ImageDimensions( postXformImageWidth, postXformImageHeight), ImageDimensions( requiredWidth, requiredHeight ) );
667  requiredWidth = correctedDesired.GetWidth();
668  requiredHeight = correctedDesired.GetHeight();
669 
670  // Rescale image during decode using one of the decoder's built-in rescaling
671  // ratios (expected to be powers of 2), keeping the final image at least as
672  // wide and high as was requested:
673 
674  int numFactors = 0;
675  tjscalingfactor* factors = tjGetScalingFactors( &numFactors );
676  if( factors == NULL )
677  {
678  DALI_LOG_WARNING("TurboJpeg tjGetScalingFactors error!");
679  success = false;
680  }
681  else
682  {
683  // Internal jpeg downscaling is the same as our BOX_X sampling modes so only
684  // apply it if the application requested one of those:
685  // (use a switch case here so this code will fail to compile if other modes are added)
686  bool downscale = true;
687  switch( samplingMode )
688  {
689  case SamplingMode::BOX:
693  {
694  downscale = true;
695  break;
696  }
700  {
701  downscale = false;
702  break;
703  }
704  }
705 
706  int scaleFactorIndex( 0 );
707  if( downscale )
708  {
709  // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
710  for( int i = 1; i < numFactors; ++i )
711  {
712  bool widthLessRequired = TJSCALED( postXformImageWidth, factors[i]) < requiredWidth;
713  bool heightLessRequired = TJSCALED( postXformImageHeight, factors[i]) < requiredHeight;
714  // If either scaled dimension is smaller than the desired one, we were done at the last iteration
715  if ( (fittingMode == FittingMode::SCALE_TO_FILL) && (widthLessRequired || heightLessRequired) )
716  {
717  break;
718  }
719  // If both dimensions are smaller than the desired one, we were done at the last iteration:
720  if ( (fittingMode == FittingMode::SHRINK_TO_FIT) && ( widthLessRequired && heightLessRequired ) )
721  {
722  break;
723  }
724  // If the width is smaller than the desired one, we were done at the last iteration:
725  if ( fittingMode == FittingMode::FIT_WIDTH && widthLessRequired )
726  {
727  break;
728  }
729  // If the width is smaller than the desired one, we were done at the last iteration:
730  if ( fittingMode == FittingMode::FIT_HEIGHT && heightLessRequired )
731  {
732  break;
733  }
734  // This factor stays is within our fitting mode constraint so remember it:
735  scaleFactorIndex = i;
736  }
737  }
738 
739  // Regardless of requested size, downscale to avoid exceeding the maximum texture size:
740  for( int i = scaleFactorIndex; i < numFactors; ++i )
741  {
742  // Continue downscaling to below maximum texture size (if possible)
743  scaleFactorIndex = i;
744 
745  if( TJSCALED(postXformImageWidth, (factors[i])) < MAX_TEXTURE_WIDTH &&
746  TJSCALED(postXformImageHeight, (factors[i])) < MAX_TEXTURE_HEIGHT )
747  {
748  // Current scale-factor downscales to below maximum texture size
749  break;
750  }
751  }
752 
753  // We have finally chosen the scale-factor, return width/height values
754  if( scaleFactorIndex > 0 )
755  {
756  preXformImageWidth = TJSCALED(preXformImageWidth, (factors[scaleFactorIndex]));
757  preXformImageHeight = TJSCALED(preXformImageHeight, (factors[scaleFactorIndex]));
758  postXformImageWidth = TJSCALED(postXformImageWidth, (factors[scaleFactorIndex]));
759  postXformImageHeight = TJSCALED(postXformImageHeight, (factors[scaleFactorIndex]));
760  }
761  }
762 
763  return success;
764 }
765 
766 ExifData* LoadExifData( FILE* fp )
767 {
768  ExifData* exifData=NULL;
769  ExifLoader* exifLoader;
770  unsigned char dataBuffer[1024];
771 
772  if( fseek( fp, 0, SEEK_SET ) )
773  {
774  DALI_LOG_ERROR("Error seeking to start of file\n");
775  }
776  else
777  {
778  exifLoader = exif_loader_new ();
779 
780  while( !feof(fp) )
781  {
782  int size = fread( dataBuffer, 1, sizeof( dataBuffer ), fp );
783  if( size <= 0 )
784  {
785  break;
786  }
787  if( ! exif_loader_write( exifLoader, dataBuffer, size ) )
788  {
789  break;
790  }
791  }
792 
793  exifData = exif_loader_get_data( exifLoader );
794  exif_loader_unref( exifLoader );
795  }
796 
797  return exifData;
798 }
799 
800 bool LoadJpegHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
801 {
802  unsigned int requiredWidth = input.scalingParameters.dimensions.GetWidth();
803  unsigned int requiredHeight = input.scalingParameters.dimensions.GetHeight();
804  FILE* const fp = input.file;
805 
806  bool success = false;
807  if( requiredWidth == 0 && requiredHeight == 0 )
808  {
809  success = LoadJpegHeader( fp, width, height );
810  }
811  else
812  {
813  // Double check we get the same width/height from the header
814  unsigned int headerWidth;
815  unsigned int headerHeight;
816  if( LoadJpegHeader( fp, headerWidth, headerHeight ) )
817  {
818  JPGFORM_CODE transform = JPGFORM_NONE;
819 
820  if( input.reorientationRequested )
821  {
822  ExifAutoPtr exifData( LoadExifData( fp ) );
823  if( exifData.mData )
824  {
825  transform = ConvertExifOrientation(exifData.mData);
826  }
827 
828  int preXformImageWidth = headerWidth;
829  int preXformImageHeight = headerHeight;
830  int postXformImageWidth = headerWidth;
831  int postXformImageHeight = headerHeight;
832 
833  success = TransformSize( requiredWidth, requiredHeight, input.scalingParameters.scalingMode, input.scalingParameters.samplingMode, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight );
834  if(success)
835  {
836  width = postXformImageWidth;
837  height = postXformImageHeight;
838  }
839  }
840  else
841  {
842  success = true;
843  width = headerWidth;
844  height = headerHeight;
845  }
846  }
847  }
848  return success;
849 }
850 
851 
852 } // namespace TizenPlatform
853 
854 } // namespace Dali
Dali Docs Home
Read more about Dali