From e63f0b0a7e8b69d21cff6a348153a1dfc293e123 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Tue, 22 Mar 2022 21:56:36 +0900 Subject: [PATCH] Cache gif colormap so make gif-loading faster gif type file only allow to hold 256 kind of color. Previous code check the index of code, and next generate 1 integer. (the function name as PixelLookup) This patch pre-calculate the result of PixelLookup. So we can reduce the bitwise operation + ColorMap access time. Change-Id: I31f00a6a25b6669582d4edf86b2e7bd6d621a020 Signed-off-by: Eunki, Hong --- dali/internal/imaging/common/gif-loading.cpp | 175 ++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 31 deletions(-) diff --git a/dali/internal/imaging/common/gif-loading.cpp b/dali/internal/imaging/common/gif-loading.cpp index a142447..cfb055e 100644 --- a/dali/internal/imaging/common/gif-loading.cpp +++ b/dali/internal/imaging/common/gif-loading.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -61,6 +61,8 @@ Debug::Filter* gGifLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false const int IMG_MAX_SIZE = 65000; constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024; +constexpr int LOCAL_CACHED_COLOR_GENERATE_THRESHOLD = 16; ///< 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 */ @@ -131,6 +133,16 @@ struct GifAnimationData bool animated; }; +struct GifCachedColorData +{ + GifCachedColorData() = default; + + // precalculated colormap table + std::vector globalCachedColor{}; + std::vector localCachedColor{}; + ColorMapObject* localCachedColorMap{nullptr}; // Weak-pointer of ColorMapObject. should be nullptr if image changed +}; + // Forward declaration struct GifAccessor; @@ -187,6 +199,7 @@ struct LoaderInfo FileData fileData; GifAnimationData animated; + GifCachedColorData cachedColor; std::unique_ptr gifAccessor{nullptr}; int imageNumber{0}; FileInfo fileInfo; @@ -617,7 +630,7 @@ FrameInfo* NewFrame(GifAnimationData& animated, int transparent, int dispose, in * @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) { int intoffset[] = {0, 4, 2, 1}; int intjump[] = {8, 8, 4, 2}; @@ -627,6 +640,9 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin, 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]; @@ -685,65 +701,149 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin, if(gif->Image.ColorMap) { colorMap = gif->Image.ColorMap; + // use local cached color map without re-calculate cache. + if(gifCachedColor.localCachedColorMap == colorMap) + { + cachedColorPtr = gifCachedColor.localCachedColor.data(); + } + // else if w * h is big enough, generate local cached color. + else 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); + } + gifCachedColor.localCachedColorMap = colorMap; + + 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 = 0; + } + 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 = 0; + } + 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) + { + // 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 = cachedColorPtr[pix]; + p++; + } + } + } + // we don't have cachedColor. use PixelLookup function. + else { - 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 = PixelLookup(colorMap, pix); + p++; + } } } } @@ -769,9 +869,10 @@ bool ReadHeader(LoaderInfo& loaderInfo, ImageProperties& prop, //output struct int* error) { - 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; @@ -957,6 +1058,18 @@ bool ReadHeader(LoaderInfo& loaderInfo, animated.currentFrame = 1; + // 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); + } + } + cachedColor.localCachedColorMap = nullptr; + // no errors in header scan etc. so set err and return value *error = 0; } @@ -1147,7 +1260,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, // use for w // 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(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first)) { DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n"); return false; @@ -1173,7 +1286,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, // use for w FillFrame(reinterpret_cast(pixels), prop.w, loaderInfo.gifAccessor->gif, frameInfo, 0, 0, prop.w, prop.h); // and decode the gif with overwriting - if(!DecodeImage(loaderInfo.gifAccessor->gif, reinterpret_cast(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true)) + if(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, reinterpret_cast(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true)) { DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n"); return false; -- 2.7.4