[Tizen] Add codes for Dali Windows Backend
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-jpeg-turbo.cpp
1 /*
2  * Copyright (c) 2017 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  // CLASS HEADER
19 #include <dali/internal/imaging/common/loader-jpeg.h>
20
21 // EXTERNAL HEADERS
22 #include <functional>
23 #include <array>
24 #include <utility>
25 #include <memory>
26 #include <libexif/exif-data.h>
27 #include <libexif/exif-loader.h>
28 #include <libexif/exif-tag.h>
29 #include <turbojpeg.h>
30 #include <jpeglib.h>
31 #include <cstring>
32 #include <setjmp.h>
33
34 #include <dali/public-api/object/property-map.h>
35 #include <dali/public-api/object/property-array.h>
36 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
37
38
39 // INTERNAL HEADERS
40 #include <dali/internal/legacy/tizen/platform-capabilities.h>
41 #include <dali/internal/imaging/common/image-operations.h>
42 #include <dali/devel-api/adaptor-framework/image-loading.h>
43 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
44
45 #include <dali/internal/system/common/file-closer.h>
46
47 using namespace Dali::Internal::Platform;
48
49 namespace
50 {
51 using Dali::Vector;
52 namespace Pixel = Dali::Pixel;
53 using PixelArray = unsigned char*;
54 const unsigned int DECODED_L8 = 1;
55 const unsigned int DECODED_RGB888 = 3;
56 const unsigned int DECODED_RGBA8888 = 4;
57
58 /** Transformations that can be applied to decoded pixels to respect exif orientation
59   *  codes in image headers */
60 enum class JpegTransform
61 {
62   NONE,             //< no transformation 0th-Row = top & 0th-Column = left
63   FLIP_HORIZONTAL,  //< horizontal flip 0th-Row = top & 0th-Column = right
64   FLIP_VERTICAL,    //< vertical flip   0th-Row = bottom & 0th-Column = right
65   TRANSPOSE,        //< transpose across UL-to-LR axis  0th-Row = bottom & 0th-Column = left
66   TRANSVERSE,       //< transpose across UR-to-LL axis  0th-Row = left   & 0th-Column = top
67   ROTATE_90,        //< 90-degree clockwise rotation  0th-Row = right  & 0th-Column = top
68   ROTATE_180,       //< 180-degree rotation  0th-Row = right  & 0th-Column = bottom
69   ROTATE_270,       //< 270-degree clockwise (or 90 ccw) 0th-Row = left  & 0th-Column = bottom
70 };
71
72 /**
73   * @brief Error handling bookeeping for the JPEG Turbo library's
74   * setjmp/longjmp simulated exceptions.
75   */
76 struct JpegErrorState
77 {
78   struct jpeg_error_mgr errorManager;
79   jmp_buf jumpBuffer;
80 };
81
82 /**
83   * @brief Called by the JPEG library when it hits an error.
84   * We jump out of the library so our loader code can return an error.
85   */
86 void  JpegErrorHandler ( j_common_ptr cinfo )
87 {
88   DALI_LOG_ERROR( "JpegErrorHandler(): libjpeg-turbo fatal error in JPEG decoding.\n" );
89   /* cinfo->err really points to a JpegErrorState struct, so coerce pointer */
90   JpegErrorState * myerr = reinterpret_cast<JpegErrorState *>( cinfo->err );
91
92   /* Return control to the setjmp point */
93   longjmp( myerr->jumpBuffer, 1 );
94 }
95
96 void JpegOutputMessageHandler( j_common_ptr cinfo )
97 {
98   /* Stop libjpeg from printing to stderr - Do Nothing */
99 }
100
101 /**
102   * LibJPEG Turbo tjDecompress2 API doesn't distinguish between errors that still allow
103   * the JPEG to be displayed and fatal errors.
104   */
105 bool IsJpegErrorFatal( const std::string& errorMessage )
106 {
107   if( ( errorMessage.find("Corrupt JPEG data") != std::string::npos ) ||
108       ( errorMessage.find("Invalid SOS parameters") != std::string::npos ) ||
109       ( errorMessage.find("Invalid JPEG file structure") != std::string::npos ) ||
110       ( errorMessage.find("Unsupported JPEG process") != std::string::npos ) ||
111       ( errorMessage.find("Unsupported marker type") != std::string::npos ) ||
112       ( errorMessage.find("Bogus marker length") != std::string::npos ) ||
113       ( errorMessage.find("Bogus DQT index") != std::string::npos ) ||
114       ( errorMessage.find("Bogus Huffman table definition") != std::string::npos ))
115   {
116     return false;
117   }
118   return true;
119 }
120
121 // helpers for safe exif memory handling
122 using ExifHandle = std::unique_ptr<ExifData, decltype(exif_data_free)*>;
123
124 ExifHandle MakeNullExifData()
125 {
126   return ExifHandle{nullptr, exif_data_free};
127 }
128
129 ExifHandle MakeExifDataFromData(unsigned char* data, unsigned int size)
130 {
131   return ExifHandle{exif_data_new_from_data(data, size), exif_data_free};
132 }
133
134 // Helpers for safe Jpeg memory handling
135 using JpegHandle = std::unique_ptr<void /*tjhandle*/, decltype(tjDestroy)*>;
136
137 JpegHandle MakeJpegCompressor()
138 {
139   return JpegHandle{tjInitCompress(), tjDestroy};
140 }
141
142 JpegHandle MakeJpegDecompressor()
143 {
144   return JpegHandle{tjInitDecompress(), tjDestroy};
145 }
146
147 using JpegMemoryHandle = std::unique_ptr<unsigned char, decltype(tjFree)*>;
148
149 JpegMemoryHandle MakeJpegMemory()
150 {
151   return JpegMemoryHandle{nullptr, tjFree};
152 }
153
154 template<class T, class Deleter>
155 class UniquePointerSetter final
156 {
157 public:
158   UniquePointerSetter(std::unique_ptr<T, Deleter>& uniquePointer)
159   : mUniquePointer(uniquePointer),
160     mRawPointer(nullptr)
161   {}
162
163   /// @brief Pointer to Pointer cast operator
164   operator T** () { return &mRawPointer; }
165
166   /// @brief Destructor, reset the unique_ptr
167   ~UniquePointerSetter() { mUniquePointer.reset(mRawPointer); }
168
169 private:
170   std::unique_ptr<T, Deleter>& mUniquePointer;
171   T* mRawPointer;
172 };
173
174 template<typename T, typename Deleter>
175 UniquePointerSetter<T, Deleter> SetPointer(std::unique_ptr<T, Deleter>& uniquePointer)
176 {
177   return UniquePointerSetter<T, Deleter>{uniquePointer};
178 }
179
180 using TransformFunction = std::function<void(PixelArray,unsigned, unsigned)>;
181 using TransformFunctionArray = std::array<TransformFunction, 3>; // 1, 3 and 4 bytes per pixel
182
183 /// @brief Select the transform function depending on the pixel format
184 TransformFunction GetTransformFunction(const TransformFunctionArray& functions,
185                                        Pixel::Format pixelFormat)
186 {
187   auto function = TransformFunction{};
188
189   int decodedPixelSize = Pixel::GetBytesPerPixel(pixelFormat);
190   switch( decodedPixelSize )
191   {
192     case DECODED_L8:
193     {
194       function = functions[0];
195       break;
196     }
197     case DECODED_RGB888:
198     {
199       function = functions[1];
200       break;
201     }
202     case DECODED_RGBA8888:
203     {
204       function = functions[2];
205       break;
206     }
207     default:
208     {
209       DALI_LOG_ERROR("Transform operation not supported on this Pixel::Format!");
210       function = functions[1];
211       break;
212     }
213   }
214   return function;
215 }
216
217 // Storing Exif fields as properties
218 template<class R, class V>
219 R ConvertExifNumeric( const ExifEntry& entry )
220 {
221   return static_cast<R>((*reinterpret_cast<V*>(entry.data)));
222 }
223
224 void AddExifFieldPropertyMap( Dali::Property::Map& out, const ExifEntry& entry, ExifIfd ifd )
225 {
226   auto shortName = std::string(exif_tag_get_name_in_ifd(entry.tag, ifd ));
227   switch( entry.format )
228   {
229     case EXIF_FORMAT_ASCII:
230     {
231       out.Insert( shortName, std::string( reinterpret_cast<char *>(entry.data), entry.size ) );
232       break;
233     }
234     case EXIF_FORMAT_SHORT:
235     {
236       out.Insert( shortName, ConvertExifNumeric<int, unsigned int>(entry) );
237       break;
238     }
239     case EXIF_FORMAT_LONG:
240     {
241       out.Insert( shortName, ConvertExifNumeric<int, unsigned long>(entry) );
242       break;
243     }
244     case EXIF_FORMAT_SSHORT:
245     {
246       out.Insert( shortName, ConvertExifNumeric<int, int>(entry) );
247       break;
248     }
249     case EXIF_FORMAT_SLONG:
250     {
251       out.Insert( shortName, ConvertExifNumeric<int, long>(entry) );
252       break;
253     }
254     case EXIF_FORMAT_FLOAT:
255     {
256       out.Insert (shortName, ConvertExifNumeric<float, float>(entry) );
257       break;
258     }
259     case EXIF_FORMAT_DOUBLE:
260     {
261       out.Insert( shortName, ConvertExifNumeric<float, double>(entry) );
262       break;
263     }
264     case EXIF_FORMAT_RATIONAL:
265     {
266       auto values = reinterpret_cast<unsigned int*>( entry.data );
267       Dali::Property::Array array;
268       array.Add( static_cast<int>(values[0]) );
269       array.Add( static_cast<int>(values[1]) );
270       out.Insert(shortName, array);
271       break;
272     }
273     case EXIF_FORMAT_SBYTE:
274     {
275       out.Insert(shortName, "EXIF_FORMAT_SBYTE Unsupported");
276       break;
277     }
278     case EXIF_FORMAT_BYTE:
279     {
280       out.Insert(shortName, "EXIF_FORMAT_BYTE Unsupported");
281       break;
282     }
283     case EXIF_FORMAT_SRATIONAL:
284     {
285       auto values = reinterpret_cast<int*>( entry.data );
286       Dali::Property::Array array;
287       array.Add(values[0]);
288       array.Add(values[1]);
289       out.Insert(shortName, array);
290       break;
291     }
292     case EXIF_FORMAT_UNDEFINED:
293     default:
294     {
295       std::stringstream ss;
296       ss << "EXIF_FORMAT_UNDEFINED, size: " << entry.size << ", components: " << entry.components;
297       out.Insert( shortName, ss.str());
298     }
299   }
300 }
301
302 /// @brief Apply a transform to a buffer
303 bool Transform(const TransformFunctionArray& transformFunctions,
304                PixelArray buffer,
305                int width,
306                int height,
307                Pixel::Format pixelFormat )
308 {
309   auto transformFunction = GetTransformFunction(transformFunctions, pixelFormat);
310   if(transformFunction)
311   {
312     transformFunction(buffer, width, height);
313   }
314   return bool(transformFunction);
315 }
316
317 /// @brief Auxiliar type to represent pixel data with different number of bytes
318 template<size_t N>
319 struct PixelType
320 {
321   char _[N];
322 };
323
324 template<size_t N>
325 void FlipVertical(PixelArray buffer, int width, int height)
326 {
327   // Destination pixel, set as the first pixel of screen
328   auto to = reinterpret_cast<PixelType<N>*>( buffer );
329
330   // Source pixel, as the image is flipped horizontally and vertically,
331   // the source pixel is the end of the buffer of size width * height
332   auto from = reinterpret_cast<PixelType<N>*>(buffer) + width * height - 1;
333
334   for (auto ix = 0, endLoop = (width * height) / 2; ix < endLoop; ++ix, ++to, --from)
335   {
336     std::swap(*from, *to);
337   }
338 }
339
340 template<size_t N>
341 void FlipHorizontal(PixelArray buffer, int width, int height)
342 {
343   for(auto iy = 0; iy < height; ++iy)
344   {
345     //Set the destination pixel as the beginning of the row
346     auto to = reinterpret_cast<PixelType<N>*>(buffer) + width * iy;
347     //Set the source pixel as the end of the row to flip in X axis
348     auto from = reinterpret_cast<PixelType<N>*>(buffer) + width * (iy + 1) - 1;
349     for(auto ix = 0; ix < width / 2; ++ix, ++to, --from)
350     {
351       std::swap(*from, *to);
352     }
353   }
354 }
355
356 template<size_t N>
357 void Transpose(PixelArray buffer, int width, int height)
358 {
359   //Transform vertically only
360   for(auto iy = 0; iy < height / 2; ++iy)
361   {
362     for(auto ix = 0; ix < width; ++ix)
363     {
364       auto to = reinterpret_cast<PixelType<N>*>(buffer) + iy * width + ix;
365       auto from = reinterpret_cast<PixelType<N>*>(buffer) + (height - 1 - iy) * width + ix;
366       std::swap(*from, *to);
367     }
368   }
369 }
370
371 template<size_t N>
372 void Transverse(PixelArray buffer, int width, int height)
373 {
374   using PixelT = PixelType<N>;
375   Vector<PixelT> data;
376   data.Resize( width * height );
377   auto dataPtr = data.Begin();
378
379   auto original = reinterpret_cast<PixelT*>(buffer);
380   std::copy(original, original + width * height, dataPtr);
381
382   auto to = original;
383   for( auto iy = 0; iy < width; ++iy )
384   {
385     for( auto ix = 0; ix < height; ++ix, ++to )
386     {
387       auto from = dataPtr + ix * width + iy;
388       *to = *from;
389     }
390   }
391 }
392
393
394 template<size_t N>
395 void Rotate90(PixelArray buffer, int width, int height)
396 {
397   using PixelT = PixelType<N>;
398   Vector<PixelT> data;
399   data.Resize(width * height);
400   auto dataPtr = data.Begin();
401
402   auto original = reinterpret_cast<PixelT*>(buffer);
403   std::copy(original, original + width * height, dataPtr);
404
405   std::swap(width, height);
406   auto hw = width * height;
407   hw = - hw - 1;
408
409   auto to = original + width - 1;
410   auto from = dataPtr;
411
412   for(auto ix = width; --ix >= 0;)
413   {
414     for(auto iy = height; --iy >= 0; ++from)
415     {
416       *to = *from;
417       to += width;
418     }
419     to += hw;
420   }
421 }
422
423 template<size_t N>
424 void Rotate180(PixelArray buffer, int width, int height)
425 {
426   using PixelT = PixelType<N>;
427   Vector<PixelT> data;
428   data.Resize(width * height);
429   auto dataPtr = data.Begin();
430
431   auto original = reinterpret_cast<PixelT*>(buffer);
432   std::copy(original, original + width * height, dataPtr);
433
434   auto to = original;
435   for( auto iy = 0; iy < width; iy++ )
436   {
437     for( auto ix = 0; ix < height; ix++ )
438     {
439       auto from = dataPtr + (height - ix) * width - 1 - iy;
440       *to = *from;
441       ++to;
442     }
443   }
444 }
445
446
447 template<size_t N>
448 void Rotate270(PixelArray buffer, int width, int height)
449 {
450   using PixelT = PixelType<N>;
451   Vector<PixelT> data;
452   data.Resize(width * height);
453   auto dataPtr = data.Begin();
454
455   auto original = reinterpret_cast<PixelT*>(buffer);
456   std::copy(original, original + width * height, dataPtr);
457
458   auto w = height;
459   std::swap(width, height);
460   auto hw = width * height;
461
462   auto* to = original + hw  - width;
463   auto* from = dataPtr;
464
465   w = -w;
466   hw =  hw + 1;
467   for(auto ix = width; --ix >= 0;)
468   {
469     for(auto iy = height; --iy >= 0;)
470     {
471       *to = *from;
472       ++from;
473       to += w;
474     }
475     to += hw;
476   }
477 }
478
479 } // namespace
480
481 namespace Dali
482 {
483
484 namespace TizenPlatform
485 {
486
487 JpegTransform ConvertExifOrientation(ExifData* exifData);
488 bool TransformSize( int requiredWidth, int requiredHeight,
489                     FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
490                     JpegTransform transform,
491                     int& preXformImageWidth, int& preXformImageHeight,
492                     int& postXformImageWidth, int& postXformImageHeight );
493
494 bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height )
495 {
496   // using libjpeg API to avoid having to read the whole file in a buffer
497   struct jpeg_decompress_struct cinfo;
498   struct JpegErrorState jerr;
499   cinfo.err = jpeg_std_error( &jerr.errorManager );
500
501   jerr.errorManager.output_message = JpegOutputMessageHandler;
502   jerr.errorManager.error_exit = JpegErrorHandler;
503
504   // On error exit from the JPEG lib, control will pass via JpegErrorHandler
505   // into this branch body for cleanup and error return:
506   if(setjmp(jerr.jumpBuffer))
507   {
508     jpeg_destroy_decompress(&cinfo);
509     return false;
510   }
511
512 // jpeg_create_decompress internally uses C casts
513 #pragma GCC diagnostic push
514 #pragma GCC diagnostic ignored "-Wold-style-cast"
515   jpeg_create_decompress( &cinfo );
516 #pragma GCC diagnostic pop
517
518   jpeg_stdio_src( &cinfo, fp );
519
520   // Check header to see if it is  JPEG file
521   if( jpeg_read_header( &cinfo, TRUE ) != JPEG_HEADER_OK )
522   {
523     width = height = 0;
524     jpeg_destroy_decompress( &cinfo );
525     return false;
526   }
527
528   width = cinfo.image_width;
529   height = cinfo.image_height;
530
531   jpeg_destroy_decompress( &cinfo );
532   return true;
533 }
534
535 bool LoadBitmapFromJpeg( const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
536 {
537   const int flags= 0;
538   FILE* const fp = input.file;
539
540   if( InternalFile::fseek(fp,0,SEEK_END) )
541   {
542     DALI_LOG_ERROR("Error seeking to end of file\n");
543     return false;
544   }
545
546   long positionIndicator = InternalFile::ftell(fp);
547   unsigned int jpegBufferSize = 0u;
548   if( positionIndicator > -1L )
549   {
550     jpegBufferSize = static_cast<unsigned int>(positionIndicator);
551   }
552
553   if( 0u == jpegBufferSize )
554   {
555     return false;
556   }
557
558   if( InternalFile::fseek(fp, 0, SEEK_SET) )
559   {
560     DALI_LOG_ERROR("Error seeking to start of file\n");
561     return false;
562   }
563
564   Vector<unsigned char> jpegBuffer;
565   try
566   {
567     jpegBuffer.Resize( jpegBufferSize );
568   }
569   catch(...)
570   {
571     DALI_LOG_ERROR( "Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U );
572     return false;
573   }
574   unsigned char * const jpegBufferPtr = jpegBuffer.Begin();
575
576   // Pull the compressed JPEG image bytes out of a file and into memory:
577   if( InternalFile::fread( jpegBufferPtr, 1, jpegBufferSize, fp ) != jpegBufferSize )
578   {
579     DALI_LOG_WARNING("Error on image file read.\n");
580     return false;
581   }
582
583   if( InternalFile::fseek(fp, 0, SEEK_SET) )
584   {
585     DALI_LOG_ERROR("Error seeking to start of file\n");
586   }
587
588   auto jpeg = MakeJpegDecompressor();
589
590   if(!jpeg)
591   {
592     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
593     return false;
594   }
595
596   auto transform = JpegTransform::NONE;
597
598   // extract exif data
599   auto exifData = MakeExifDataFromData(jpegBufferPtr, jpegBufferSize);
600
601   if( exifData && input.reorientationRequested )
602   {
603     transform = ConvertExifOrientation(exifData.get());
604   }
605
606   std::unique_ptr<Property::Map> exifMap;
607   exifMap.reset( new Property::Map() );
608
609   for( auto k = 0u; k < EXIF_IFD_COUNT; ++k )
610   {
611     auto content = exifData->ifd[k];
612     for (auto i = 0u; i < content->count; ++i)
613     {
614       auto       &&tag      = content->entries[i];
615       const char *shortName = exif_tag_get_name_in_ifd(tag->tag, static_cast<ExifIfd>(k));
616       if(shortName)
617       {
618         AddExifFieldPropertyMap(*exifMap, *tag, static_cast<ExifIfd>(k));
619       }
620     }
621   }
622
623   // Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array:
624   int chrominanceSubsampling = -1;
625   int preXformImageWidth = 0, preXformImageHeight = 0;
626
627   // In Ubuntu, the turbojpeg version is not correct. so build error occurs.
628   // Temporarily separate Ubuntu and other profiles.
629 #ifndef DALI_PROFILE_UBUNTU
630   int jpegColorspace = -1;
631   if( tjDecompressHeader3( jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling, &jpegColorspace ) == -1 )
632   {
633     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
634     // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
635   }
636 #else
637   if( tjDecompressHeader2( jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling ) == -1 )
638   {
639     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
640     // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
641   }
642 #endif
643
644   if(preXformImageWidth == 0 || preXformImageHeight == 0)
645   {
646     DALI_LOG_WARNING("Invalid Image!\n");
647     return false;
648   }
649
650   int requiredWidth  = input.scalingParameters.dimensions.GetWidth();
651   int requiredHeight = input.scalingParameters.dimensions.GetHeight();
652
653   // If transform is a 90 or 270 degree rotation, the logical width and height
654   // request from the client needs to be adjusted to account by effectively
655   // rotating that too, and the final width and height need to be swapped:
656   int postXformImageWidth = preXformImageWidth;
657   int postXformImageHeight = preXformImageHeight;
658
659
660   int scaledPreXformWidth   = preXformImageWidth;
661   int scaledPreXformHeight  = preXformImageHeight;
662   int scaledPostXformWidth  = postXformImageWidth;
663   int scaledPostXformHeight = postXformImageHeight;
664
665   TransformSize( requiredWidth, requiredHeight,
666                  input.scalingParameters.scalingMode,
667                  input.scalingParameters.samplingMode,
668                  transform,
669                  scaledPreXformWidth, scaledPreXformHeight,
670                  scaledPostXformWidth, scaledPostXformHeight );
671
672
673   // Colorspace conversion options
674   TJPF pixelLibJpegType = TJPF_RGB;
675   Pixel::Format pixelFormat = Pixel::RGB888;
676 #ifndef DALI_PROFILE_UBUNTU
677   switch (jpegColorspace)
678   {
679     case TJCS_RGB:
680     // YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission.
681     // YCbCr images must be converted to RGB before they can actually be displayed.
682     case TJCS_YCbCr:
683     {
684       pixelLibJpegType = TJPF_RGB;
685       pixelFormat = Pixel::RGB888;
686       break;
687     }
688     case TJCS_GRAY:
689     {
690       pixelLibJpegType = TJPF_GRAY;
691       pixelFormat = Pixel::L8;
692       break;
693     }
694     case TJCS_CMYK:
695     case TJCS_YCCK:
696     {
697       pixelLibJpegType = TJPF_CMYK;
698       pixelFormat = Pixel::RGBA8888;
699       break;
700     }
701     default:
702     {
703       pixelLibJpegType = TJPF_RGB;
704       pixelFormat = Pixel::RGB888;
705       break;
706     }
707   }
708 #endif
709   // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
710   bitmap = Dali::Devel::PixelBuffer::New(scaledPostXformWidth, scaledPostXformHeight, pixelFormat);
711
712   // set metadata
713   GetImplementation(bitmap).SetMetadata( std::move(exifMap) );
714
715   auto bitmapPixelBuffer = bitmap.GetBuffer();
716
717   if( tjDecompress2( jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char*>( bitmapPixelBuffer ), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags ) == -1 )
718   {
719     std::string errorString = tjGetErrorStr();
720
721     if( IsJpegErrorFatal( errorString ) )
722     {
723         DALI_LOG_ERROR("%s\n", errorString.c_str() );
724         return false;
725     }
726     else
727     {
728         DALI_LOG_WARNING("%s\n", errorString.c_str() );
729     }
730   }
731
732   const unsigned int  bufferWidth  = GetTextureDimension( scaledPreXformWidth );
733   const unsigned int  bufferHeight = GetTextureDimension( scaledPreXformHeight );
734
735   bool result = false;
736   switch(transform)
737   {
738     case JpegTransform::NONE:
739     {
740       result = true;
741       break;
742     }
743     // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
744     case JpegTransform::ROTATE_180:
745     {
746       static auto rotate180Functions = TransformFunctionArray {
747         &Rotate180<1>,
748         &Rotate180<3>,
749         &Rotate180<4>,
750       };
751       result = Transform(rotate180Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
752       break;
753     }
754     case JpegTransform::ROTATE_270:
755     {
756       static auto rotate270Functions = TransformFunctionArray {
757         &Rotate270<1>,
758         &Rotate270<3>,
759         &Rotate270<4>,
760       };
761       result = Transform(rotate270Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
762       break;
763     }
764     case JpegTransform::ROTATE_90:
765     {
766       static auto rotate90Functions = TransformFunctionArray {
767         &Rotate90<1>,
768         &Rotate90<3>,
769         &Rotate90<4>,
770       };
771       result = Transform(rotate90Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
772       break;
773     }
774     case JpegTransform::FLIP_VERTICAL:
775     {
776       static auto flipVerticalFunctions = TransformFunctionArray {
777         &FlipVertical<1>,
778         &FlipVertical<3>,
779         &FlipVertical<4>,
780       };
781       result = Transform(flipVerticalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
782       break;
783     }
784     // Less-common orientation changes, since they don't correspond to a camera's physical orientation:
785     case JpegTransform::FLIP_HORIZONTAL:
786     {
787       static auto flipHorizontalFunctions = TransformFunctionArray {
788         &FlipHorizontal<1>,
789         &FlipHorizontal<3>,
790         &FlipHorizontal<4>,
791       };
792       result = Transform(flipHorizontalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
793       break;
794     }
795     case JpegTransform::TRANSPOSE:
796     {
797       static auto transposeFunctions = TransformFunctionArray {
798         &Transpose<1>,
799         &Transpose<3>,
800         &Transpose<4>,
801       };
802       result = Transform(transposeFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
803       break;
804     }
805     case JpegTransform::TRANSVERSE:
806     {
807       static auto transverseFunctions = TransformFunctionArray {
808         &Transverse<1>,
809         &Transverse<3>,
810         &Transverse<4>,
811       };
812       result = Transform(transverseFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat );
813       break;
814     }
815     default:
816     {
817       DALI_LOG_ERROR( "Unsupported JPEG Orientation transformation: %x.\n", transform );
818       break;
819     }
820   }
821
822   return result;
823 }
824
825 bool EncodeToJpeg( const unsigned char* const pixelBuffer, Vector< unsigned char >& encodedPixels,
826                    const std::size_t width, const std::size_t height, const Pixel::Format pixelFormat, unsigned quality )
827 {
828
829   if( !pixelBuffer )
830   {
831     DALI_LOG_ERROR("Null input buffer\n");
832     return false;
833   }
834
835   // Translate pixel format enum:
836   int jpegPixelFormat = -1;
837
838   switch( pixelFormat )
839   {
840     case Pixel::RGB888:
841     {
842       jpegPixelFormat = TJPF_RGB;
843       break;
844     }
845     case Pixel::RGBA8888:
846     {
847       // Ignore the alpha:
848       jpegPixelFormat = TJPF_RGBX;
849       break;
850     }
851     case Pixel::BGRA8888:
852     {
853       // Ignore the alpha:
854       jpegPixelFormat = TJPF_BGRX;
855       break;
856     }
857     default:
858     {
859       DALI_LOG_ERROR( "Unsupported pixel format for encoding to JPEG.\n" );
860       return false;
861     }
862   }
863
864   // Assert quality is in the documented allowable range of the jpeg-turbo lib:
865   DALI_ASSERT_DEBUG( quality >= 1 );
866   DALI_ASSERT_DEBUG( quality <= 100 );
867   if( quality < 1 )
868   {
869     quality = 1;
870   }
871   if( quality > 100 )
872   {
873     quality = 100;
874   }
875
876   // Initialise a JPEG codec:
877   {
878     auto jpeg = MakeJpegCompressor();
879     if( jpeg )
880     {
881       DALI_LOG_ERROR( "JPEG Compressor init failed: %s\n", tjGetErrorStr() );
882       return false;
883     }
884
885
886     // Safely wrap the jpeg codec's buffer in case we are about to throw, then
887     // save the pixels to a persistent buffer that we own and let our cleaner
888     // class clean up the buffer as it goes out of scope:
889     auto dstBuffer = MakeJpegMemory();
890
891     // Run the compressor:
892     unsigned long dstBufferSize = 0;
893     const int flags = 0;
894
895     if( tjCompress2( jpeg.get(),
896                      const_cast<unsigned char*>(pixelBuffer),
897                      width, 0, height,
898                      jpegPixelFormat, SetPointer(dstBuffer), &dstBufferSize,
899                      TJSAMP_444, quality, flags ) )
900     {
901       DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr());
902       return false;
903     }
904
905     encodedPixels.Resize( dstBufferSize );
906     memcpy( encodedPixels.Begin(), dstBuffer.get(), dstBufferSize );
907   }
908   return true;
909 }
910
911
912 JpegTransform ConvertExifOrientation(ExifData* exifData)
913 {
914   auto transform = JpegTransform::NONE;
915   ExifEntry * const entry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
916   int orientation = 0;
917   if( entry )
918   {
919     orientation = exif_get_short(entry->data, exif_data_get_byte_order(entry->parent->parent));
920     switch( orientation )
921     {
922       case 1:
923       {
924         transform = JpegTransform::NONE;
925         break;
926       }
927       case 2:
928       {
929         transform = JpegTransform::FLIP_HORIZONTAL;
930         break;
931       }
932       case 3:
933       {
934         transform = JpegTransform::FLIP_VERTICAL;
935         break;
936       }
937       case 4:
938       {
939         transform = JpegTransform::TRANSPOSE;
940         break;
941       }
942       case 5:
943       {
944         transform = JpegTransform::TRANSVERSE;
945         break;
946       }
947       case 6:
948       {
949         transform = JpegTransform::ROTATE_90;
950         break;
951       }
952       case 7:
953       {
954         transform = JpegTransform::ROTATE_180;
955         break;
956       }
957       case 8:
958       {
959         transform = JpegTransform::ROTATE_270;
960         break;
961       }
962       default:
963       {
964         // Try to keep loading the file, but let app developer know there was something fishy:
965         DALI_LOG_WARNING( "Incorrect/Unknown Orientation setting found in EXIF header of JPEG image (%x). Orientation setting will be ignored.\n", entry );
966         break;
967       }
968     }
969   }
970   return transform;
971 }
972
973 bool TransformSize( int requiredWidth, int requiredHeight,
974                     FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
975                     JpegTransform transform,
976                     int& preXformImageWidth, int& preXformImageHeight,
977                     int& postXformImageWidth, int& postXformImageHeight )
978 {
979   bool success = true;
980
981   if( transform == JpegTransform::ROTATE_90 || transform == JpegTransform::ROTATE_270 || transform == JpegTransform::ROTATE_180 || transform == JpegTransform::TRANSVERSE)
982   {
983     std::swap( requiredWidth, requiredHeight );
984     std::swap( postXformImageWidth, postXformImageHeight );
985   }
986
987   // Apply the special rules for when there are one or two zeros in requested dimensions:
988   const ImageDimensions correctedDesired = Internal::Platform::CalculateDesiredDimensions( ImageDimensions( postXformImageWidth, postXformImageHeight), ImageDimensions( requiredWidth, requiredHeight ) );
989   requiredWidth = correctedDesired.GetWidth();
990   requiredHeight = correctedDesired.GetHeight();
991
992   // Rescale image during decode using one of the decoder's built-in rescaling
993   // ratios (expected to be powers of 2), keeping the final image at least as
994   // wide and high as was requested:
995
996   int numFactors = 0;
997   tjscalingfactor* factors = tjGetScalingFactors( &numFactors );
998   if( factors == NULL )
999   {
1000     DALI_LOG_WARNING("TurboJpeg tjGetScalingFactors error!\n");
1001     success = false;
1002   }
1003   else
1004   {
1005     // Internal jpeg downscaling is the same as our BOX_X sampling modes so only
1006     // apply it if the application requested one of those:
1007     // (use a switch case here so this code will fail to compile if other modes are added)
1008     bool downscale = true;
1009     switch( samplingMode )
1010     {
1011       case SamplingMode::BOX:
1012       case SamplingMode::BOX_THEN_NEAREST:
1013       case SamplingMode::BOX_THEN_LINEAR:
1014       case SamplingMode::DONT_CARE:
1015       {
1016         downscale = true;
1017         break;
1018       }
1019       case SamplingMode::NO_FILTER:
1020       case SamplingMode::NEAREST:
1021       case SamplingMode::LINEAR:
1022       {
1023         downscale = false;
1024         break;
1025       }
1026     }
1027
1028     int scaleFactorIndex( 0 );
1029     if( downscale )
1030     {
1031       // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
1032       for( int i = 1; i < numFactors; ++i )
1033       {
1034         bool widthLessRequired  = TJSCALED( postXformImageWidth,  factors[i]) < requiredWidth;
1035         bool heightLessRequired = TJSCALED( postXformImageHeight, factors[i]) < requiredHeight;
1036         // If either scaled dimension is smaller than the desired one, we were done at the last iteration
1037         if ( (fittingMode == FittingMode::SCALE_TO_FILL) && (widthLessRequired || heightLessRequired) )
1038         {
1039           break;
1040         }
1041         // If both dimensions are smaller than the desired one, we were done at the last iteration:
1042         if ( (fittingMode == FittingMode::SHRINK_TO_FIT) && ( widthLessRequired && heightLessRequired ) )
1043         {
1044           break;
1045         }
1046         // If the width is smaller than the desired one, we were done at the last iteration:
1047         if ( fittingMode == FittingMode::FIT_WIDTH && widthLessRequired )
1048         {
1049           break;
1050         }
1051         // If the width is smaller than the desired one, we were done at the last iteration:
1052         if ( fittingMode == FittingMode::FIT_HEIGHT && heightLessRequired )
1053         {
1054           break;
1055         }
1056         // This factor stays is within our fitting mode constraint so remember it:
1057         scaleFactorIndex = i;
1058       }
1059     }
1060
1061     // Regardless of requested size, downscale to avoid exceeding the maximum texture size:
1062     for( int i = scaleFactorIndex; i < numFactors; ++i )
1063     {
1064       // Continue downscaling to below maximum texture size (if possible)
1065       scaleFactorIndex = i;
1066
1067       if( TJSCALED(postXformImageWidth,  (factors[i])) < static_cast< int >( Dali::GetMaxTextureSize() ) &&
1068           TJSCALED(postXformImageHeight, (factors[i])) < static_cast< int >( Dali::GetMaxTextureSize() ) )
1069       {
1070         // Current scale-factor downscales to below maximum texture size
1071         break;
1072       }
1073     }
1074
1075     // We have finally chosen the scale-factor, return width/height values
1076     if( scaleFactorIndex > 0 )
1077     {
1078       preXformImageWidth   = TJSCALED(preXformImageWidth,   (factors[scaleFactorIndex]));
1079       preXformImageHeight  = TJSCALED(preXformImageHeight,  (factors[scaleFactorIndex]));
1080       postXformImageWidth  = TJSCALED(postXformImageWidth,  (factors[scaleFactorIndex]));
1081       postXformImageHeight = TJSCALED(postXformImageHeight, (factors[scaleFactorIndex]));
1082     }
1083   }
1084
1085   return success;
1086 }
1087
1088 ExifHandle LoadExifData( FILE* fp )
1089 {
1090   auto exifData = MakeNullExifData();
1091   unsigned char dataBuffer[1024];
1092
1093   if( InternalFile::fseek( fp, 0, SEEK_SET ) )
1094   {
1095     DALI_LOG_ERROR("Error seeking to start of file\n");
1096   }
1097   else
1098   {
1099     auto exifLoader = std::unique_ptr<ExifLoader, decltype(exif_loader_unref)*>{
1100         exif_loader_new(), exif_loader_unref };
1101
1102     while( !InternalFile::feof(fp) )
1103     {
1104       int size = InternalFile::fread( dataBuffer, 1, sizeof( dataBuffer ), fp );
1105       if( size <= 0 )
1106       {
1107         break;
1108       }
1109       if( ! exif_loader_write( exifLoader.get(), dataBuffer, size ) )
1110       {
1111         break;
1112       }
1113     }
1114
1115     exifData.reset( exif_loader_get_data( exifLoader.get() ) );
1116   }
1117
1118   return exifData;
1119 }
1120
1121 bool LoadJpegHeader( const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height )
1122 {
1123   unsigned int requiredWidth  = input.scalingParameters.dimensions.GetWidth();
1124   unsigned int requiredHeight = input.scalingParameters.dimensions.GetHeight();
1125   FILE* const fp = input.file;
1126
1127   bool success = false;
1128   if( requiredWidth == 0 && requiredHeight == 0 )
1129   {
1130     success = LoadJpegHeader( fp, width, height );
1131   }
1132   else
1133   {
1134     // Double check we get the same width/height from the header
1135     unsigned int headerWidth;
1136     unsigned int headerHeight;
1137     if( LoadJpegHeader( fp, headerWidth, headerHeight ) )
1138     {
1139       auto transform = JpegTransform::NONE;
1140
1141       if( input.reorientationRequested )
1142       {
1143         auto exifData = LoadExifData( fp );
1144         if( exifData )
1145         {
1146           transform = ConvertExifOrientation(exifData.get());
1147         }
1148
1149         int preXformImageWidth = headerWidth;
1150         int preXformImageHeight = headerHeight;
1151         int postXformImageWidth = headerWidth;
1152         int postXformImageHeight = headerHeight;
1153
1154         success = TransformSize( requiredWidth, requiredHeight, input.scalingParameters.scalingMode, input.scalingParameters.samplingMode, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight );
1155         if(success)
1156         {
1157           width = postXformImageWidth;
1158           height = postXformImageHeight;
1159         }
1160       }
1161       else
1162       {
1163         success = true;
1164         width = headerWidth;
1165         height = headerHeight;
1166       }
1167     }
1168   }
1169   return success;
1170 }
1171
1172
1173 } // namespace TizenPlatform
1174
1175 } // namespace Dali