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