2 * Copyright (c) 2017 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 "gif-loading.h"
24 #include <sys/types.h>
31 #include <dali/integration-api/debug.h>
32 #include <dali/public-api/images/pixel-data.h>
34 #define IMG_TOO_BIG( w, h ) \
35 ( ( static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h) ) >= \
36 ( (1ULL << (29 * (sizeof(void *) / 4))) - 2048) )
38 #define LOADERR( x ) \
40 DALI_LOG_ERROR( x ); \
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter *gGifLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
53 const int IMG_MAX_SIZE = 65000;
56 const int DISPOSE_BACKGROUND = 2; /* Set area too background color */
57 const int DISPOSE_PREVIOUS = 3; /* Restore to previous content */
69 dispose( DISPOSE_BACKGROUND ),
75 unsigned short delay; // delay time in 1/100ths of a sec
76 short transparent : 10; // -1 == not, anything else == index
77 short dispose : 6; // 0, 1, 2, 3 (others invalid)
78 short interlace : 1; // interlaced or not
96 uint32_t *data; /* frame decoding data */
97 FrameInfo info; /* special image type info */
101 struct GifAnimationData
112 std::vector<ImageFrame> frames;
130 : fileName( nullptr ),
131 globalMap ( nullptr ),
137 const char *fileName; /**< The absolute path of the file. */
138 unsigned char *globalMap ; /**< A pointer to the entire contents of the file that have been mapped with mmap(2). */
139 unsigned long long length; /**< The length of the file in bytes. */
140 int fileDescriptor; /**< The file descriptor. */
153 int position, length; // yes - gif uses ints for file sizes.
157 GifAnimationData animated;
163 struct ImageProperties
171 * @brief This combines R, G, B and Alpha values into a single 32-bit (ABGR) value.
173 * @param[in] animated A structure containing GIF animation data
174 * @param[in] index Frame index to be searched in GIF
175 * @return A pointer to the ImageFrame.
177 inline int CombinePixelABGR( int a, int r, int g, int b )
179 return ( ((a) << 24) + ((b) << 16) + ((g) << 8) + (r) );
182 inline int PixelLookup( ColorMapObject *colorMap, int index )
184 return CombinePixelABGR( 0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue );
188 * @brief Brute force find frame index - gifs are normally small so ok for now.
190 * @param[in] animated A structure containing GIF animation data
191 * @param[in] index Frame index to be searched in GIF
192 * @return A pointer to the ImageFrame.
194 ImageFrame *FindFrame( const GifAnimationData &animated, int index )
196 for( auto &&elem : animated.frames )
198 if( elem.index == index )
200 return const_cast<ImageFrame *>( &elem );
207 * @brief Fill in an image with a specific rgba color value.
209 * @param[in] data A pointer pointing to an image data
210 * @param[in] row A int containing the number of rows in an image
211 * @param[in] val A uint32_t containing rgba color value
212 * @param[in] x X-coordinate used an offset to calculate pixel position
213 * @param[in] y Y-coordinate used an offset to calculate pixel position
214 * @param[in] width Width of the image
215 * @param[in] height Height of the image
217 void FillImage( uint32_t *data, int row, uint32_t val, int x, int y, int width, int height )
220 uint32_t *pixelPosition;
222 for( yAxis = 0; yAxis < height; yAxis++ )
224 pixelPosition = data + ((y + yAxis) * row) + x;
225 for( xAxis = 0; xAxis < width; xAxis++ )
227 *pixelPosition = val;
234 * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
236 * @param[in] data A pointer pointing to an image data
237 * @param[in] row A int containing the number of rows in an image
238 * @param[in] gif A pointer pointing to GIF File Type
239 * @param[in] frameInfo A pointer pointing to Frame Information data
240 * @param[in] x X-coordinate used an offset to calculate pixel position
241 * @param[in] y Y-coordinate used an offset to calculate pixel position
242 * @param[in] width Width of the image
243 * @param[in] height Height of the image
245 void FillFrame( uint32_t *data, int row, GifFileType *gif, FrameInfo *frameInfo, int x, int y, int w, int h )
247 // solid color fill for pre frame region
248 if( frameInfo->transparent < 0 )
250 ColorMapObject *colorMap;
253 // work out color to use from colorMap
254 if( gif->Image.ColorMap )
256 colorMap = gif->Image.ColorMap;
260 colorMap = gif->SColorMap;
262 backGroundColor = gif->SBackGroundColor;
264 FillImage( data, row,
265 CombinePixelABGR( 0xff, colorMap->Colors[backGroundColor].Red,
266 colorMap->Colors[backGroundColor].Green,
267 colorMap->Colors[backGroundColor].Blue ),
270 // fill in region with 0 (transparent)
273 FillImage( data, row, 0, x, y, w, h );
278 * @brief Store common fields from gif file info into frame info
280 * @param[in] gif A pointer pointing to GIF File Type
281 * @param[in] frameInfo A pointer pointing to Frame Information data
283 void StoreFrameInfo( GifFileType *gif, FrameInfo *frameInfo )
285 frameInfo->x = gif->Image.Left;
286 frameInfo->y = gif->Image.Top;
287 frameInfo->w = gif->Image.Width;
288 frameInfo->h = gif->Image.Height;
289 frameInfo->interlace = gif->Image.Interlace;
293 * @brief Check if image fills "screen space" and if so, if it is transparent
294 * at all then the image could be transparent - OR if image doesnt fill,
295 * then it could be trasnparent (full coverage of screen). Some gifs will
296 * be recognized as solid here for faster rendering, but not all.
298 * @param[out] full A boolean to show whether image is transparent or not
299 * @param[in] frameInfo A pointer pointing to Frame Information data
300 * @param[in] width Width of the image
301 * @param[in] height Height of the image
303 void CheckTransparency( bool &full, FrameInfo *frameInfo, int width, int height )
305 if( ( frameInfo->x == 0 ) && ( frameInfo->y == 0 ) &&
306 ( frameInfo->w == width ) && ( frameInfo->h == height ) )
308 if( frameInfo->transparent >= 0 )
320 * @brief Fix coords and work out an x and y inset in orig data if out of image bounds.
322 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 )
330 if( (x0 + w0) > imageWidth )
332 w0 = imageWidth - x0;
340 if( (y0 + h0) > imageHeight )
342 h0 = imageHeight - y0;
351 * @brief Flush out rgba frame images to save memory but skip current,
352 * previous and lastPreservedFrame frames (needed for dispose mode DISPOSE_PREVIOUS)
354 * @param[in] animated A structure containing GIF animation data
355 * @param[in] width Width of the image
356 * @param[in] height Height of the image
357 * @param[in] thisframe The current frame
358 * @param[in] prevframe The previous frame
359 * @param[in] lastPreservedFrame The last preserved frame
361 void FlushFrames( GifAnimationData &animated, int width, int height, ImageFrame *thisframe, ImageFrame *prevframe, ImageFrame *lastPreservedFrame )
363 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "FlushFrames() START \n" );
365 // target is the amount of memory we want to be under for stored frames
366 int total = 0, target = 512 * 1024;
368 // total up the amount of memory used by stored frames for this image
369 for( auto &&frame : animated.frames )
376 total *= ( width * height * sizeof( uint32_t ) );
378 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "Total used frame size: %d\n", total );
380 // If we use more than target (512k) for frames - flush
383 // Clean frames (except current and previous) until below target
384 for( auto &&frame : animated.frames )
386 if( (frame.index != thisframe->index) && (!prevframe || frame.index != prevframe->index) &&
387 (!lastPreservedFrame || frame.index != lastPreservedFrame->index) )
389 if( frame.data != nullptr )
392 frame.data = nullptr;
394 // subtract memory used and if below target - stop flush
395 total -= ( width * height * sizeof( uint32_t ) );
405 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "FlushFrames() END \n" );
409 * @brief allocate frame and frame info and append to list and store fields.
411 * @param[in] animated A structure containing GIF animation data
412 * @param[in] transparent Transparent index of the new frame
413 * @param[in] dispose Dispose mode of new frame
414 * @param[in] delay The frame delay of new frame
415 * @param[in] index The index of new frame
417 FrameInfo *NewFrame( GifAnimationData &animated, int transparent, int dispose, int delay, int index )
421 // record transparent index to be used or -1 if none
422 // for this SPECIFIC frame
423 frame.info.transparent = transparent;
424 // record dispose mode (3 bits)
425 frame.info.dispose = dispose;
426 // record delay (2 bytes so max 65546 /100 sec)
427 frame.info.delay = delay;
428 // record the index number we are at
430 // that frame is stored AT image/screen size
432 animated.frames.push_back( frame );
434 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "NewFrame: animated.frames.size() = %d\n", animated.frames.size() );
436 return &( animated.frames.back().info );
440 * @brief Copy data from gif file into buffer.
442 * @param[in] gifFileType A pointer pointing to GIF File Type
443 * @param[out] buffer A pointer to buffer containing GIF raw data
444 * @param[in] len The length in bytes to be copied
445 * @return The data length of the image in bytes
447 int FileRead( GifFileType *gifFileType, GifByteType *buffer, int length )
449 LoaderInfo::FileInfo *fi = reinterpret_cast<LoaderInfo::FileInfo *>( gifFileType->UserData );
451 if( fi->position >= fi->length )
453 return 0; // if at or past end - no
455 if( (fi->position + length) >= fi->length )
457 length = fi->length - fi->position;
459 memcpy( buffer, fi->map + fi->position, length );
460 fi->position += length;
465 * @brief Decode a gif image into rows then expand to 32bit into the destination
468 bool DecodeImage( GifFileType *gif, uint32_t *data, int rowpix, int xin, int yin,
469 int transparent, int x, int y, int w, int h, bool fill )
471 int intoffset[] = {0, 4, 2, 1};
472 int intjump[] = {8, 8, 4, 2};
474 GifRowType *rows = NULL;
476 ColorMapObject *colorMap;
479 // what we need is image size.
481 sp = &gif->SavedImages[ gif->ImageCount - 1 ];
486 w = sp->ImageDesc.Width;
487 h = sp->ImageDesc.Height;
489 // build a blob of memory to have pointers to rows of pixels
490 // AND store the decoded gif pixels (1 byte per pixel) as welll
491 rows = static_cast<GifRowType *>(malloc( (h * sizeof(GifRowType) ) + ( w * h * sizeof(GifPixelType) )));
497 // fill in the pointers at the start
498 for( yy = 0; yy < h; yy++ )
500 rows[yy] = reinterpret_cast<unsigned char *>(rows) + (h * sizeof(GifRowType)) + (yy * w * sizeof(GifPixelType));
503 // if gif is interlaced, walk interlace pattern and decode into rows
504 if( gif->Image.Interlace )
506 for( i = 0; i < 4; i++ )
508 for( yy = intoffset[i]; yy < h; yy += intjump[i] )
510 if( DGifGetLine( gif, rows[yy], w ) != GIF_OK )
517 // normal top to bottom - decode into rows
520 for( yy = 0; yy < h; yy++ )
522 if( DGifGetLine( gif, rows[yy], w ) != GIF_OK )
529 // work out what colormap to use
530 if( gif->Image.ColorMap )
532 colorMap = gif->Image.ColorMap;
536 colorMap = gif->SColorMap;
539 // if we need to deal with transparent pixels at all...
540 if( transparent >= 0 )
542 // if we are told to FILL (overwrite with transparency kept)
545 for( yy = 0; yy < h; yy++ )
547 p = data + ((y + yy) * rowpix) + x;
548 for( xx = 0; xx < w; xx++ )
550 pix = rows[yin + yy][xin + xx];
551 if( pix != transparent )
553 *p = PixelLookup( colorMap, pix );
563 // paste on top with transparent pixels untouched
566 for( yy = 0; yy < h; yy++ )
568 p = data + ((y + yy) * rowpix) + x;
569 for( xx = 0; xx < w; xx++ )
571 pix = rows[yin + yy][xin + xx];
572 if( pix != transparent )
574 *p = PixelLookup( colorMap, pix );
583 // walk pixels without worring about transparency at all
584 for( yy = 0; yy < h; yy++ )
586 p = data + ((y + yy) * rowpix) + x;
587 for( xx = 0; xx < w; xx++ )
589 pix = rows[yin + yy][xin + xx];
590 *p = PixelLookup( colorMap, pix );
606 * @brief Reader header from the gif file and populates structures accordingly.
608 * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
609 * @param[out] prop A ImageProperties structure for storing information about GIF data.
610 * @param[out] error Error code
611 * @return The true or false whether reading was successful or not.
613 bool ReadHeader( LoaderInfo &loaderInfo,
614 ImageProperties &prop, //output struct
617 GifAnimationData &animated = loaderInfo.animated;
618 LoaderInfo::FileData &fileData = loaderInfo.fileData;
620 LoaderInfo::FileInfo fileInfo;
622 GifFileType *gif = NULL;
623 // it is possible which gif file have error midle of frames,
624 // in that case we should play gif file until meet error frame.
627 FrameInfo *frameInfo = NULL;
630 // init prop struct with some default null values
634 fileData.fileDescriptor = open( fileData.fileName, O_RDONLY );
636 if( fileData.fileDescriptor == -1 )
641 fileData.length = lseek( fileData.fileDescriptor, 0, SEEK_END );
642 lseek( fileData.fileDescriptor, 0, SEEK_SET );
644 // map the file and store/track info
645 fileData.globalMap = reinterpret_cast<unsigned char *>( mmap(NULL, fileData.length, PROT_READ, MAP_SHARED, fileData.fileDescriptor, 0 ));
646 fileInfo.map = fileData.globalMap ;
648 close(fileData.fileDescriptor);
649 fileData.fileDescriptor = -1;
653 LOADERR("LOAD_ERROR_CORRUPT_FILE");
655 fileInfo.length = fileData.length;
656 fileInfo.position = 0;
658 // actually ask libgif to open the file
659 #if GIFLIB_MAJOR >= 5
660 gif = DGifOpen( &fileInfo, FileRead, NULL );
662 gif = DGifOpen( &fileInfo, FileRead );
667 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
670 // get the gif "screen size" (the actual image size)
671 prop.w = gif->SWidth;
672 prop.h = gif->SHeight;
674 // if size is invalid - abort here
675 if( (prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h) )
677 if( IMG_TOO_BIG(prop.w, prop.h) )
679 LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
681 LOADERR("LOAD_ERROR_GENERIC");
683 // walk through gif records in file to figure out info
686 if( DGifGetRecordType(gif, &rec) == GIF_ERROR )
688 // if we have a gif that ends part way through a sequence
689 // (or animation) consider it valid and just break - no error
690 if( imageNumber > 1 )
694 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
697 // get image description section
698 if( rec == IMAGE_DESC_RECORD_TYPE )
704 if( DGifGetImageDesc(gif) == GIF_ERROR )
706 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
708 // skip decoding and just walk image to next
709 if( DGifGetCode(gif, &img_code, &img) == GIF_ERROR )
711 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
717 DGifGetCodeNext( gif, &img );
719 // store geometry in the last frame info data
722 StoreFrameInfo( gif, frameInfo );
723 CheckTransparency( full, frameInfo, prop.w, prop.h );
725 // or if we dont have a frameInfo entry - create one even for stills
728 // allocate and save frame with field data
729 frameInfo = NewFrame( animated, -1, 0, 0, imageNumber + 1 );
732 LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
734 // store geometry info from gif image
735 StoreFrameInfo( gif, frameInfo );
736 // check for transparency/alpha
737 CheckTransparency( full, frameInfo, prop.w, prop.h );
741 // we have an extension code block - for animated gifs for sure
742 else if( rec == EXTENSION_RECORD_TYPE )
748 // get the first extension entry
749 DGifGetExtension( gif, &ext_code, &ext );
752 // graphic control extension - for animated gif data
753 // and transparent index + flag
754 if( ext_code == 0xf9 )
756 // create frame and store it in image
757 int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
758 int disposeMode = (ext[1] >> 2) & 0x7;
759 int delay = (int(ext[3]) << 8) | int(ext[2]);
760 frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
763 LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
766 // netscape extension indicating loop count.
767 else if( ext_code == 0xff ) /* application extension */
769 if( !strncmp(reinterpret_cast<char *>(&ext[1]), "NETSCAPE2.0", 11) ||
770 !strncmp(reinterpret_cast<char *>(&ext[1]), "ANIMEXTS1.0", 11) )
773 DGifGetExtensionNext( gif, &ext );
776 loopCount = (int(ext[3]) << 8) | int(ext[2]);
784 // and continue onto the next extension entry
786 DGifGetExtensionNext( gif, &ext );
789 } while( rec != TERMINATE_RECORD_TYPE );
791 // if the gif main says we have more than one image or our image counting
792 // says so, then this image is animated - indicate this
793 if( (gif->ImageCount > 1) || (imageNumber > 1) )
795 animated.animated = 1;
796 animated.loopCount = loopCount;
798 animated.frameCount = std::min( gif->ImageCount, imageNumber );
805 animated.currentFrame = 1;
807 // no errors in header scan etc. so set err and return value
811 on_error: // jump here on any errors to clean up
812 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
815 DGifCloseFile( gif, NULL );
820 DGifCloseFile( gif );
828 * @brief Reader next frame of the gif file and populates structures accordingly.
830 * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
831 * @param[in/out] prop A ImageProperties structure containing information about gif data.
832 * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
833 * @param[out] error Error code
834 * @return The true or false whether reading was successful or not.
836 bool ReadNextFrame( LoaderInfo &loaderInfo, ImageProperties &prop, // use for w and h
837 unsigned char *pixels, int *error )
839 GifAnimationData &animated = loaderInfo.animated;
840 LoaderInfo::FileData &fileData = loaderInfo.fileData;
843 GifFileType *gif = NULL;
844 int index = 0, imageNumber = 0;
845 FrameInfo *frameInfo;
846 ImageFrame *frame = NULL;
847 ImageFrame *lastPreservedFrame = NULL;
849 index = animated.currentFrame;
851 // if index is invalid for animated image - error out
852 if ((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
854 LOADERR("LOAD_ERROR_GENERIC");
857 // find the given frame index
858 frame = FindFrame( animated, index );
861 if( (frame->loaded) && (frame->data) )
863 // frame is already there and decoded - jump to end
869 LOADERR("LOAD_ERROR_CORRUPT_FILE");
873 // actually ask libgif to open the file
874 gif = loaderInfo.gif;
877 loaderInfo.fileInfo.map = fileData.globalMap ;
878 if( !loaderInfo.fileInfo.map )
880 LOADERR("LOAD_ERROR_CORRUPT_FILE");
882 loaderInfo.fileInfo.length = fileData.length;
883 loaderInfo.fileInfo.position = 0;
885 #if GIFLIB_MAJOR >= 5
886 gif = DGifOpen( &( loaderInfo.fileInfo ), FileRead, NULL );
888 gif = DGifOpen( &( loaderInfo.fileInfo ), FileRead );
890 // if gif open failed... get out of here
893 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
895 loaderInfo.gif = gif;
896 loaderInfo.imageNumber = 1;
899 // if we want to go backwards, we likely need/want to re-decode from the
900 // start as we have nothing to build on
901 if( (index > 0) && (index < loaderInfo.imageNumber) && (animated.animated) )
903 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
905 DGifCloseFile( loaderInfo.gif, NULL );
908 DGifCloseFile( loaderInfo.gif );
910 loaderInfo.gif = NULL;
911 loaderInfo.imageNumber = 0;
915 // our current position is the previous frame we decoded from the file
916 imageNumber = loaderInfo.imageNumber;
918 // walk through gif records in file to figure out info
921 if( DGifGetRecordType( gif, &rec ) == GIF_ERROR )
923 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
926 if( rec == EXTENSION_RECORD_TYPE )
929 GifByteType *ext = NULL;
930 DGifGetExtension( gif, &ext_code, &ext );
935 DGifGetExtensionNext( gif, &ext );
938 // get image description section
939 else if( rec == IMAGE_DESC_RECORD_TYPE )
941 int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
944 ImageFrame *previousFrame = NULL;
945 ImageFrame *thisFrame = NULL;
948 if( DGifGetImageDesc(gif) == GIF_ERROR )
950 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
953 // get the previous frame entry AND the current one to fill in
954 previousFrame = FindFrame(animated, imageNumber - 1);
955 thisFrame = FindFrame(animated, imageNumber);
957 // if we have a frame AND we're animated AND we have no data...
958 if( (thisFrame) && (!thisFrame->data) && (animated.animated) )
963 thisFrame->data = new uint32_t[prop.w * prop.h];
965 if( !thisFrame->data )
967 LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
970 // if we have no prior frame OR prior frame data... empty
971 if( (!previousFrame) || (!previousFrame->data) )
974 frameInfo = &(thisFrame->info);
975 memset( thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t) );
977 // we have a prior frame to copy data from...
980 frameInfo = &( previousFrame->info );
982 // fix coords of sub image in case it goes out...
983 ClipCoordinates( prop.w, prop.h, &xin, &yin,
984 frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
987 // if dispose mode is not restore - then copy pre frame
988 if( frameInfo->dispose != DISPOSE_PREVIOUS )
990 memcpy( thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t) );
993 // if dispose mode is "background" then fill with bg
994 if( frameInfo->dispose == DISPOSE_BACKGROUND )
996 FillFrame( thisFrame->data, prop.w, gif, frameInfo, x, y, w, h );
999 else if( frameInfo->dispose == DISPOSE_PREVIOUS ) // GIF_DISPOSE_RESTORE
1004 // Find last preserved frame.
1005 lastPreservedFrame = FindFrame( animated, imageNumber - prevIndex );
1007 } while( lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS );
1009 if ( lastPreservedFrame )
1011 memcpy( thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1015 // now draw this frame on top
1016 frameInfo = &( thisFrame->info );
1017 ClipCoordinates( prop.w, prop.h, &xin, &yin,
1018 frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1020 if( !DecodeImage( gif, thisFrame->data, prop.w,
1021 xin, yin, frameInfo->transparent,
1022 x, y, w, h, first) )
1024 LOADERR("LOAD_ERROR_CORRUPT_FILE");
1027 // mark as loaded and done
1028 thisFrame->loaded = true;
1030 FlushFrames( animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame );
1032 // if we hve a frame BUT the image is not animated... different
1034 else if( (thisFrame) && (!thisFrame->data) && (!animated.animated) )
1036 // if we don't have the data decoded yet - decode it
1037 if( (!thisFrame->loaded) || (!thisFrame->data) )
1039 // use frame info but we WONT allocate frame pixels
1040 frameInfo = &( thisFrame->info );
1041 ClipCoordinates( prop.w, prop.h, &xin, &yin,
1042 frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1045 // clear out all pixels
1046 FillFrame( reinterpret_cast<uint32_t *>(pixels), prop.w, gif, frameInfo, 0, 0, prop.w, prop.h );
1048 // and decode the gif with overwriting
1049 if( !DecodeImage( gif, reinterpret_cast<uint32_t *>(pixels), prop.w,
1050 xin, yin, frameInfo->transparent, x, y, w, h, true) )
1052 LOADERR("LOAD_ERROR_CORRUPT_FILE");
1055 // mark as loaded and done
1056 thisFrame->loaded = true;
1058 // flush mem we don't need (at expense of decode cpu)
1062 // skip decoding and just walk image to next
1063 if( DGifGetCode( gif, &img_code, &img ) == GIF_ERROR )
1065 LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
1071 DGifGetCodeNext( gif, &img );
1076 // if we found the image we wanted - get out of here
1077 if( imageNumber > index )
1082 } while( rec != TERMINATE_RECORD_TYPE );
1084 // if we are at the end of the animation or not animated, close file
1085 loaderInfo.imageNumber = imageNumber;
1086 if( (animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE) )
1088 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
1089 if( loaderInfo.gif )
1091 DGifCloseFile( loaderInfo.gif, NULL );
1094 if( loaderInfo.gif )
1096 DGifCloseFile( loaderInfo.gif );
1100 loaderInfo.gif = NULL;
1101 loaderInfo.imageNumber = 0;
1105 // no errors in header scan etc. so set err and return value
1109 // if it was an animated image we need to copy the data to the
1110 // pixels for the image from the frame holding the data
1111 if( animated.animated && frame->data )
1113 memcpy( pixels, frame->data, prop.w * prop.h * sizeof( uint32_t ) );
1116 on_error: // jump here on any errors to clean up
1120 } // unnamed namespace
1122 struct GifLoading::Impl
1125 Impl( const std::string& url)
1128 loaderInfo.gif = nullptr;
1130 loaderInfo.fileData.fileName = mUrl.c_str();
1132 ReadHeader( loaderInfo, imageProperties, &error );
1137 if( loaderInfo.fileData.globalMap )
1139 munmap( loaderInfo.fileData.globalMap , loaderInfo.fileData.length );
1140 loaderInfo.fileData.globalMap = nullptr;
1145 LoaderInfo loaderInfo;
1146 ImageProperties imageProperties;
1149 std::unique_ptr<GifLoading> GifLoading::New( const std::string &url )
1151 return std::unique_ptr<GifLoading>( new GifLoading( url ) );
1154 GifLoading::GifLoading( const std::string &url )
1155 : mImpl( new GifLoading::Impl( url ) )
1159 GifLoading::~GifLoading()
1164 bool GifLoading::LoadNextNFrames( int frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
1169 const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof( uint32_t );
1171 DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
1173 for( int i = 0; i < count; ++i )
1175 auto pixelBuffer = new unsigned char[ bufferSize ];
1177 mImpl->loaderInfo.animated.currentFrame = 1 + ( (frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount );
1179 if( ReadNextFrame( mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error ) )
1183 pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
1184 mImpl->imageProperties.w, mImpl->imageProperties.h,
1185 Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY) );
1194 bool GifLoading::LoadAllFrames( std::vector<Dali::PixelData> &pixelData, Dali::Vector<uint32_t> &frameDelays )
1196 if( LoadFrameDelays( frameDelays ) )
1198 return LoadNextNFrames( 0, mImpl->loaderInfo.animated.frameCount, pixelData );
1203 ImageDimensions GifLoading::GetImageSize()
1205 return ImageDimensions( mImpl->imageProperties.w, mImpl->imageProperties.w );
1208 int GifLoading::GetImageCount()
1210 return mImpl->loaderInfo.animated.frameCount;
1213 bool GifLoading::LoadFrameDelays( Dali::Vector<uint32_t> &frameDelays )
1215 frameDelays.Clear();
1217 for( auto &&elem : mImpl->loaderInfo.animated.frames )
1219 // Read frame delay time, multiply 10 to change time unit to milliseconds
1220 frameDelays.PushBack( elem.info.delay * 10 );