2 * Copyright (c) 2021 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>
27 #include <sys/types.h>
32 #include <dali/devel-api/threading/mutex.h>
33 #include <dali/integration-api/debug.h>
34 #include <dali/internal/imaging/common/file-download.h>
35 #include <dali/internal/system/common/file-reader.h>
36 #include <dali/public-api/images/pixel-data.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))
57 #if defined(DEBUG_ENABLED)
58 Debug::Filter* gGifLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
61 const int IMG_MAX_SIZE = 65000;
62 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
65 const int DISPOSE_BACKGROUND = 2; /* Set area too background color */
66 const int DISPOSE_PREVIOUS = 3; /* Restore to previous content */
78 dispose(DISPOSE_BACKGROUND),
84 unsigned short delay; // delay time in 1/100ths of a sec
85 short transparent : 10; // -1 == not, anything else == index
86 short dispose : 6; // 0, 1, 2, 3 (others invalid)
87 short interlace : 1; // interlaced or not
104 // De-allocate memory of the frame data.
111 uint32_t* data; /* frame decoding data */
112 FrameInfo info; /* special image type info */
116 struct GifAnimationData
127 std::vector<ImageFrame> frames;
134 // Forward declaration
149 isLocalResource(true)
165 bool LoadLocalFile();
166 bool LoadRemoteFile();
169 const char* fileName; /**< The absolute path of the file. */
170 unsigned char* globalMap; /**< A pointer to the entire contents of the file */
171 long long length; /**< The length of the file in bytes. */
172 bool isLocalResource; /**< The flag whether the file is a local resource */
185 int position, length; // yes - gif uses ints for file sizes.
189 GifAnimationData animated;
190 std::unique_ptr<GifAccessor> gifAccessor{nullptr};
195 struct ImageProperties
203 * Class to access gif open/close using riaa
209 * @param[in,out] fileInfo Contains ptr to memory, and is updated by DGifOpen
211 GifAccessor(LoaderInfo::FileInfo& fileInfo)
213 // actually ask libgif to open the file
214 #if GIFLIB_MAJOR >= 5
215 gif = DGifOpen(&fileInfo, FileRead, NULL);
217 gif = DGifOpen(&fileInfo, FileRead);
222 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
230 #if(GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
231 DGifCloseFile(gif, NULL);
239 * @brief Copy data from gif file into buffer.
241 * @param[in] gifFileType A pointer pointing to GIF File Type
242 * @param[out] buffer A pointer to buffer containing GIF raw data
243 * @param[in] len The length in bytes to be copied
244 * @return The data length of the image in bytes
246 static int FileRead(GifFileType* gifFileType, GifByteType* buffer, int length)
248 LoaderInfo::FileInfo* fi = reinterpret_cast<LoaderInfo::FileInfo*>(gifFileType->UserData);
250 if(fi->position >= fi->length)
252 return 0; // if at or past end - no
254 if((fi->position + length) >= fi->length)
256 length = fi->length - fi->position;
258 memcpy(buffer, fi->map + fi->position, length);
259 fi->position += length;
263 GifFileType* gif = nullptr;
266 bool LoaderInfo::FileData::LoadFile()
268 bool success = false;
271 success = LoadLocalFile();
275 success = LoadRemoteFile();
280 bool LoaderInfo::FileData::LoadLocalFile()
282 Internal::Platform::FileReader fileReader(fileName);
283 FILE* fp = fileReader.GetFile();
289 if(fseek(fp, 0, SEEK_END) <= -1)
300 if((!fseek(fp, 0, SEEK_SET)))
302 globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * length));
303 length = fread(globalMap, sizeof(GifByteType), length, fp);
312 bool LoaderInfo::FileData::LoadRemoteFile()
315 bool succeeded = false;
316 Dali::Vector<uint8_t> dataBuffer;
319 succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(fileName, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
322 size_t blobSize = dataBuffer.Size();
325 // Open a file handle on the memory buffer:
326 Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
327 FILE* const fp = fileReader.GetFile();
330 if((!fseek(fp, 0, SEEK_SET)))
332 globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * blobSize));
333 length = fread(globalMap, sizeof(GifByteType), blobSize, fp);
338 DALI_LOG_ERROR("Error seeking within file\n");
343 DALI_LOG_ERROR("Error reading file\n");
352 * @brief This combines R, G, B and Alpha values into a single 32-bit (ABGR) value.
354 * @param[in] animated A structure containing GIF animation data
355 * @param[in] index Frame index to be searched in GIF
356 * @return A pointer to the ImageFrame.
358 inline int CombinePixelABGR(int a, int r, int g, int b)
360 return (((a) << 24) + ((b) << 16) + ((g) << 8) + (r));
363 inline int PixelLookup(ColorMapObject* colorMap, int index)
365 return CombinePixelABGR(0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue);
369 * @brief Brute force find frame index - gifs are normally small so ok for now.
371 * @param[in] animated A structure containing GIF animation data
372 * @param[in] index Frame index to be searched in GIF
373 * @return A pointer to the ImageFrame.
375 ImageFrame* FindFrame(const GifAnimationData& animated, int index)
377 for(auto&& elem : animated.frames)
379 if(elem.index == index)
381 return const_cast<ImageFrame*>(&elem);
388 * @brief Fill in an image with a specific rgba color value.
390 * @param[in] data A pointer pointing to an image data
391 * @param[in] row A int containing the number of rows in an image
392 * @param[in] val A uint32_t containing rgba color value
393 * @param[in] x X-coordinate used an offset to calculate pixel position
394 * @param[in] y Y-coordinate used an offset to calculate pixel position
395 * @param[in] width Width of the image
396 * @param[in] height Height of the image
398 void FillImage(uint32_t* data, int row, uint32_t val, int x, int y, int width, int height)
401 uint32_t* pixelPosition;
403 for(yAxis = 0; yAxis < height; yAxis++)
405 pixelPosition = data + ((y + yAxis) * row) + x;
406 for(xAxis = 0; xAxis < width; xAxis++)
408 *pixelPosition = val;
415 * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
417 * @param[in] data A pointer pointing to an image data
418 * @param[in] row A int containing the number of rows in an image
419 * @param[in] gif A pointer pointing to GIF File Type
420 * @param[in] frameInfo A pointer pointing to Frame Information data
421 * @param[in] x X-coordinate used an offset to calculate pixel position
422 * @param[in] y Y-coordinate used an offset to calculate pixel position
423 * @param[in] width Width of the image
424 * @param[in] height Height of the image
426 void FillFrame(uint32_t* data, int row, GifFileType* gif, FrameInfo* frameInfo, int x, int y, int w, int h)
428 // solid color fill for pre frame region
429 if(frameInfo->transparent < 0)
431 ColorMapObject* colorMap;
434 // work out color to use from colorMap
435 if(gif->Image.ColorMap)
437 colorMap = gif->Image.ColorMap;
441 colorMap = gif->SColorMap;
443 backGroundColor = gif->SBackGroundColor;
445 FillImage(data, row, CombinePixelABGR(0xff, colorMap->Colors[backGroundColor].Red, colorMap->Colors[backGroundColor].Green, colorMap->Colors[backGroundColor].Blue), x, y, w, h);
447 // fill in region with 0 (transparent)
450 FillImage(data, row, 0, x, y, w, h);
455 * @brief Store common fields from gif file info into frame info
457 * @param[in] gif A pointer pointing to GIF File Type
458 * @param[in] frameInfo A pointer pointing to Frame Information data
460 void StoreFrameInfo(GifFileType* gif, FrameInfo* frameInfo)
462 frameInfo->x = gif->Image.Left;
463 frameInfo->y = gif->Image.Top;
464 frameInfo->w = gif->Image.Width;
465 frameInfo->h = gif->Image.Height;
466 frameInfo->interlace = gif->Image.Interlace;
470 * @brief Check if image fills "screen space" and if so, if it is transparent
471 * at all then the image could be transparent - OR if image doesnt fill,
472 * then it could be trasnparent (full coverage of screen). Some gifs will
473 * be recognized as solid here for faster rendering, but not all.
475 * @param[out] full A boolean to show whether image is transparent or not
476 * @param[in] frameInfo A pointer pointing to Frame Information data
477 * @param[in] width Width of the image
478 * @param[in] height Height of the image
480 void CheckTransparency(bool& full, FrameInfo* frameInfo, int width, int height)
482 if((frameInfo->x == 0) && (frameInfo->y == 0) &&
483 (frameInfo->w == width) && (frameInfo->h == height))
485 if(frameInfo->transparent >= 0)
497 * @brief Fix coords and work out an x and y inset in orig data if out of image bounds.
499 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)
507 if((x0 + w0) > imageWidth)
509 w0 = imageWidth - x0;
517 if((y0 + h0) > imageHeight)
519 h0 = imageHeight - y0;
528 * @brief Flush out rgba frame images to save memory but skip current,
529 * previous and lastPreservedFrame frames (needed for dispose mode DISPOSE_PREVIOUS)
531 * @param[in] animated A structure containing GIF animation data
532 * @param[in] width Width of the image
533 * @param[in] height Height of the image
534 * @param[in] thisframe The current frame
535 * @param[in] prevframe The previous frame
536 * @param[in] lastPreservedFrame The last preserved frame
538 void FlushFrames(GifAnimationData& animated, int width, int height, ImageFrame* thisframe, ImageFrame* prevframe, ImageFrame* lastPreservedFrame)
540 DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "FlushFrames() START \n");
542 // target is the amount of memory we want to be under for stored frames
543 int total = 0, target = 512 * 1024;
545 // total up the amount of memory used by stored frames for this image
546 for(auto&& frame : animated.frames)
553 total *= (width * height * sizeof(uint32_t));
555 DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "Total used frame size: %d\n", total);
557 // If we use more than target (512k) for frames - flush
560 // Clean frames (except current and previous) until below target
561 for(auto&& frame : animated.frames)
563 if((frame.index != thisframe->index) && (!prevframe || frame.index != prevframe->index) &&
564 (!lastPreservedFrame || frame.index != lastPreservedFrame->index))
566 if(frame.data != nullptr)
569 frame.data = nullptr;
571 // subtract memory used and if below target - stop flush
572 total -= (width * height * sizeof(uint32_t));
582 DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "FlushFrames() END \n");
586 * @brief allocate frame and frame info and append to list and store fields.
588 * @param[in] animated A structure containing GIF animation data
589 * @param[in] transparent Transparent index of the new frame
590 * @param[in] dispose Dispose mode of new frame
591 * @param[in] delay The frame delay of new frame
592 * @param[in] index The index of new frame
594 FrameInfo* NewFrame(GifAnimationData& animated, int transparent, int dispose, int delay, int index)
598 // record transparent index to be used or -1 if none
599 // for this SPECIFIC frame
600 frame.info.transparent = transparent;
601 // record dispose mode (3 bits)
602 frame.info.dispose = dispose;
603 // record delay (2 bytes so max 65546 /100 sec)
604 frame.info.delay = delay;
605 // record the index number we are at
607 // that frame is stored AT image/screen size
609 animated.frames.push_back(frame);
611 DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "NewFrame: animated.frames.size() = %d\n", animated.frames.size());
613 return &(animated.frames.back().info);
617 * @brief Decode a gif image into rows then expand to 32bit into the destination
620 bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin, int transparent, int x, int y, int w, int h, bool fill)
622 int intoffset[] = {0, 4, 2, 1};
623 int intjump[] = {8, 8, 4, 2};
624 int i, xx, yy, pix, gifW, gifH;
625 GifRowType* rows = NULL;
627 ColorMapObject* colorMap;
630 // what we need is image size.
632 sp = &gif->SavedImages[gif->ImageCount - 1];
634 gifW = sp->ImageDesc.Width;
635 gifH = sp->ImageDesc.Height;
637 if((gifW < w) || (gifH < h))
639 DALI_LOG_ERROR("gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
640 DALI_ASSERT_DEBUG(false && "Dimensions are bigger than the Gif image size");
644 // build a blob of memory to have pointers to rows of pixels
645 // AND store the decoded gif pixels (1 byte per pixel) as welll
646 rows = static_cast<GifRowType*>(malloc((gifH * sizeof(GifRowType)) + (gifW * gifH * sizeof(GifPixelType))));
652 // fill in the pointers at the start
653 for(yy = 0; yy < gifH; yy++)
655 rows[yy] = reinterpret_cast<unsigned char*>(rows) + (gifH * sizeof(GifRowType)) + (yy * gifW * sizeof(GifPixelType));
658 // if gif is interlaced, walk interlace pattern and decode into rows
659 if(gif->Image.Interlace)
661 for(i = 0; i < 4; i++)
663 for(yy = intoffset[i]; yy < gifH; yy += intjump[i])
665 if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
672 // normal top to bottom - decode into rows
675 for(yy = 0; yy < gifH; yy++)
677 if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
684 // work out what colormap to use
685 if(gif->Image.ColorMap)
687 colorMap = gif->Image.ColorMap;
691 colorMap = gif->SColorMap;
694 // if we need to deal with transparent pixels at all...
697 // if we are told to FILL (overwrite with transparency kept)
700 for(yy = 0; yy < h; yy++)
702 p = data + ((y + yy) * rowpix) + x;
703 for(xx = 0; xx < w; xx++)
705 pix = rows[yin + yy][xin + xx];
706 if(pix != transparent)
708 *p = PixelLookup(colorMap, pix);
718 // paste on top with transparent pixels untouched
721 for(yy = 0; yy < h; yy++)
723 p = data + ((y + yy) * rowpix) + x;
724 for(xx = 0; xx < w; xx++)
726 pix = rows[yin + yy][xin + xx];
727 if(pix != transparent)
729 *p = PixelLookup(colorMap, pix);
738 // walk pixels without worring about transparency at all
739 for(yy = 0; yy < h; yy++)
741 p = data + ((y + yy) * rowpix) + x;
742 for(xx = 0; xx < w; xx++)
744 pix = rows[yin + yy][xin + xx];
745 *p = PixelLookup(colorMap, pix);
761 * @brief Reader header from the gif file and populates structures accordingly.
763 * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
764 * @param[out] prop A ImageProperties structure for storing information about GIF data.
765 * @param[out] error Error code
766 * @return The true or false whether reading was successful or not.
768 bool ReadHeader(LoaderInfo& loaderInfo,
769 ImageProperties& prop, //output struct
772 GifAnimationData& animated = loaderInfo.animated;
773 LoaderInfo::FileData& fileData = loaderInfo.fileData;
774 bool success = false;
775 LoaderInfo::FileInfo fileInfo;
778 // it is possible which gif file have error midle of frames,
779 // in that case we should play gif file until meet error frame.
782 FrameInfo* frameInfo = NULL;
785 success = fileData.LoadFile();
786 if(!success || !fileData.globalMap)
789 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
793 fileInfo.map = fileData.globalMap;
794 fileInfo.length = fileData.length;
795 fileInfo.position = 0;
796 GifAccessor gifAccessor(fileInfo);
800 // get the gif "screen size" (the actual image size)
801 prop.w = gifAccessor.gif->SWidth;
802 prop.h = gifAccessor.gif->SHeight;
804 // if size is invalid - abort here
805 if((prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h))
807 if(IMG_TOO_BIG(prop.w, prop.h))
810 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
815 DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
820 // walk through gif records in file to figure out info
824 if(DGifGetRecordType(gifAccessor.gif, &rec) == GIF_ERROR)
826 // if we have a gif that ends part way through a sequence
827 // (or animation) consider it valid and just break - no error
835 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
840 // get image description section
841 if(rec == IMAGE_DESC_RECORD_TYPE)
847 if(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR)
850 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
853 // skip decoding and just walk image to next
854 if(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR)
857 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
864 DGifGetCodeNext(gifAccessor.gif, &img);
866 // store geometry in the last frame info data
869 StoreFrameInfo(gifAccessor.gif, frameInfo);
870 CheckTransparency(full, frameInfo, prop.w, prop.h);
872 // or if we dont have a frameInfo entry - create one even for stills
875 // allocate and save frame with field data
876 frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
880 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
883 // store geometry info from gif image
884 StoreFrameInfo(gifAccessor.gif, frameInfo);
885 // check for transparency/alpha
886 CheckTransparency(full, frameInfo, prop.w, prop.h);
890 // we have an extension code block - for animated gifs for sure
891 else if(rec == EXTENSION_RECORD_TYPE)
894 GifByteType* ext = NULL;
896 // get the first extension entry
897 DGifGetExtension(gifAccessor.gif, &ext_code, &ext);
900 // graphic control extension - for animated gif data
901 // and transparent index + flag
904 // create frame and store it in image
905 int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
906 int disposeMode = (ext[1] >> 2) & 0x7;
907 int delay = (int(ext[3]) << 8) | int(ext[2]);
908 frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
912 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
916 // netscape extension indicating loop count.
917 else if(ext_code == 0xff) /* application extension */
919 if(!strncmp(reinterpret_cast<char*>(&ext[1]), "NETSCAPE2.0", 11) ||
920 !strncmp(reinterpret_cast<char*>(&ext[1]), "ANIMEXTS1.0", 11))
923 DGifGetExtensionNext(gifAccessor.gif, &ext);
926 loopCount = (int(ext[3]) << 8) | int(ext[2]);
935 // and continue onto the next extension entry
937 DGifGetExtensionNext(gifAccessor.gif, &ext);
940 } while(rec != TERMINATE_RECORD_TYPE && success);
944 // if the gif main says we have more than one image or our image counting
945 // says so, then this image is animated - indicate this
946 if((gifAccessor.gif->ImageCount > 1) || (imageNumber > 1))
948 animated.animated = 1;
949 animated.loopCount = loopCount;
951 animated.frameCount = std::min(gifAccessor.gif->ImageCount, imageNumber);
958 animated.currentFrame = 1;
960 // no errors in header scan etc. so set err and return value
970 * @brief Reader next frame of the gif file and populates structures accordingly.
972 * @param[in,out] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
973 * @param[in,out] prop A ImageProperties structure containing information about gif data.
974 * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
975 * @param[out] error Error code
976 * @return The true or false whether reading was successful or not.
978 bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, // use for w and h
979 unsigned char* pixels,
982 GifAnimationData& animated = loaderInfo.animated;
983 LoaderInfo::FileData& fileData = loaderInfo.fileData;
986 int index = 0, imageNumber = 0;
987 FrameInfo* frameInfo;
988 ImageFrame* frame = NULL;
989 ImageFrame* lastPreservedFrame = NULL;
991 index = animated.currentFrame;
993 // if index is invalid for animated image - error out
994 if((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
996 DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
1000 // find the given frame index
1001 frame = FindFrame(animated, index);
1004 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1007 else if(!(frame->loaded) || !(frame->data))
1009 // if we want to go backwards, we likely need/want to re-decode from the
1010 // start as we have nothing to build on. If there is a gif, imageNumber
1011 // has been set already.
1012 if(loaderInfo.gifAccessor && loaderInfo.imageNumber > 0)
1014 if((index > 0) && (index < loaderInfo.imageNumber) && (animated.animated))
1016 loaderInfo.gifAccessor.reset();
1017 loaderInfo.imageNumber = 0;
1021 // actually ask libgif to open the file
1022 if(!loaderInfo.gifAccessor)
1024 loaderInfo.fileInfo.map = fileData.globalMap;
1025 loaderInfo.fileInfo.length = fileData.length;
1026 loaderInfo.fileInfo.position = 0;
1027 if(!loaderInfo.fileInfo.map)
1029 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1032 std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
1033 if(!gifAccessor->gif)
1035 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1038 loaderInfo.gifAccessor = std::move(gifAccessor);
1039 loaderInfo.imageNumber = 1;
1042 // our current position is the previous frame we decoded from the file
1043 imageNumber = loaderInfo.imageNumber;
1045 // walk through gif records in file to figure out info
1048 if(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR)
1050 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1054 if(rec == EXTENSION_RECORD_TYPE)
1057 GifByteType* ext = NULL;
1058 DGifGetExtension(loaderInfo.gifAccessor->gif, &ext_code, &ext);
1063 DGifGetExtensionNext(loaderInfo.gifAccessor->gif, &ext);
1066 // get image description section
1067 else if(rec == IMAGE_DESC_RECORD_TYPE)
1069 int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
1072 ImageFrame* previousFrame = NULL;
1073 ImageFrame* thisFrame = NULL;
1076 if(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR)
1078 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1082 // get the previous frame entry AND the current one to fill in
1083 previousFrame = FindFrame(animated, imageNumber - 1);
1084 thisFrame = FindFrame(animated, imageNumber);
1086 // if we have a frame AND we're animated AND we have no data...
1087 if((thisFrame) && (!thisFrame->data) && (animated.animated))
1092 thisFrame->data = new uint32_t[prop.w * prop.h];
1094 if(!thisFrame->data)
1096 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1100 // if we have no prior frame OR prior frame data... empty
1101 if((!previousFrame) || (!previousFrame->data))
1104 frameInfo = &(thisFrame->info);
1105 memset(thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t));
1107 // we have a prior frame to copy data from...
1110 frameInfo = &(previousFrame->info);
1112 // fix coords of sub image in case it goes out...
1113 ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1115 // if dispose mode is not restore - then copy pre frame
1116 if(frameInfo->dispose != DISPOSE_PREVIOUS)
1118 memcpy(thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t));
1121 // if dispose mode is "background" then fill with bg
1122 if(frameInfo->dispose == DISPOSE_BACKGROUND)
1124 FillFrame(thisFrame->data, prop.w, loaderInfo.gifAccessor->gif, frameInfo, x, y, w, h);
1126 else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
1131 // Find last preserved frame.
1132 lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
1133 if(!lastPreservedFrame)
1135 DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
1139 } while(lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS);
1141 if(lastPreservedFrame)
1143 memcpy(thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t));
1147 // now draw this frame on top
1148 frameInfo = &(thisFrame->info);
1149 ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1150 if(!DecodeImage(loaderInfo.gifAccessor->gif, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first))
1152 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1156 // mark as loaded and done
1157 thisFrame->loaded = true;
1159 FlushFrames(animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame);
1161 // if we have a frame BUT the image is not animated. different
1163 else if((thisFrame) && (!thisFrame->data) && (!animated.animated))
1165 // if we don't have the data decoded yet - decode it
1166 if((!thisFrame->loaded) || (!thisFrame->data))
1168 // use frame info but we WONT allocate frame pixels
1169 frameInfo = &(thisFrame->info);
1170 ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1172 // clear out all pixels
1173 FillFrame(reinterpret_cast<uint32_t*>(pixels), prop.w, loaderInfo.gifAccessor->gif, frameInfo, 0, 0, prop.w, prop.h);
1175 // and decode the gif with overwriting
1176 if(!DecodeImage(loaderInfo.gifAccessor->gif, reinterpret_cast<uint32_t*>(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true))
1178 DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1182 // mark as loaded and done
1183 thisFrame->loaded = true;
1185 // flush mem we don't need (at expense of decode cpu)
1189 // skip decoding and just walk image to next
1190 if(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR)
1192 DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1199 DGifGetCodeNext(loaderInfo.gifAccessor->gif, &img);
1204 // if we found the image we wanted - get out of here
1205 if(imageNumber > index)
1210 } while(rec != TERMINATE_RECORD_TYPE);
1212 // if we are at the end of the animation or not animated, close file
1213 loaderInfo.imageNumber = imageNumber;
1214 if((animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE))
1216 loaderInfo.gifAccessor.reset();
1217 loaderInfo.imageNumber = 0;
1221 // no errors in header scan etc. so set err and return value
1225 // if it was an animated image we need to copy the data to the
1226 // pixels for the image from the frame holding the data
1227 if(animated.animated && frame->data)
1229 memcpy(pixels, frame->data, prop.w * prop.h * sizeof(uint32_t));
1235 } // unnamed namespace
1237 struct GifLoading::Impl
1240 Impl(const std::string& url, bool isLocalResource)
1242 mLoadSucceeded(true),
1245 loaderInfo.gifAccessor = nullptr;
1247 loaderInfo.fileData.fileName = mUrl.c_str();
1248 loaderInfo.fileData.isLocalResource = isLocalResource;
1250 mLoadSucceeded = ReadHeader(loaderInfo, imageProperties, &error);
1253 // Moveable but not copyable
1254 Impl(const Impl&) = delete;
1255 Impl& operator=(const Impl&) = delete;
1256 Impl(Impl&&) = default;
1257 Impl& operator=(Impl&&) = default;
1260 LoaderInfo loaderInfo;
1261 ImageProperties imageProperties;
1262 bool mLoadSucceeded;
1266 AnimatedImageLoadingPtr GifLoading::New(const std::string& url, bool isLocalResource)
1268 return AnimatedImageLoadingPtr(new GifLoading(url, isLocalResource));
1271 GifLoading::GifLoading(const std::string& url, bool isLocalResource)
1272 : mImpl(new GifLoading::Impl(url, isLocalResource))
1276 GifLoading::~GifLoading()
1281 bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
1285 if(!mImpl->mLoadSucceeded)
1290 Mutex::ScopedLock lock(mImpl->mMutex);
1291 if(!mImpl->mLoadSucceeded)
1296 const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
1298 DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
1300 for(int i = 0; i < count; ++i)
1302 auto pixelBuffer = new unsigned char[bufferSize];
1304 mImpl->loaderInfo.animated.currentFrame = 1 + ((frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount);
1306 if(ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error))
1310 pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
1319 Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
1322 Dali::Devel::PixelBuffer pixelBuffer;
1323 if(!mImpl->mLoadSucceeded)
1328 Mutex::ScopedLock lock(mImpl->mMutex);
1329 DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
1331 pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
1333 mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
1334 ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer.GetBuffer(), &error);
1338 pixelBuffer = Dali::Devel::PixelBuffer();
1343 ImageDimensions GifLoading::GetImageSize() const
1345 return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
1348 uint32_t GifLoading::GetImageCount() const
1350 return mImpl->loaderInfo.animated.frameCount;
1353 uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
1355 return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
1358 std::string GifLoading::GetUrl() const
1363 bool GifLoading::HasLoadingSucceeded() const
1365 return mImpl->mLoadSucceeded;
1368 } // namespace Adaptor
1370 } // namespace Internal