2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "loader-jpeg.h"
19 #include <turbojpeg.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/bitmap.h>
25 #include <dali/public-api/images/image-attributes.h>
26 #include <resource-loader/debug/resource-loader-debug.h>
27 #include "platform-capabilities.h"
28 #include <libexif/exif-data.h>
29 #include <libexif/exif-loader.h>
30 #include <libexif/exif-tag.h>
35 using Integration::Bitmap;
42 const unsigned DECODED_PIXEL_SIZE = 3;
43 const TJPF DECODED_PIXEL_LIBJPEG_TYPE = TJPF_RGB;
45 // Configuration options for JPEG decoder:
47 const bool FORCEMMX = false; ///< On Intel, use MMX-optimised codepaths.
48 const bool FORCESSE = false; ///< On Intel, use SSE1-optimised codepaths.
49 const bool FORCESSE2 = false; ///< On Intel, use SSE2-optimised codepaths.
50 const bool FORCESSE3 = false; ///< On Intel, use SSE3-optimised codepaths.
51 /** Use the fastest chrominance upsampling algorithm available in the underlying codec. */
52 const bool FASTUPSAMPLE = false;
54 /** Transformations that can be applied to decoded pixels to respect exif orientation
55 * codes in image headers */
58 JPGFORM_NONE = 1, /* no transformation 0th-Row = top & 0th-Column = left */
59 JPGFORM_FLIP_H, /* horizontal flip 0th-Row = top & 0th-Column = right */
60 JPGFORM_FLIP_V, /* vertical flip 0th-Row = bottom & 0th-Column = right*/
61 JPGFORM_TRANSPOSE, /* transpose across UL-to-LR axis 0th-Row = bottom & 0th-Column = left*/
62 JPGFORM_TRANSVERSE,/* transpose across UR-to-LL axis 0th-Row = left & 0th-Column = top*/
63 JPGFORM_ROT_90, /* 90-degree clockwise rotation 0th-Row = right & 0th-Column = top*/
64 JPGFORM_ROT_180, /* 180-degree rotation 0th-Row = right & 0th-Column = bottom*/
65 JPGFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) 0th-Row = left & 0th-Column = bottom*/
95 * @brief Error handling bookeeping for the JPEG Turbo library's
96 * setjmp/longjmp simulated exceptions.
98 struct JpegErrorState {
99 struct jpeg_error_mgr errorManager;
104 * @brief Called by the JPEG library when it hits an error.
105 * We jump out of the library so our loader code can return an error.
107 void JpegErrorHandler ( j_common_ptr cinfo )
109 DALI_LOG_ERROR( "JpegErrorHandler(): libjpeg-turbo fatal error in JPEG decoding.\n" );
110 /* cinfo->err really points to a JpegErrorState struct, so coerce pointer */
111 JpegErrorState * myerr = reinterpret_cast<JpegErrorState *>( cinfo->err );
113 /* Return control to the setjmp point */
114 longjmp( myerr->jumpBuffer, 1 );
117 void JpegOutputMessageHandler( j_common_ptr cinfo )
119 /* Stop libjpeg from printing to stderr - Do Nothing */
122 /** Simple struct to ensure xif data is deleted. */
125 ExifAutoPtr( ExifData* data)
131 exif_data_free( mData);
136 /** simple class to enforce clean-up of JPEG structures. */
139 AutoJpg(const tjhandle jpgHandle)
146 // clean up JPG resources
150 tjhandle GetHandle() const
156 AutoJpg( const AutoJpg& ); //< not defined
157 AutoJpg& operator= ( const AutoJpg& ); //< not defined
160 }; // struct AutoJpg;
162 /** RAII wrapper to free memory allocated by the jpeg-turbo library. */
165 AutoJpgMem(unsigned char * const tjMem)
175 unsigned char * Get() const
181 AutoJpgMem( const AutoJpgMem& ); //< not defined
182 AutoJpgMem& operator= ( const AutoJpgMem& ); //< not defined
184 unsigned char * const mTjMem;
187 // Workaround to avoid exceeding the maximum texture size
188 const int MAX_TEXTURE_WIDTH = 4096;
189 const int MAX_TEXTURE_HEIGHT = 4096;
193 bool JpegRotate90 (unsigned char *buffer, int width, int height, int bpp);
194 bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp);
195 bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp);
196 JPGFORM_CODE ConvertExifOrientation(ExifData* exifData);
197 bool TransformSize( int requiredWidth, int requiredHeight, JPGFORM_CODE transform,
198 int& preXformImageWidth, int& preXformImageHeight,
199 int& postXformImageWidth, int& postXformImageHeight );
201 bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height )
203 // using libjpeg API to avoid having to read the whole file in a buffer
204 struct jpeg_decompress_struct cinfo;
205 struct JpegErrorState jerr;
206 cinfo.err = jpeg_std_error( &jerr.errorManager );
208 jerr.errorManager.output_message = JpegOutputMessageHandler;
209 jerr.errorManager.error_exit = JpegErrorHandler;
211 // On error exit from the JPEG lib, control will pass via JpegErrorHandler
212 // into this branch body for cleanup and error return:
213 if(setjmp(jerr.jumpBuffer))
215 jpeg_destroy_decompress(&cinfo);
219 jpeg_create_decompress( &cinfo );
221 jpeg_stdio_src( &cinfo, fp );
223 // Check header to see if it is JPEG file
224 if( jpeg_read_header( &cinfo, TRUE ) != JPEG_HEADER_OK )
227 jpeg_destroy_decompress( &cinfo );
231 width = cinfo.image_width;
232 height = cinfo.image_height;
234 jpeg_destroy_decompress( &cinfo );
238 bool LoadBitmapFromJpeg( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes )
240 int flags=(FORCEMMX ? TJ_FORCEMMX : 0) |
241 (FORCESSE ? TJ_FORCESSE : 0) |
242 (FORCESSE2 ? TJ_FORCESSE2 : 0) |
243 (FORCESSE3 ? TJ_FORCESSE3 : 0) |
244 (FASTUPSAMPLE ? TJ_FASTUPSAMPLE : 0);
246 if( fseek(fp,0,SEEK_END) )
248 DALI_LOG_ERROR("Error seeking to end of file\n");
252 long positionIndicator = ftell(fp);
253 unsigned int jpegBufferSize = 0u;
254 if( positionIndicator > -1L )
256 jpegBufferSize = static_cast<unsigned int>(positionIndicator);
259 if( 0u == jpegBufferSize )
264 if( fseek(fp, 0, SEEK_SET) )
266 DALI_LOG_ERROR("Error seeking to start of file\n");
270 std::vector<unsigned char> jpegBuffer(0);
273 jpegBuffer.reserve( jpegBufferSize );
277 DALI_LOG_ERROR( "Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U );
280 unsigned char * const jpegBufferPtr = &jpegBuffer[0];
282 AutoJpg autoJpg(tjInitDecompress());
284 if(autoJpg.GetHandle() == NULL)
286 DALI_LOG_ERROR("%s\n", tjGetErrorStr());
290 // Pull the compressed JPEG image bytes out of a file and into memory:
291 if( fread( jpegBufferPtr, 1, jpegBufferSize, fp ) != jpegBufferSize )
293 DALI_LOG_WARNING("Error on image file read.");
297 if( fseek(fp, 0, SEEK_SET) )
299 DALI_LOG_ERROR("Error seeking to start of file\n");
302 JPGFORM_CODE transform = JPGFORM_NONE;
304 if( attributes.GetOrientationCorrection() )
306 ExifAutoPtr exifData( exif_data_new_from_data(jpegBufferPtr, jpegBufferSize) );
309 transform = ConvertExifOrientation(exifData.mData);
313 // Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array:
314 int chrominanceSubsampling = -1;
315 int preXformImageWidth = 0, preXformImageHeight = 0;
316 if( tjDecompressHeader2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling ) == -1 )
318 DALI_LOG_ERROR("%s\n", tjGetErrorStr());
319 // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
322 if(preXformImageWidth == 0 || preXformImageHeight == 0)
324 DALI_LOG_WARNING("Invalid Image!");
328 int requiredWidth = attributes.GetWidth();
329 int requiredHeight = attributes.GetHeight();
331 // If transform is a 90 or 270 degree rotation, the logical width and height
332 // request from the client needs to be adjusted to account by effectively
333 // rotating that too, and the final width and height need to be swapped:
334 int postXformImageWidth = preXformImageWidth;
335 int postXformImageHeight = preXformImageHeight;
338 int scaledPreXformWidth = preXformImageWidth;
339 int scaledPreXformHeight = preXformImageHeight;
340 int scaledPostXformWidth = postXformImageWidth;
341 int scaledPostXformHeight = postXformImageHeight;
343 TransformSize( requiredWidth, requiredHeight, transform,
344 scaledPreXformWidth, scaledPreXformHeight,
345 scaledPostXformWidth, scaledPostXformHeight );
347 // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
349 unsigned char * const bitmapPixelBuffer = bitmap.GetPackedPixelsProfile()->ReserveBuffer(Pixel::RGB888, scaledPostXformWidth, scaledPostXformHeight);
351 const int pitch = scaledPreXformWidth * DECODED_PIXEL_SIZE;
352 if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, pitch, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 )
354 DALI_LOG_ERROR("%s\n", tjGetErrorStr());
358 attributes.SetSize( scaledPostXformWidth, scaledPostXformHeight );
359 attributes.SetPixelFormat( Pixel::RGB888 );
361 const unsigned int bufferWidth = GetTextureDimension( scaledPreXformWidth );
362 const unsigned int bufferHeight = GetTextureDimension( scaledPreXformHeight );
369 case JPGFORM_TRANSPOSE:
370 case JPGFORM_TRANSVERSE: ///!ToDo: None of these are supported!
372 DALI_LOG_WARNING( "Unsupported JPEG Orientation transformation: %x.\n", transform );
380 case JPGFORM_ROT_180:
382 result = JpegRotate180(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
385 case JPGFORM_ROT_270:
387 result = JpegRotate270(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
392 result = JpegRotate90(bitmapPixelBuffer, bufferWidth, bufferHeight, DECODED_PIXEL_SIZE);
399 bool JpegRotate90(unsigned char *buffer, int width, int height, int bpp)
401 int w, iw, ih, hw = 0;
405 std::vector<unsigned char> data(width * height * bpp);
406 unsigned char *dataPtr = &data[0];
407 memcpy(dataPtr, buffer, width * height * bpp);
417 RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + iw - 1;
418 RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
420 for(ix = iw; -- ix >= 0;)
422 for(iy = ih; -- iy >= 0; ++from )
441 bool JpegRotate180(unsigned char *buffer, int width, int height, int bpp)
443 int ix, iw, ih, hw = 0;
454 RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) ;
455 RGB888Type* from = reinterpret_cast<RGB888Type*>( buffer ) + hw - 1;
456 for(; --ix >= (hw / 2); ++to, --from)
474 bool JpegRotate270(unsigned char *buffer, int width, int height, int bpp)
476 int w, iw, ih, hw = 0;
481 std::vector<unsigned char> data(width * height * bpp);
482 unsigned char *dataPtr = &data[0];
483 memcpy(dataPtr, buffer, width * height * bpp);
493 RGB888Type* to = reinterpret_cast<RGB888Type*>(buffer) + hw - iw;
494 RGB888Type* from = reinterpret_cast<RGB888Type*>( dataPtr );
498 for(ix = iw; -- ix >= 0;)
500 for(iy = ih; -- iy >= 0;)
519 bool EncodeToJpeg( const unsigned char* const pixelBuffer, std::vector< unsigned char >& encodedPixels, const std::size_t width, const std::size_t height, const Pixel::Format pixelFormat, unsigned quality)
523 DALI_LOG_ERROR("Null input buffer\n");
527 // Translate pixel format enum:
528 int jpegPixelFormat = -1;
530 switch( pixelFormat )
534 jpegPixelFormat = TJPF_RGB;
537 case Pixel::RGBA8888:
540 jpegPixelFormat = TJPF_RGBX;
543 case Pixel::BGRA8888:
546 jpegPixelFormat = TJPF_BGRX;
551 DALI_LOG_ERROR( "Unsupported pixel format for encoding to JPEG." );
556 // Assert quality is in the documented allowable range of the jpeg-turbo lib:
557 DALI_ASSERT_DEBUG( quality >= 1 );
558 DALI_ASSERT_DEBUG( quality <= 100 );
568 // Initialise a JPEG codec:
569 AutoJpg autoJpg( tjInitCompress() );
571 if( autoJpg.GetHandle() == NULL )
573 DALI_LOG_ERROR( "JPEG Compressor init failed: %s\n", tjGetErrorStr() );
577 // Run the compressor:
578 unsigned char* dstBuffer = NULL;
579 unsigned long dstBufferSize = 0;
582 if( tjCompress2( autoJpg.GetHandle(), const_cast<unsigned char*>(pixelBuffer), width, 0, height, jpegPixelFormat, &dstBuffer, &dstBufferSize, TJSAMP_444, quality, flags ) )
584 DALI_LOG_ERROR("JPEG Compression failed: %s\n", tjGetErrorStr());
588 // Safely wrap the jpeg codec's buffer in case we are about to throw, then
589 // save the pixels to a persistent buffer that we own and let our cleaner
590 // class clean up the buffer as it goes out of scope:
591 AutoJpgMem cleaner(dstBuffer);
592 encodedPixels.resize(dstBufferSize);
593 memcpy(&encodedPixels[0], dstBuffer, dstBufferSize);
599 JPGFORM_CODE ConvertExifOrientation(ExifData* exifData)
601 JPGFORM_CODE transform = JPGFORM_NONE;
602 ExifEntry * const entry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
606 orientation = exif_get_short(entry->data, exif_data_get_byte_order(entry->parent->parent));
607 switch( orientation )
611 transform = JPGFORM_NONE;
616 transform = JPGFORM_FLIP_H;
621 transform = JPGFORM_FLIP_V;
626 transform = JPGFORM_TRANSPOSE;
631 transform = JPGFORM_TRANSVERSE;
636 transform = JPGFORM_ROT_90;
641 transform = JPGFORM_ROT_180;
646 transform = JPGFORM_ROT_270;
651 // Try to keep loading the file, but let app developer know there was something fishy:
652 DALI_LOG_WARNING( "Incorrect/Unknown Orientation setting found in EXIF header of JPEG image (%x). Orientation setting will be ignored.", entry );
660 bool TransformSize( int requiredWidth, int requiredHeight, JPGFORM_CODE transform,
661 int& preXformImageWidth, int& preXformImageHeight,
662 int& postXformImageWidth, int& postXformImageHeight )
666 if( transform == JPGFORM_ROT_90 || transform == JPGFORM_ROT_270 )
668 std::swap( requiredWidth, requiredHeight );
669 std::swap( postXformImageWidth, postXformImageHeight );
672 // Rescale image during decode using one of the decoder's built-in rescaling
673 // ratios (expected to be powers of 2), keeping the final image at least as
674 // wide and high as was requested:
677 tjscalingfactor* factors = tjGetScalingFactors( &numFactors );
678 if( factors == NULL )
680 DALI_LOG_WARNING("TurboJpeg tjGetScalingFactors error!");
685 // Find nearest supported scaling factor (factors are in sequential order, getting smaller)
686 int scaleFactorIndex( 0 );
687 for( int i = 1; i < numFactors; ++i )
689 // if requested width or height set to 0, ignore value
690 // TJSCALED performs an integer-based ceil operation on (dim*factor)
691 if( (requiredWidth && TJSCALED(postXformImageWidth , (factors[i])) > requiredWidth) ||
692 (requiredHeight && TJSCALED(postXformImageHeight, (factors[i])) > requiredHeight) )
694 scaleFactorIndex = i;
698 // This scaling would result in an image that was smaller than requested in both
699 // dimensions, so stop at the previous entry.
704 // Workaround for libjpeg-turbo problem adding a green line on one edge
705 // when downscaling to 1/8 in each dimension. Prefer not to scale to less than
706 // 1/4 in each dimension:
707 if( 2 < scaleFactorIndex )
709 scaleFactorIndex = 2;
710 DALI_LOG_INFO( gLoaderFilter, Debug::General, "Down-scaling requested for image limited to 1/4.\n" );
713 // Regardless of requested size, downscale to avoid exceeding the maximum texture size
714 for( int i = scaleFactorIndex; i < numFactors; ++i )
716 // Continue downscaling to below maximum texture size (if possible)
717 scaleFactorIndex = i;
719 if( TJSCALED(postXformImageWidth, (factors[i])) < MAX_TEXTURE_WIDTH &&
720 TJSCALED(postXformImageHeight, (factors[i])) < MAX_TEXTURE_HEIGHT )
722 // Current scale-factor downscales to below maximum texture size
727 // We have finally chosen the scale-factor, return width/height values
728 if( scaleFactorIndex > 0 )
730 preXformImageWidth = TJSCALED(preXformImageWidth, (factors[scaleFactorIndex]));
731 preXformImageHeight = TJSCALED(preXformImageHeight, (factors[scaleFactorIndex]));
732 postXformImageWidth = TJSCALED(postXformImageWidth, (factors[scaleFactorIndex]));
733 postXformImageHeight = TJSCALED(postXformImageHeight, (factors[scaleFactorIndex]));
740 ExifData* LoadExifData( FILE* fp )
742 ExifData* exifData=NULL;
743 ExifLoader* exifLoader;
744 unsigned char dataBuffer[1024];
746 if( fseek( fp, 0, SEEK_SET ) )
748 DALI_LOG_ERROR("Error seeking to start of file\n");
752 exifLoader = exif_loader_new ();
756 int size = fread( dataBuffer, 1, sizeof( dataBuffer ), fp );
761 if( ! exif_loader_write( exifLoader, dataBuffer, size ) )
767 exifData = exif_loader_get_data( exifLoader );
768 exif_loader_unref( exifLoader );
774 bool LoadJpegHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &width, unsigned int &height)
776 unsigned int requiredWidth = attributes.GetWidth();
777 unsigned int requiredHeight = attributes.GetHeight();
779 bool success = false;
780 if( requiredWidth == 0 && requiredHeight == 0 )
782 success = LoadJpegHeader( fp, width, height );
786 // Double check we get the same width/height from the header
787 unsigned int headerWidth;
788 unsigned int headerHeight;
789 if( LoadJpegHeader( fp, headerWidth, headerHeight ) )
791 JPGFORM_CODE transform = JPGFORM_NONE;
793 if( attributes.GetOrientationCorrection() )
795 ExifAutoPtr exifData( LoadExifData( fp ) );
798 transform = ConvertExifOrientation(exifData.mData);
801 int preXformImageWidth = headerWidth;
802 int preXformImageHeight = headerHeight;
803 int postXformImageWidth = headerWidth;
804 int postXformImageHeight = headerHeight;
806 success = TransformSize(requiredWidth, requiredHeight, transform, preXformImageWidth, preXformImageHeight, postXformImageWidth, postXformImageHeight);
809 width = postXformImageWidth;
810 height = postXformImageHeight;
817 height = headerHeight;
825 } // namespace SlpPlatform