/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <cstring>
#include <memory>
+#include <dali/devel-api/threading/mutex.h>
#include <dali/integration-api/debug.h>
#include <dali/internal/imaging/common/file-download.h>
#include <dali/internal/system/common/file-reader.h>
const int IMG_MAX_SIZE = 65000;
constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
+constexpr int LOCAL_CACHED_COLOR_GENERATE_THRESHOLD = 64; ///< Generate color map optimize only if colorCount * threshold < width * height, So we don't loop if image is small
+
#if GIFLIB_MAJOR < 5
const int DISPOSE_BACKGROUND = 2; /* Set area too background color */
const int DISPOSE_PREVIOUS = 3; /* Restore to previous content */
bool animated;
};
+struct GifCachedColorData
+{
+ GifCachedColorData() = default;
+
+ // precalculated colormap table
+ std::vector<std::uint32_t> globalCachedColor{};
+ std::vector<std::uint32_t> localCachedColor{};
+};
+
// Forward declaration
struct GifAccessor;
FileData fileData;
GifAnimationData animated;
+ GifCachedColorData cachedColor;
std::unique_ptr<GifAccessor> gifAccessor{nullptr};
int imageNumber{0};
FileInfo fileInfo;
if(!gif)
{
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
}
}
{
LoaderInfo::FileInfo* fi = reinterpret_cast<LoaderInfo::FileInfo*>(gifFileType->UserData);
- if(fi->position >= fi->length)
+ if(DALI_UNLIKELY(fi->position >= fi->length))
{
return 0; // if at or past end - no
}
{
Internal::Platform::FileReader fileReader(fileName);
FILE* fp = fileReader.GetFile();
- if(fp == NULL)
+ if(DALI_UNLIKELY(fp == NULL))
{
return false;
}
- if(fseek(fp, 0, SEEK_END) <= -1)
+ if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END) <= -1))
{
return false;
}
length = ftell(fp);
- if(length <= -1)
+ if(DALI_UNLIKELY(length <= -1))
{
return false;
}
- if((!fseek(fp, 0, SEEK_SET)))
+ if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
{
globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * length));
length = fread(globalMap, sizeof(GifByteType), length, fp);
size_t dataSize;
succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(fileName, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
- if(succeeded)
+ if(DALI_LIKELY(succeeded))
{
size_t blobSize = dataBuffer.Size();
- if(blobSize > 0U)
+ if(DALI_LIKELY(blobSize > 0U))
{
// Open a file handle on the memory buffer:
Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
FILE* const fp = fileReader.GetFile();
- if(NULL != fp)
+ if(DALI_LIKELY(NULL != fp))
{
- if((!fseek(fp, 0, SEEK_SET)))
+ if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
{
globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * blobSize));
length = fread(globalMap, sizeof(GifByteType), blobSize, fp);
*
* @param[in] animated A structure containing GIF animation data
* @param[in] index Frame index to be searched in GIF
- * @return A pointer to the ImageFrame.
+ * @return single 32-bit (ABGR) value.
*/
-inline int CombinePixelABGR(int a, int r, int g, int b)
+inline std::uint32_t CombinePixelABGR(const std::uint32_t& a, const std::uint32_t& r, const std::uint32_t& g, const std::uint32_t& b)
{
return (((a) << 24) + ((b) << 16) + ((g) << 8) + (r));
}
-inline int PixelLookup(ColorMapObject* colorMap, int index)
+inline std::uint32_t PixelLookup(const ColorMapObject* const& colorMap, int index)
{
return CombinePixelABGR(0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue);
}
/**
+ * @brief Get the Background Color from frameInfo
+ *
+ * @param[in] gif A pointer pointing to GIF File Type
+ * @param[in] frameInfo A pointer pointing to Frame Information data
+ * @return single 32-bit (ABGR) value. of background color
+ */
+std::uint32_t GetBackgroundColor(GifFileType* gif, FrameInfo* frameInfo)
+{
+ if(frameInfo->transparent < 0)
+ {
+ ColorMapObject* colorMap;
+ int backGroundColor;
+
+ // work out color to use from colorMap
+ if(gif->Image.ColorMap)
+ {
+ colorMap = gif->Image.ColorMap;
+ }
+ else
+ {
+ colorMap = gif->SColorMap;
+ }
+ backGroundColor = gif->SBackGroundColor;
+ // Get background color from colormap
+ return PixelLookup(colorMap, backGroundColor);
+ }
+ else
+ {
+ // transparent
+ return 0;
+ }
+}
+
+/**
* @brief Brute force find frame index - gifs are normally small so ok for now.
*
* @param[in] animated A structure containing GIF animation data
* @brief Fill in an image with a specific rgba color value.
*
* @param[in] data A pointer pointing to an image data
- * @param[in] row A int containing the number of rows in an image
+ * @param[in] stride A int containing the number of stride in an image
* @param[in] val A uint32_t containing rgba color value
* @param[in] x X-coordinate used an offset to calculate pixel position
* @param[in] y Y-coordinate used an offset to calculate pixel position
* @param[in] width Width of the image
* @param[in] height Height of the image
*/
-void FillImage(uint32_t* data, int row, uint32_t val, int x, int y, int width, int height)
+void FillImage(uint32_t* data, int stride, uint32_t val, int x, int y, int width, int height)
{
- int xAxis, yAxis;
uint32_t* pixelPosition;
- for(yAxis = 0; yAxis < height; yAxis++)
- {
- pixelPosition = data + ((y + yAxis) * row) + x;
- for(xAxis = 0; xAxis < width; xAxis++)
- {
- *pixelPosition = val;
- pixelPosition++;
- }
- }
-}
-
-/**
- * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
- *
- * @param[in] data A pointer pointing to an image data
- * @param[in] row A int containing the number of rows in an image
- * @param[in] gif A pointer pointing to GIF File Type
- * @param[in] frameInfo A pointer pointing to Frame Information data
- * @param[in] x X-coordinate used an offset to calculate pixel position
- * @param[in] y Y-coordinate used an offset to calculate pixel position
- * @param[in] width Width of the image
- * @param[in] height Height of the image
- */
-void FillFrame(uint32_t* data, int row, GifFileType* gif, FrameInfo* frameInfo, int x, int y, int w, int h)
-{
- // solid color fill for pre frame region
- if(frameInfo->transparent < 0)
+ // Boost time if stride == width and x == 0. We can assume that all pointer is continuous.
+ if(x == 0 && stride == width)
{
- ColorMapObject* colorMap;
- int backGroundColor;
-
- // work out color to use from colorMap
- if(gif->Image.ColorMap)
+ pixelPosition = data + (y * stride);
+ // Clear as white or transparent
+ // Special case. we can use memset.
+ if(val == 0x00 || val == 0xffffffffu)
{
- colorMap = gif->Image.ColorMap;
+ const std::int8_t setupVal = val & 0xff;
+ memset(pixelPosition, setupVal, width * height * sizeof(std::uint32_t));
}
else
{
- colorMap = gif->SColorMap;
+ for(int byteCount = 0; byteCount < width * height; ++byteCount)
+ {
+ *pixelPosition = val;
+ ++pixelPosition;
+ }
}
- backGroundColor = gif->SBackGroundColor;
- // and do the fill
- FillImage(data, row, CombinePixelABGR(0xff, colorMap->Colors[backGroundColor].Red, colorMap->Colors[backGroundColor].Green, colorMap->Colors[backGroundColor].Blue), x, y, w, h);
}
- // fill in region with 0 (transparent)
else
{
- FillImage(data, row, 0, x, y, w, h);
+ for(int yAxis = 0; yAxis < height; ++yAxis)
+ {
+ pixelPosition = data + ((y + yAxis) * stride) + x;
+ for(int xAxis = 0; xAxis < width; ++xAxis)
+ {
+ *pixelPosition = val;
+ ++pixelPosition;
+ }
+ }
}
}
* @brief Decode a gif image into rows then expand to 32bit into the destination
* data pointer.
*/
-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)
+bool DecodeImage(GifFileType* gif, GifCachedColorData& gifCachedColor, uint32_t* data, int rowpix, int xin, int yin, int transparent, int x, int y, int w, int h, bool fill, uint32_t fillColor = 0u)
{
int intoffset[] = {0, 4, 2, 1};
int intjump[] = {8, 8, 4, 2};
ColorMapObject* colorMap;
uint32_t* p;
+ // cached color data.
+ const std::uint32_t* cachedColorPtr = nullptr;
+
// what we need is image size.
SavedImage* sp;
sp = &gif->SavedImages[gif->ImageCount - 1];
gifW = sp->ImageDesc.Width;
gifH = sp->ImageDesc.Height;
- if((gifW < w) || (gifH < h))
+ if(DALI_UNLIKELY((gifW < w) || (gifH < h)))
{
+ DALI_LOG_ERROR("gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
DALI_ASSERT_DEBUG(false && "Dimensions are bigger than the Gif image size");
goto on_error;
}
// build a blob of memory to have pointers to rows of pixels
// AND store the decoded gif pixels (1 byte per pixel) as welll
rows = static_cast<GifRowType*>(malloc((gifH * sizeof(GifRowType)) + (gifW * gifH * sizeof(GifPixelType))));
- if(!rows)
+ if(DALI_UNLIKELY(!rows))
{
goto on_error;
}
{
for(yy = intoffset[i]; yy < gifH; yy += intjump[i])
{
- if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
+ if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
{
goto on_error;
}
{
for(yy = 0; yy < gifH; yy++)
{
- if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
+ if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
{
goto on_error;
}
if(gif->Image.ColorMap)
{
colorMap = gif->Image.ColorMap;
+ // if w * h is big enough, generate local cached color.
+ if(colorMap->ColorCount * LOCAL_CACHED_COLOR_GENERATE_THRESHOLD < w * h)
+ {
+ gifCachedColor.localCachedColor.resize(colorMap->ColorCount);
+ for(i = 0; i < colorMap->ColorCount; ++i)
+ {
+ gifCachedColor.localCachedColor[i] = PixelLookup(colorMap, i);
+ }
+
+ cachedColorPtr = gifCachedColor.localCachedColor.data();
+ }
}
else
{
- colorMap = gif->SColorMap;
+ colorMap = gif->SColorMap;
+ cachedColorPtr = gifCachedColor.globalCachedColor.data();
}
+ // HARD-CODING optimize
// if we need to deal with transparent pixels at all...
if(transparent >= 0)
{
// if we are told to FILL (overwrite with transparency kept)
if(fill)
{
- for(yy = 0; yy < h; yy++)
+ // if we use cachedColor, use it
+ if(cachedColorPtr)
{
- p = data + ((y + yy) * rowpix) + x;
- for(xx = 0; xx < w; xx++)
+ for(yy = 0; yy < h; yy++)
{
- pix = rows[yin + yy][xin + xx];
- if(pix != transparent)
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
{
- *p = PixelLookup(colorMap, pix);
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = cachedColorPtr[pix];
+ }
+ else
+ {
+ *p = fillColor;
+ }
+ p++;
}
- else
+ }
+ }
+ // we don't have cachedColor. use PixelLookup function.
+ else
+ {
+ for(yy = 0; yy < h; yy++)
+ {
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
{
- *p = 0;
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = PixelLookup(colorMap, pix);
+ }
+ else
+ {
+ *p = fillColor;
+ }
+ p++;
}
- p++;
}
}
}
// paste on top with transparent pixels untouched
else
{
- for(yy = 0; yy < h; yy++)
+ // if we use cachedColor, use it
+ if(cachedColorPtr)
{
- p = data + ((y + yy) * rowpix) + x;
- for(xx = 0; xx < w; xx++)
+ for(yy = 0; yy < h; yy++)
{
- pix = rows[yin + yy][xin + xx];
- if(pix != transparent)
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
{
- *p = PixelLookup(colorMap, pix);
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = cachedColorPtr[pix];
+ }
+ p++;
+ }
+ }
+ }
+ // we don't have cachedColor. use PixelLookup function.
+ else
+ {
+ for(yy = 0; yy < h; yy++)
+ {
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
+ {
+ pix = rows[yin + yy][xin + xx];
+ if(pix != transparent)
+ {
+ *p = PixelLookup(colorMap, pix);
+ }
+ p++;
}
- p++;
}
}
}
}
else
{
- // walk pixels without worring about transparency at all
- for(yy = 0; yy < h; yy++)
+ // if we use cachedColor, use it
+ if(cachedColorPtr)
{
- p = data + ((y + yy) * rowpix) + x;
- for(xx = 0; xx < w; xx++)
+ // walk pixels without worring about transparency at all
+ for(yy = 0; yy < h; yy++)
{
- pix = rows[yin + yy][xin + xx];
- *p = PixelLookup(colorMap, pix);
- p++;
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
+ {
+ pix = rows[yin + yy][xin + xx];
+ *p = cachedColorPtr[pix];
+ p++;
+ }
+ }
+ }
+ // we don't have cachedColor. use PixelLookup function.
+ else
+ {
+ // walk pixels without worring about transparency at all
+ for(yy = 0; yy < h; yy++)
+ {
+ p = data + ((y + yy) * rowpix) + x;
+ for(xx = 0; xx < w; xx++)
+ {
+ pix = rows[yin + yy][xin + xx];
+ *p = PixelLookup(colorMap, pix);
+ p++;
+ }
}
}
}
*
* @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
* @param[out] prop A ImageProperties structure for storing information about GIF data.
- * @param[out] error Error code
* @return The true or false whether reading was successful or not.
*/
bool ReadHeader(LoaderInfo& loaderInfo,
- ImageProperties& prop, //output struct
- int* error)
+ ImageProperties& prop)
{
- GifAnimationData& animated = loaderInfo.animated;
- LoaderInfo::FileData& fileData = loaderInfo.fileData;
- bool success = false;
+ GifAnimationData& animated = loaderInfo.animated;
+ GifCachedColorData& cachedColor = loaderInfo.cachedColor;
+ LoaderInfo::FileData& fileData = loaderInfo.fileData;
+ bool success = false;
LoaderInfo::FileInfo fileInfo;
GifRecordType rec;
bool full = true;
success = fileData.LoadFile();
- if(!success || !fileData.globalMap)
+ if(DALI_UNLIKELY(!success || !fileData.globalMap))
{
success = false;
- DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
+ DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
}
else
{
prop.h = gifAccessor.gif->SHeight;
// if size is invalid - abort here
- if((prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h))
+ if(DALI_UNLIKELY((prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h)))
{
if(IMG_TOO_BIG(prop.w, prop.h))
{
else
{
success = false;
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
}
break;
}
GifByteType* img;
// get image desc
- if(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR))
{
success = false;
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
break;
}
// skip decoding and just walk image to next
- if(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR))
{
success = false;
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
break;
}
// skip till next...
{
// allocate and save frame with field data
frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
- if(!frameInfo)
+ if(DALI_UNLIKELY(!frameInfo))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
int disposeMode = (ext[1] >> 2) & 0x7;
int delay = (int(ext[3]) << 8) | int(ext[2]);
frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
- if(!frameInfo)
+ if(DALI_UNLIKELY(!frameInfo))
{
success = false;
DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
animated.currentFrame = 1;
- // no errors in header scan etc. so set err and return value
- *error = 0;
+ // cache global color map
+ ColorMapObject* colorMap = gifAccessor.gif->SColorMap;
+ if(colorMap)
+ {
+ cachedColor.globalCachedColor.resize(colorMap->ColorCount);
+ for(int i = 0; i < colorMap->ColorCount; ++i)
+ {
+ cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
+ }
+ }
}
}
}
index = animated.currentFrame;
// if index is invalid for animated image - error out
- if((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
+ if(DALI_UNLIKELY((animated.animated) && ((index <= 0) || (index > animated.frameCount))))
{
DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
return false;
// find the given frame index
frame = FindFrame(animated, index);
- if(!frame)
+ if(DALI_UNLIKELY(!frame))
{
- DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
+ DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
}
else if(!(frame->loaded) || !(frame->data))
loaderInfo.fileInfo.map = fileData.globalMap;
loaderInfo.fileInfo.length = fileData.length;
loaderInfo.fileInfo.position = 0;
- if(!loaderInfo.fileInfo.map)
+ if(DALI_UNLIKELY(!loaderInfo.fileInfo.map))
{
- DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
+ DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
}
std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
- if(!gifAccessor->gif)
+ if(DALI_UNLIKELY(!gifAccessor->gif))
{
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
}
loaderInfo.gifAccessor = std::move(gifAccessor);
// walk through gif records in file to figure out info
do
{
- if(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR))
{
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
}
ImageFrame* thisFrame = NULL;
// get image desc
- if(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR))
{
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
}
// allocate it
thisFrame->data = new uint32_t[prop.w * prop.h];
- if(!thisFrame->data)
+ if(DALI_UNLIKELY(!thisFrame->data))
{
DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
return false;
}
+ // Lazy fill background color feature.
+ // DecodeImage function draw range is EQUAL with previous FillImage range,
+ // We don't need to fill background that time.
+ // It will try to reduce the number of FillImage API call
+ // Note : We might check overlapping. But that operation looks expensive
+ // So, just optimize only if EQUAL case.
+ bool updateBackgroundColorLazy = false;
+ uint32_t backgroundColor = 0u;
+ int prevX = 0;
+ int prevY = 0;
+ int prevW = 0;
+ int prevH = 0;
+
// if we have no prior frame OR prior frame data... empty
if((!previousFrame) || (!previousFrame->data))
{
- first = true;
- frameInfo = &(thisFrame->info);
- memset(thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t));
+ first = true;
+ frameInfo = &(thisFrame->info);
+ updateBackgroundColorLazy = true;
+ backgroundColor = 0u;
+ prevX = 0;
+ prevY = 0;
+ prevW = prop.w;
+ prevH = prop.h;
}
// we have a prior frame to copy data from...
else
// if dispose mode is "background" then fill with bg
if(frameInfo->dispose == DISPOSE_BACKGROUND)
{
- FillFrame(thisFrame->data, prop.w, loaderInfo.gifAccessor->gif, frameInfo, x, y, w, h);
+ updateBackgroundColorLazy = true;
+ backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
+ prevX = x;
+ prevY = y;
+ prevW = w;
+ prevH = h;
}
else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
{
{
// Find last preserved frame.
lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
- if(!lastPreservedFrame)
+ if(DALI_UNLIKELY(!lastPreservedFrame))
{
DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
return false;
// now draw this frame on top
frameInfo = &(thisFrame->info);
ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
- if(!DecodeImage(loaderInfo.gifAccessor->gif, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first))
+
+ if(updateBackgroundColorLazy)
{
- DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
+ // If this frame's x,y,w,h is not equal with previous x,y,w,h, FillImage. else, don't fill
+ if(prevX != x || prevY != y || prevW != w || prevH != h)
+ {
+ FillImage(thisFrame->data, prop.w, backgroundColor, prevX, prevY, prevW, prevH);
+ // Don't send background color information to DecodeImage function.
+ updateBackgroundColorLazy = false;
+ }
+ }
+ if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first || updateBackgroundColorLazy, backgroundColor)))
+ {
+ DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
}
frameInfo = &(thisFrame->info);
ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
- // clear out all pixels
- FillFrame(reinterpret_cast<uint32_t*>(pixels), prop.w, loaderInfo.gifAccessor->gif, frameInfo, 0, 0, prop.w, prop.h);
+ // clear out all pixels only if x,y,w,h is not whole image.
+ if(x != 0 || y != 0 || w != static_cast<int>(prop.w) || h != static_cast<int>(prop.h))
+ {
+ const std::uint32_t backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
+ FillImage(reinterpret_cast<uint32_t*>(pixels), prop.w, backgroundColor, 0, 0, prop.w, prop.h);
+ }
// and decode the gif with overwriting
- if(!DecodeImage(loaderInfo.gifAccessor->gif, reinterpret_cast<uint32_t*>(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true))
+ if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, reinterpret_cast<uint32_t*>(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true, 0u)))
{
- DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE");
+ DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
return false;
}
else
{
// skip decoding and just walk image to next
- if(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR)
+ if(DALI_UNLIKELY(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR))
{
- DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT");
+ DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
return false;
}
{
public:
Impl(const std::string& url, bool isLocalResource)
- : mUrl(url)
+ : mUrl(url),
+ mLoadSucceeded(false),
+ mMutex()
{
loaderInfo.gifAccessor = nullptr;
- int error;
loaderInfo.fileData.fileName = mUrl.c_str();
loaderInfo.fileData.isLocalResource = isLocalResource;
+ }
- ReadHeader(loaderInfo, imageProperties, &error);
+ bool LoadGifInformation()
+ {
+ // Block to do not load this file again.
+ Mutex::ScopedLock lock(mMutex);
+ if(DALI_LIKELY(mLoadSucceeded))
+ {
+ return mLoadSucceeded;
+ }
+
+ mLoadSucceeded = ReadHeader(loaderInfo, imageProperties);
+ return mLoadSucceeded;
}
// Moveable but not copyable
std::string mUrl;
LoaderInfo loaderInfo;
ImageProperties imageProperties;
+ bool mLoadSucceeded;
+ Mutex mMutex;
};
AnimatedImageLoadingPtr GifLoading::New(const std::string& url, bool isLocalResource)
int error;
bool ret = false;
+ if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
+ {
+ return false;
+ }
+
+ Mutex::ScopedLock lock(mImpl->mMutex);
const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
+ // If Gif file is still not loaded, Load the information.
+ if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
+ {
+ return pixelBuffer;
+ }
+
+ Mutex::ScopedLock lock(mImpl->mMutex);
pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
ImageDimensions GifLoading::GetImageSize() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
}
uint32_t GifLoading::GetImageCount() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
return mImpl->loaderInfo.animated.frameCount;
}
uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
{
- return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
+
+ uint32_t interval = 0u;
+ if(DALI_LIKELY(mImpl->mLoadSucceeded))
+ {
+ interval = mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+ }
+ return interval;
}
std::string GifLoading::GetUrl() const
return mImpl->mUrl;
}
+bool GifLoading::HasLoadingSucceeded() const
+{
+ return mImpl->mLoadSucceeded;
+}
+
} // namespace Adaptor
} // namespace Internal