2 * Copyright (c) 2020 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.
16 * Copyright notice for the EFL:
17 * Copyright (C) EFL developers (see AUTHORS)
21 #include <dali/internal/imaging/common/gif-loading.h>
24 #include <sys/types.h>
32 #include <dali/integration-api/debug.h>
33 #include <dali/public-api/images/pixel-data.h>
34 #include <dali/internal/imaging/common/file-download.h>
35 #include <dali/internal/system/common/file-reader.h>
36 #include <dali/devel-api/threading/mutex.h>
38 #define IMG_TOO_BIG( w, h ) \
39 ( ( static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h) ) >= \
40 ( (1ULL << (29 * (sizeof(void *) / 4))) - 2048) )
42 #define LOADERR( x ) \
44 DALI_LOG_ERROR( x ); \
59 #if defined(DEBUG_ENABLED)
60 Debug::Filter *gGifLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
63 const int IMG_MAX_SIZE = 65000;
64 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
67 const int DISPOSE_BACKGROUND = 2; /* Set area too background color */
68 const int DISPOSE_PREVIOUS = 3; /* Restore to previous content */
80 dispose( DISPOSE_BACKGROUND ),
86 unsigned short delay; // delay time in 1/100ths of a sec
87 short transparent : 10; // -1 == not, anything else == index
88 short dispose : 6; // 0, 1, 2, 3 (others invalid)
89 short interlace : 1; // interlaced or not
104 if( data != nullptr )
106 // De-allocate memory of the frame data.
113 uint32_t *data; /* frame decoding data */
114 FrameInfo info; /* special image type info */
118 struct GifAnimationData
129 std::vector<ImageFrame> frames;
136 // Forward declaration
148 : fileName( nullptr ),
149 globalMap ( nullptr ),
151 isLocalResource( true )
167 bool LoadLocalFile();
168 bool LoadRemoteFile();
171 const char *fileName; /**< The absolute path of the file. */
172 unsigned char *globalMap ; /**< A pointer to the entire contents of the file */
173 long long length; /**< The length of the file in bytes. */
174 bool isLocalResource; /**< The flag whether the file is a local resource */
187 int position, length; // yes - gif uses ints for file sizes.
191 GifAnimationData animated;
192 std::unique_ptr<GifAccessor> gifAccessor{nullptr};
197 struct ImageProperties
205 * Class to access gif open/close using riaa
211 * @param[in,out] fileInfo Contains ptr to memory, and is updated by DGifOpen
213 GifAccessor(LoaderInfo::FileInfo& fileInfo)
216 // actually ask libgif to open the file
217 #if GIFLIB_MAJOR >= 5
218 gif = DGifOpen( &fileInfo, FileRead, NULL );
220 gif = DGifOpen( &fileInfo, FileRead );
225 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
233 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
234 DGifCloseFile( gif, NULL );
236 DGifCloseFile( gif );
242 * @brief Copy data from gif file into buffer.
244 * @param[in] gifFileType A pointer pointing to GIF File Type
245 * @param[out] buffer A pointer to buffer containing GIF raw data
246 * @param[in] len The length in bytes to be copied
247 * @return The data length of the image in bytes
249 static int FileRead( GifFileType *gifFileType, GifByteType *buffer, int length )
251 LoaderInfo::FileInfo *fi = reinterpret_cast<LoaderInfo::FileInfo *>( gifFileType->UserData );
253 if( fi->position >= fi->length )
255 return 0; // if at or past end - no
257 if( (fi->position + length) >= fi->length )
259 length = fi->length - fi->position;
261 memcpy( buffer, fi->map + fi->position, length );
262 fi->position += length;
267 GifFileType *gif = nullptr;
270 bool LoaderInfo::FileData::LoadFile()
273 if( isLocalResource )
275 success = LoadLocalFile();
279 success = LoadRemoteFile();
284 bool LoaderInfo::FileData::LoadLocalFile()
286 Internal::Platform::FileReader fileReader( fileName );
287 FILE *fp = fileReader.GetFile();
293 if( fseek( fp, 0, SEEK_END ) <= -1 )
298 length = ftell( fp );
304 if( ( ! fseek( fp, 0, SEEK_SET ) ) )
306 globalMap = reinterpret_cast<GifByteType*>( malloc(sizeof( GifByteType ) * length ) );
307 length = fread( globalMap, sizeof( GifByteType ), length, fp);
316 bool LoaderInfo::FileData::LoadRemoteFile()
319 bool succeeded=false;
320 Dali::Vector<uint8_t> dataBuffer;
323 succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory( fileName, dataBuffer, dataSize,
324 MAXIMUM_DOWNLOAD_IMAGE_SIZE );
327 size_t blobSize = dataBuffer.Size();
330 // Open a file handle on the memory buffer:
331 Dali::Internal::Platform::FileReader fileReader( dataBuffer, blobSize );
332 FILE * const fp = fileReader.GetFile();
335 if( ( ! fseek( fp, 0, SEEK_SET ) ) )
337 globalMap = reinterpret_cast<GifByteType*>( malloc(sizeof( GifByteType ) * blobSize ) );
338 length = fread( globalMap, sizeof( GifByteType ), blobSize, fp);
343 DALI_LOG_ERROR( "Error seeking within file\n" );
348 DALI_LOG_ERROR( "Error reading file\n" );
358 * @brief This combines R, G, B and Alpha values into a single 32-bit (ABGR) value.
360 * @param[in] animated A structure containing GIF animation data
361 * @param[in] index Frame index to be searched in GIF
362 * @return A pointer to the ImageFrame.
364 inline int CombinePixelABGR( int a, int r, int g, int b )
366 return ( ((a) << 24) + ((b) << 16) + ((g) << 8) + (r) );
369 inline int PixelLookup( ColorMapObject *colorMap, int index )
371 return CombinePixelABGR( 0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue );
375 * @brief Brute force find frame index - gifs are normally small so ok for now.
377 * @param[in] animated A structure containing GIF animation data
378 * @param[in] index Frame index to be searched in GIF
379 * @return A pointer to the ImageFrame.
381 ImageFrame *FindFrame( const GifAnimationData &animated, int index )
383 for( auto &&elem : animated.frames )
385 if( elem.index == index )
387 return const_cast<ImageFrame *>( &elem );
394 * @brief Fill in an image with a specific rgba color value.
396 * @param[in] data A pointer pointing to an image data
397 * @param[in] row A int containing the number of rows in an image
398 * @param[in] val A uint32_t containing rgba color value
399 * @param[in] x X-coordinate used an offset to calculate pixel position
400 * @param[in] y Y-coordinate used an offset to calculate pixel position
401 * @param[in] width Width of the image
402 * @param[in] height Height of the image
404 void FillImage( uint32_t *data, int row, uint32_t val, int x, int y, int width, int height )
407 uint32_t *pixelPosition;
409 for( yAxis = 0; yAxis < height; yAxis++ )
411 pixelPosition = data + ((y + yAxis) * row) + x;
412 for( xAxis = 0; xAxis < width; xAxis++ )
414 *pixelPosition = val;
421 * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
423 * @param[in] data A pointer pointing to an image data
424 * @param[in] row A int containing the number of rows in an image
425 * @param[in] gif A pointer pointing to GIF File Type
426 * @param[in] frameInfo A pointer pointing to Frame Information data
427 * @param[in] x X-coordinate used an offset to calculate pixel position
428 * @param[in] y Y-coordinate used an offset to calculate pixel position
429 * @param[in] width Width of the image
430 * @param[in] height Height of the image
432 void FillFrame( uint32_t *data, int row, GifFileType *gif, FrameInfo *frameInfo, int x, int y, int w, int h )
434 // solid color fill for pre frame region
435 if( frameInfo->transparent < 0 )
437 ColorMapObject *colorMap;
440 // work out color to use from colorMap
441 if( gif->Image.ColorMap )
443 colorMap = gif->Image.ColorMap;
447 colorMap = gif->SColorMap;
449 backGroundColor = gif->SBackGroundColor;
451 FillImage( data, row,
452 CombinePixelABGR( 0xff, colorMap->Colors[backGroundColor].Red,
453 colorMap->Colors[backGroundColor].Green,
454 colorMap->Colors[backGroundColor].Blue ),
457 // fill in region with 0 (transparent)
460 FillImage( data, row, 0, x, y, w, h );
465 * @brief Store common fields from gif file info into frame info
467 * @param[in] gif A pointer pointing to GIF File Type
468 * @param[in] frameInfo A pointer pointing to Frame Information data
470 void StoreFrameInfo( GifFileType *gif, FrameInfo *frameInfo )
472 frameInfo->x = gif->Image.Left;
473 frameInfo->y = gif->Image.Top;
474 frameInfo->w = gif->Image.Width;
475 frameInfo->h = gif->Image.Height;
476 frameInfo->interlace = gif->Image.Interlace;
480 * @brief Check if image fills "screen space" and if so, if it is transparent
481 * at all then the image could be transparent - OR if image doesnt fill,
482 * then it could be trasnparent (full coverage of screen). Some gifs will
483 * be recognized as solid here for faster rendering, but not all.
485 * @param[out] full A boolean to show whether image is transparent or not
486 * @param[in] frameInfo A pointer pointing to Frame Information data
487 * @param[in] width Width of the image
488 * @param[in] height Height of the image
490 void CheckTransparency( bool &full, FrameInfo *frameInfo, int width, int height )
492 if( ( frameInfo->x == 0 ) && ( frameInfo->y == 0 ) &&
493 ( frameInfo->w == width ) && ( frameInfo->h == height ) )
495 if( frameInfo->transparent >= 0 )
507 * @brief Fix coords and work out an x and y inset in orig data if out of image bounds.
509 void ClipCoordinates( int imageWidth, int imageHeight, int *xin, int *yin, int x0, int y0, int w0, int h0, int *x, int *y, int *w, int *h )
517 if( (x0 + w0) > imageWidth )
519 w0 = imageWidth - x0;
527 if( (y0 + h0) > imageHeight )
529 h0 = imageHeight - y0;
538 * @brief Flush out rgba frame images to save memory but skip current,
539 * previous and lastPreservedFrame frames (needed for dispose mode DISPOSE_PREVIOUS)
541 * @param[in] animated A structure containing GIF animation data
542 * @param[in] width Width of the image
543 * @param[in] height Height of the image
544 * @param[in] thisframe The current frame
545 * @param[in] prevframe The previous frame
546 * @param[in] lastPreservedFrame The last preserved frame
548 void FlushFrames( GifAnimationData &animated, int width, int height, ImageFrame *thisframe, ImageFrame *prevframe, ImageFrame *lastPreservedFrame )
550 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "FlushFrames() START \n" );
552 // target is the amount of memory we want to be under for stored frames
553 int total = 0, target = 512 * 1024;
555 // total up the amount of memory used by stored frames for this image
556 for( auto &&frame : animated.frames )
563 total *= ( width * height * sizeof( uint32_t ) );
565 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "Total used frame size: %d\n", total );
567 // If we use more than target (512k) for frames - flush
570 // Clean frames (except current and previous) until below target
571 for( auto &&frame : animated.frames )
573 if( (frame.index != thisframe->index) && (!prevframe || frame.index != prevframe->index) &&
574 (!lastPreservedFrame || frame.index != lastPreservedFrame->index) )
576 if( frame.data != nullptr )
579 frame.data = nullptr;
581 // subtract memory used and if below target - stop flush
582 total -= ( width * height * sizeof( uint32_t ) );
592 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "FlushFrames() END \n" );
596 * @brief allocate frame and frame info and append to list and store fields.
598 * @param[in] animated A structure containing GIF animation data
599 * @param[in] transparent Transparent index of the new frame
600 * @param[in] dispose Dispose mode of new frame
601 * @param[in] delay The frame delay of new frame
602 * @param[in] index The index of new frame
604 FrameInfo *NewFrame( GifAnimationData &animated, int transparent, int dispose, int delay, int index )
608 // record transparent index to be used or -1 if none
609 // for this SPECIFIC frame
610 frame.info.transparent = transparent;
611 // record dispose mode (3 bits)
612 frame.info.dispose = dispose;
613 // record delay (2 bytes so max 65546 /100 sec)
614 frame.info.delay = delay;
615 // record the index number we are at
617 // that frame is stored AT image/screen size
619 animated.frames.push_back( frame );
621 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "NewFrame: animated.frames.size() = %d\n", animated.frames.size() );
623 return &( animated.frames.back().info );
628 * @brief Decode a gif image into rows then expand to 32bit into the destination
631 bool DecodeImage( GifFileType *gif, uint32_t *data, int rowpix, int xin, int yin,
632 int transparent, int x, int y, int w, int h, bool fill )
634 int intoffset[] = {0, 4, 2, 1};
635 int intjump[] = {8, 8, 4, 2};
636 int i, xx, yy, pix, gifW, gifH;
637 GifRowType *rows = NULL;
639 ColorMapObject *colorMap;
642 // what we need is image size.
644 sp = &gif->SavedImages[ gif->ImageCount - 1 ];
646 gifW = sp->ImageDesc.Width;
647 gifH = sp->ImageDesc.Height;
649 if( ( gifW < w ) || ( gifH < h ) )
651 DALI_ASSERT_DEBUG( false && "Dimensions are bigger than the Gif image size");
655 // build a blob of memory to have pointers to rows of pixels
656 // AND store the decoded gif pixels (1 byte per pixel) as welll
657 rows = static_cast<GifRowType *>( malloc( (gifH * sizeof(GifRowType) ) + ( gifW * gifH * sizeof(GifPixelType) )));
663 // fill in the pointers at the start
664 for( yy = 0; yy < gifH; yy++ )
666 rows[yy] = reinterpret_cast<unsigned char *>(rows) + (gifH * sizeof(GifRowType)) + (yy * gifW * sizeof(GifPixelType));
669 // if gif is interlaced, walk interlace pattern and decode into rows
670 if( gif->Image.Interlace )
672 for( i = 0; i < 4; i++ )
674 for( yy = intoffset[i]; yy < gifH; yy += intjump[i] )
676 if( DGifGetLine( gif, rows[yy], gifW ) != GIF_OK )
683 // normal top to bottom - decode into rows
686 for( yy = 0; yy < gifH; yy++ )
688 if( DGifGetLine( gif, rows[yy], gifW ) != GIF_OK )
695 // work out what colormap to use
696 if( gif->Image.ColorMap )
698 colorMap = gif->Image.ColorMap;
702 colorMap = gif->SColorMap;
705 // if we need to deal with transparent pixels at all...
706 if( transparent >= 0 )
708 // if we are told to FILL (overwrite with transparency kept)
711 for( yy = 0; yy < h; yy++ )
713 p = data + ((y + yy) * rowpix) + x;
714 for( xx = 0; xx < w; xx++ )
716 pix = rows[yin + yy][xin + xx];
717 if( pix != transparent )
719 *p = PixelLookup( colorMap, pix );
729 // paste on top with transparent pixels untouched
732 for( yy = 0; yy < h; yy++ )
734 p = data + ((y + yy) * rowpix) + x;
735 for( xx = 0; xx < w; xx++ )
737 pix = rows[yin + yy][xin + xx];
738 if( pix != transparent )
740 *p = PixelLookup( colorMap, pix );
749 // walk pixels without worring about transparency at all
750 for( yy = 0; yy < h; yy++ )
752 p = data + ((y + yy) * rowpix) + x;
753 for( xx = 0; xx < w; xx++ )
755 pix = rows[yin + yy][xin + xx];
756 *p = PixelLookup( colorMap, pix );
774 * @brief Reader header from the gif file and populates structures accordingly.
776 * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
777 * @param[out] prop A ImageProperties structure for storing information about GIF data.
778 * @param[out] error Error code
779 * @return The true or false whether reading was successful or not.
781 bool ReadHeader( LoaderInfo &loaderInfo,
782 ImageProperties &prop, //output struct
785 GifAnimationData &animated = loaderInfo.animated;
786 LoaderInfo::FileData &fileData = loaderInfo.fileData;
787 bool success = false;
788 LoaderInfo::FileInfo fileInfo;
791 // it is possible which gif file have error midle of frames,
792 // in that case we should play gif file until meet error frame.
795 FrameInfo *frameInfo = NULL;
798 success = fileData.LoadFile();
799 if(!success || !fileData.globalMap)
802 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
806 fileInfo.map = fileData.globalMap;
807 fileInfo.length = fileData.length;
808 fileInfo.position = 0;
809 GifAccessor gifAccessor(fileInfo);
811 if( gifAccessor.gif )
813 // get the gif "screen size" (the actual image size)
814 prop.w = gifAccessor.gif->SWidth;
815 prop.h = gifAccessor.gif->SHeight;
817 // if size is invalid - abort here
818 if( (prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h) )
820 if( IMG_TOO_BIG(prop.w, prop.h) )
823 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
828 DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
833 // walk through gif records in file to figure out info
837 if( DGifGetRecordType(gifAccessor.gif, &rec) == GIF_ERROR )
839 // if we have a gif that ends part way through a sequence
840 // (or animation) consider it valid and just break - no error
841 if( imageNumber <= 1 )
848 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
853 // get image description section
854 if( rec == IMAGE_DESC_RECORD_TYPE )
860 if( DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR )
863 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
866 // skip decoding and just walk image to next
867 if( DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR )
870 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
877 DGifGetCodeNext( gifAccessor.gif, &img );
879 // store geometry in the last frame info data
882 StoreFrameInfo( gifAccessor.gif, frameInfo );
883 CheckTransparency( full, frameInfo, prop.w, prop.h );
885 // or if we dont have a frameInfo entry - create one even for stills
888 // allocate and save frame with field data
889 frameInfo = NewFrame( animated, -1, 0, 0, imageNumber + 1 );
893 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
896 // store geometry info from gif image
897 StoreFrameInfo( gifAccessor.gif, frameInfo );
898 // check for transparency/alpha
899 CheckTransparency( full, frameInfo, prop.w, prop.h );
903 // we have an extension code block - for animated gifs for sure
904 else if( rec == EXTENSION_RECORD_TYPE )
907 GifByteType *ext = NULL;
909 // get the first extension entry
910 DGifGetExtension( gifAccessor.gif, &ext_code, &ext );
913 // graphic control extension - for animated gif data
914 // and transparent index + flag
915 if( ext_code == 0xf9 )
917 // create frame and store it in image
918 int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
919 int disposeMode = (ext[1] >> 2) & 0x7;
920 int delay = (int(ext[3]) << 8) | int(ext[2]);
921 frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
925 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
929 // netscape extension indicating loop count.
930 else if( ext_code == 0xff ) /* application extension */
932 if( !strncmp(reinterpret_cast<char *>(&ext[1]), "NETSCAPE2.0", 11) ||
933 !strncmp(reinterpret_cast<char *>(&ext[1]), "ANIMEXTS1.0", 11) )
936 DGifGetExtensionNext( gifAccessor.gif, &ext );
939 loopCount = (int(ext[3]) << 8) | int(ext[2]);
948 // and continue onto the next extension entry
950 DGifGetExtensionNext( gifAccessor.gif, &ext );
953 } while( rec != TERMINATE_RECORD_TYPE && success );
957 // if the gif main says we have more than one image or our image counting
958 // says so, then this image is animated - indicate this
959 if( (gifAccessor.gif->ImageCount > 1) || (imageNumber > 1) )
961 animated.animated = 1;
962 animated.loopCount = loopCount;
964 animated.frameCount = std::min( gifAccessor.gif->ImageCount, imageNumber );
971 animated.currentFrame = 1;
973 // no errors in header scan etc. so set err and return value
983 * @brief Reader next frame of the gif file and populates structures accordingly.
985 * @param[in,out] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
986 * @param[in,out] prop A ImageProperties structure containing information about gif data.
987 * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
988 * @param[out] error Error code
989 * @return The true or false whether reading was successful or not.
991 bool ReadNextFrame( LoaderInfo &loaderInfo, ImageProperties &prop, // use for w and h
992 unsigned char *pixels, int *error )
994 GifAnimationData &animated = loaderInfo.animated;
995 LoaderInfo::FileData &fileData = loaderInfo.fileData;
998 int index = 0, imageNumber = 0;
999 FrameInfo *frameInfo;
1000 ImageFrame *frame = NULL;
1001 ImageFrame *lastPreservedFrame = NULL;
1003 index = animated.currentFrame;
1005 // if index is invalid for animated image - error out
1006 if ((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
1008 DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
1012 // find the given frame index
1013 frame = FindFrame( animated, index );
1016 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
1019 else if( !(frame->loaded) || !(frame->data) )
1021 // if we want to go backwards, we likely need/want to re-decode from the
1022 // start as we have nothing to build on. If there is a gif, imageNumber
1023 // has been set already.
1024 if( loaderInfo.gifAccessor && loaderInfo.imageNumber > 0)
1026 if( (index > 0) && (index < loaderInfo.imageNumber) && (animated.animated) )
1028 loaderInfo.gifAccessor.reset();
1029 loaderInfo.imageNumber = 0;
1033 // actually ask libgif to open the file
1034 if( !loaderInfo.gifAccessor )
1036 loaderInfo.fileInfo.map = fileData.globalMap ;
1037 loaderInfo.fileInfo.length = fileData.length;
1038 loaderInfo.fileInfo.position = 0;
1039 if( !loaderInfo.fileInfo.map )
1041 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
1044 std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
1045 if( !gifAccessor->gif )
1047 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
1050 loaderInfo.gifAccessor = std::move(gifAccessor);
1051 loaderInfo.imageNumber = 1;
1054 // our current position is the previous frame we decoded from the file
1055 imageNumber = loaderInfo.imageNumber;
1057 // walk through gif records in file to figure out info
1060 if( DGifGetRecordType( loaderInfo.gifAccessor->gif, &rec ) == GIF_ERROR )
1062 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
1066 if( rec == EXTENSION_RECORD_TYPE )
1069 GifByteType *ext = NULL;
1070 DGifGetExtension( loaderInfo.gifAccessor->gif, &ext_code, &ext );
1075 DGifGetExtensionNext( loaderInfo.gifAccessor->gif, &ext );
1078 // get image description section
1079 else if( rec == IMAGE_DESC_RECORD_TYPE )
1081 int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
1084 ImageFrame *previousFrame = NULL;
1085 ImageFrame *thisFrame = NULL;
1088 if( DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR )
1090 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
1094 // get the previous frame entry AND the current one to fill in
1095 previousFrame = FindFrame(animated, imageNumber - 1);
1096 thisFrame = FindFrame(animated, imageNumber);
1098 // if we have a frame AND we're animated AND we have no data...
1099 if( (thisFrame) && (!thisFrame->data) && (animated.animated) )
1104 thisFrame->data = new uint32_t[prop.w * prop.h];
1106 if( !thisFrame->data )
1108 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1112 // if we have no prior frame OR prior frame data... empty
1113 if( (!previousFrame) || (!previousFrame->data) )
1116 frameInfo = &(thisFrame->info);
1117 memset( thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t) );
1119 // we have a prior frame to copy data from...
1122 frameInfo = &( previousFrame->info );
1124 // fix coords of sub image in case it goes out...
1125 ClipCoordinates( prop.w, prop.h, &xin, &yin,
1126 frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1129 // if dispose mode is not restore - then copy pre frame
1130 if( frameInfo->dispose != DISPOSE_PREVIOUS )
1132 memcpy( thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1135 // if dispose mode is "background" then fill with bg
1136 if( frameInfo->dispose == DISPOSE_BACKGROUND )
1138 FillFrame( thisFrame->data, prop.w, loaderInfo.gifAccessor->gif, frameInfo, x, y, w, h );
1140 else if( frameInfo->dispose == DISPOSE_PREVIOUS ) // GIF_DISPOSE_RESTORE
1145 // Find last preserved frame.
1146 lastPreservedFrame = FindFrame( animated, imageNumber - prevIndex );
1147 if( ! lastPreservedFrame )
1149 DALI_LOG_ERROR( "LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND" );
1153 } while( lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS );
1155 if ( lastPreservedFrame )
1157 memcpy( thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1161 // now draw this frame on top
1162 frameInfo = &( thisFrame->info );
1163 ClipCoordinates( prop.w, prop.h, &xin, &yin,
1164 frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1166 if( !DecodeImage( loaderInfo.gifAccessor->gif, thisFrame->data, prop.w,
1167 xin, yin, frameInfo->transparent,
1168 x, y, w, h, first) )
1170 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
1174 // mark as loaded and done
1175 thisFrame->loaded = true;
1177 FlushFrames( animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame );
1179 // if we have a frame BUT the image is not animated. different
1181 else if( (thisFrame) && (!thisFrame->data) && (!animated.animated) )
1183 // if we don't have the data decoded yet - decode it
1184 if( (!thisFrame->loaded) || (!thisFrame->data) )
1186 // use frame info but we WONT allocate frame pixels
1187 frameInfo = &( thisFrame->info );
1188 ClipCoordinates( prop.w, prop.h, &xin, &yin,
1189 frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1192 // clear out all pixels
1193 FillFrame( reinterpret_cast<uint32_t *>(pixels), prop.w, loaderInfo.gifAccessor->gif,
1194 frameInfo, 0, 0, prop.w, prop.h );
1196 // and decode the gif with overwriting
1197 if( !DecodeImage( loaderInfo.gifAccessor->gif, reinterpret_cast<uint32_t *>(pixels),
1198 prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true) )
1200 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
1204 // mark as loaded and done
1205 thisFrame->loaded = true;
1207 // flush mem we don't need (at expense of decode cpu)
1211 // skip decoding and just walk image to next
1212 if( DGifGetCode( loaderInfo.gifAccessor->gif, &img_code, &img ) == GIF_ERROR )
1214 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
1221 DGifGetCodeNext( loaderInfo.gifAccessor->gif, &img );
1226 // if we found the image we wanted - get out of here
1227 if( imageNumber > index )
1232 } while( rec != TERMINATE_RECORD_TYPE );
1234 // if we are at the end of the animation or not animated, close file
1235 loaderInfo.imageNumber = imageNumber;
1236 if( (animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE) )
1238 loaderInfo.gifAccessor.reset();
1239 loaderInfo.imageNumber = 0;
1243 // no errors in header scan etc. so set err and return value
1247 // if it was an animated image we need to copy the data to the
1248 // pixels for the image from the frame holding the data
1249 if( animated.animated && frame->data )
1251 memcpy( pixels, frame->data, prop.w * prop.h * sizeof( uint32_t ) );
1257 } // unnamed namespace
1259 struct GifLoading::Impl
1262 Impl( const std::string& url, bool isLocalResource )
1266 loaderInfo.gifAccessor = nullptr;
1268 loaderInfo.fileData.fileName = mUrl.c_str();
1269 loaderInfo.fileData.isLocalResource = isLocalResource;
1271 ReadHeader( loaderInfo, imageProperties, &error );
1274 // Moveable but not copyable
1275 Impl( const Impl& ) = delete;
1276 Impl& operator=( const Impl& ) = delete;
1277 Impl( Impl&& ) = default;
1278 Impl& operator=( Impl&& ) = default;
1281 LoaderInfo loaderInfo;
1282 ImageProperties imageProperties;
1286 AnimatedImageLoadingPtr GifLoading::New( const std::string &url, bool isLocalResource )
1288 return AnimatedImageLoadingPtr( new GifLoading( url, isLocalResource ) );
1291 GifLoading::GifLoading( const std::string &url, bool isLocalResource )
1292 : mImpl( new GifLoading::Impl( url, isLocalResource ) )
1297 GifLoading::~GifLoading()
1302 bool GifLoading::LoadNextNFrames( uint32_t frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
1307 Mutex::ScopedLock lock( mImpl->mMutex );
1308 const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof( uint32_t );
1310 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
1312 for( int i = 0; i < count; ++i )
1314 auto pixelBuffer = new unsigned char[ bufferSize ];
1316 mImpl->loaderInfo.animated.currentFrame = 1 + ( (frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount );
1318 if( ReadNextFrame( mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error ) )
1322 pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
1323 mImpl->imageProperties.w, mImpl->imageProperties.h,
1324 Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY) );
1333 Dali::Devel::PixelBuffer GifLoading::LoadFrame( uint32_t frameIndex )
1336 Dali::Devel::PixelBuffer pixelBuffer;
1338 Mutex::ScopedLock lock( mImpl->mMutex );
1339 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex );
1341 pixelBuffer = Dali::Devel::PixelBuffer::New( mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888 );
1343 mImpl->loaderInfo.animated.currentFrame = 1 + ( frameIndex % mImpl->loaderInfo.animated.frameCount );
1344 ReadNextFrame( mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer.GetBuffer(), &error );
1348 pixelBuffer = Dali::Devel::PixelBuffer();
1353 ImageDimensions GifLoading::GetImageSize() const
1355 return ImageDimensions( mImpl->imageProperties.w, mImpl->imageProperties.h );
1358 uint32_t GifLoading::GetImageCount() const
1360 return mImpl->loaderInfo.animated.frameCount;
1363 uint32_t GifLoading::GetFrameInterval( uint32_t frameIndex ) const
1365 return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
1368 std::string GifLoading::GetUrl() const
1373 } // namespace Adaptor
1375 } // namespace Internal