[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / npatch-loader.cpp
index e89ebcb..fb844ce 100644 (file)
@@ -1,5 +1,5 @@
- /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2023 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.
 // CLASS HEADER
 #include <dali-toolkit/internal/visuals/npatch-loader.h>
 
-// EXTERNAL HEADER
-#include <dali/devel-api/adaptor-framework/image-loading.h>
-#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/rendering-addon.h>
+
+// EXTERNAL HEADERS
 #include <dali/devel-api/common/hash.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
 
 namespace Dali
 {
-
 namespace Toolkit
 {
-
 namespace Internal
 {
+namespace
+{
+constexpr auto INVALID_CACHE_INDEX = int32_t{-1}; ///< Invalid Cache index
+constexpr auto UNINITIALIZED_ID    = int32_t{0};  ///< uninitialised id, use to initialize ids
 
-namespace NPatchBuffer
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false);
+} // Anonymous namespace
+
+NPatchLoader::NPatchLoader()
+: mCurrentNPatchDataId(0),
+  mRemoveProcessorRegistered(false)
 {
+}
 
-void GetRedOffsetAndMask( Dali::Pixel::Format pixelFormat, int& byteOffset, int& bitMask )
+NPatchLoader::~NPatchLoader()
 {
-  switch( pixelFormat )
+  if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
   {
-    case Dali::Pixel::A8:
-    case Dali::Pixel::L8:
-    case Dali::Pixel::LA88:
-    {
-      byteOffset = 0;
-      bitMask = 0;
-      break;
-    }
-    case Dali::Pixel::RGB888:
-    case Dali::Pixel::RGB8888:
-    case Dali::Pixel::RGBA8888:
-    {
-      byteOffset = 0;
-      bitMask = 0xFF;
-      break;
-    }
-    case Dali::Pixel::BGR8888:
-    case Dali::Pixel::BGRA8888:
-    {
-      byteOffset = 2;
-      bitMask = 0xff;
-      break;
-    }
-    case Dali::Pixel::RGB565:
-    {
-      byteOffset = 0;
-      bitMask = 0xf8;
-      break;
-    }
-    case Dali::Pixel::BGR565:
-    {
-      byteOffset = 1;
-      bitMask = 0x1f;
-      break;
-    }
-    case Dali::Pixel::RGBA4444:
-    {
-      byteOffset = 0;
-      bitMask = 0xf0;
-      break;
-    }
-    case Dali::Pixel::BGRA4444:
-    {
-      byteOffset = 1;
-      bitMask = 0xf0;
-      break;
-    }
-    case Dali::Pixel::RGBA5551:
+    Adaptor::Get().UnregisterProcessor(*this, true);
+    mRemoveProcessorRegistered = false;
+  }
+}
+
+NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
+{
+  // Skip invalid id generation.
+  if(DALI_UNLIKELY(mCurrentNPatchDataId == NPatchData::INVALID_NPATCH_DATA_ID))
+  {
+    mCurrentNPatchDataId = 0;
+  }
+  return mCurrentNPatchDataId++;
+}
+
+NPatchData::NPatchDataId NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
+{
+  NPatchDataPtr data = GetNPatchData(url, border, preMultiplyOnLoad);
+
+  DALI_ASSERT_ALWAYS(data.Get() && "NPatchData creation failed!");
+
+  if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
+  {
+    if(!synchronousLoading)
     {
-      byteOffset = 0;
-      bitMask = 0xf8;
-      break;
+      // NotifyObserver already done, so
+      // data will not iterate observer list.
+      // We need to call LoadComplete directly.
+      data->NotifyObserver(textureObserver, true);
     }
-    case Dali::Pixel::BGRA5551:
+  }
+  else // if NOT_STARTED or LOADING or LOAD_FAILED, try to reload.
+  {
+    if(!synchronousLoading)
     {
-      byteOffset = 1;
-      bitMask = 0x1e;
-      break;
+      data->AddObserver(textureObserver);
+      // If still LOADING and async, don't need to request reload. Fast return.
+      if(data->GetLoadingState() == NPatchData::LoadingState::LOADING)
+      {
+        return data->GetId();
+      }
     }
-    case Dali::Pixel::INVALID:
-    case Dali::Pixel::COMPRESSED_R11_EAC:
-    case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC:
-    case Dali::Pixel::COMPRESSED_RG11_EAC:
-    case Dali::Pixel::COMPRESSED_SIGNED_RG11_EAC:
-    case Dali::Pixel::COMPRESSED_RGB8_ETC2:
-    case Dali::Pixel::COMPRESSED_SRGB8_ETC2:
-    case Dali::Pixel::COMPRESSED_RGB8_ETC1:
-    case Dali::Pixel::COMPRESSED_RGB_PVRTC_4BPPV1:
-    case Dali::Pixel::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
-    case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
-    case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR:
-    case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
-    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
+
+    data->SetLoadingState(NPatchData::LoadingState::LOADING);
+
+    auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
+                                                  : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
+    Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data.Get(), true, preMultiplyOnLoading);
+
+    if(pixelBuffer)
     {
-      DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
-      byteOffset=0;
-      bitMask=0;
-      break;
+      preMultiplyOnLoad = (preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) ? true : false;
+      data->SetLoadedNPatchData(pixelBuffer, preMultiplyOnLoad);
     }
-    case Dali::Pixel::RGB16F:
-    case Dali::Pixel::RGB32F:
+    else if(synchronousLoading)
     {
-      DALI_LOG_ERROR("Pixel format not compatible.\n");
-      byteOffset=0;
-      bitMask=0;
-      break;
+      data->SetLoadingState(NPatchData::LoadingState::LOAD_FAILED);
     }
   }
+  return data->GetId();
 }
 
-Uint16Pair ParseRange( unsigned int& index, unsigned int width, unsigned char*& pixel, unsigned int pixelStride, int testByte, int testBits, int testValue )
+int32_t NPatchLoader::GetCacheIndexFromId(const NPatchData::NPatchDataId id)
 {
-  unsigned int start = 0xFFFF;
-  for( ; index < width; ++index, pixel += pixelStride )
-  {
-    if( ( pixel[ testByte ] & testBits ) == testValue )
-    {
-        start = index;
-        ++index;
-        pixel += pixelStride;
-        break;
-    }
-  }
+  const unsigned int size = mCache.size();
 
-  unsigned int end = width;
-  for( ; index < width; ++index, pixel += pixelStride )
+  for(unsigned int i = 0; i < size; ++i)
   {
-    if( ( pixel[ testByte ] & testBits ) != testValue )
+    if(mCache[i].mData->GetId() == id)
     {
-        end = index;
-        ++index;
-        pixel += pixelStride;
-        break;
+      return i;
     }
   }
 
-  return Uint16Pair( start, end );
+  return INVALID_CACHE_INDEX;
 }
 
-void ParseBorders( Devel::PixelBuffer& pixelBuffer, NPatchLoader::Data* data  )
+bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, NPatchDataPtr& data)
 {
-  data->stretchPixelsX.Clear();
-  data->stretchPixelsY.Clear();
-
-  Pixel::Format pixelFormat = pixelBuffer.GetPixelFormat();
-
-  int alphaByte = 0;
-  int alphaBits = 0;
-  Pixel::GetAlphaOffsetAndMask( pixelFormat, alphaByte, alphaBits );
-
-  int testByte = alphaByte;
-  int testBits = alphaBits;
-  int testValue = alphaBits; // Opaque == stretch
-  if( !alphaBits )
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    GetRedOffsetAndMask( pixelFormat, testByte, testBits );
-    testValue = 0;           // Black == stretch
+    data = mCache[cacheIndex].mData;
+    return true;
   }
+  data = nullptr;
+  return false;
+}
 
-  unsigned int bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
-  unsigned int width = pixelBuffer.GetWidth();
-  unsigned int height = pixelBuffer.GetHeight();
-  unsigned char* srcPixels = pixelBuffer.GetBuffer();
-  unsigned int srcStride = width * bytesPerPixel;
-
-  // TOP
-  unsigned char* top = srcPixels + bytesPerPixel;
-  unsigned int index = 0;
-
-  for( ; index < width - 2; )
+void NPatchLoader::RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
+{
+  // Remove observer first
+  if(textureObserver)
   {
-    Uint16Pair range = ParseRange( index, width - 2, top, bytesPerPixel, testByte, testBits, testValue );
-    if( range.GetX() != 0xFFFF )
+    int32_t cacheIndex = GetCacheIndexFromId(id);
+    if(cacheIndex != INVALID_CACHE_INDEX)
     {
-      data->stretchPixelsX.PushBack( range );
+      NPatchInfo& info(mCache[cacheIndex]);
+
+      info.mData->RemoveObserver(textureObserver);
     }
   }
 
-  // LEFT
-  unsigned char* left  = srcPixels + srcStride;
-  index = 0;
-  for( ; index < height - 2; )
+  mRemoveQueue.push_back({id, nullptr});
+
+  if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
   {
-    Uint16Pair range = ParseRange( index, height - 2, left, srcStride, testByte, testBits, testValue );
-    if( range.GetX() != 0xFFFF )
-    {
-      data->stretchPixelsY.PushBack( range );
-    }
+    mRemoveProcessorRegistered = true;
+    Adaptor::Get().RegisterProcessor(*this, true);
   }
+}
 
-  // If there are no stretch pixels then make the entire image stretchable
-  if( data->stretchPixelsX.Size() == 0 )
+void NPatchLoader::Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
+{
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if(cacheIndex == INVALID_CACHE_INDEX)
   {
-    data->stretchPixelsX.PushBack( Uint16Pair( 0, width - 2 ) );
+    return;
   }
-  if( data->stretchPixelsY.Size() == 0 )
+
+  NPatchInfo& info(mCache[cacheIndex]);
+
+  info.mData->RemoveObserver(textureObserver);
+
+  if(--info.mReferenceCount <= 0)
   {
-    data->stretchPixelsY.PushBack( Uint16Pair( 0, height - 2 ) );
+    mCache.erase(mCache.begin() + cacheIndex);
   }
 }
 
-} // namespace NPatchBuffer
-
-NPatchLoader::NPatchLoader()
+void NPatchLoader::Process(bool postProcessor)
 {
-}
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_NPATCH_LOADER_PROCESS_REMOVE_QUEUE", [&](std::ostringstream& oss) {
+    oss << "[" << mRemoveQueue.size() << "]";
+  });
 
-NPatchLoader::~NPatchLoader()
-{
+  for(auto& iter : mRemoveQueue)
+  {
+    Remove(iter.first, iter.second);
+  }
+
+  mRemoveQueue.clear();
+
+  if(Adaptor::IsAvailable())
+  {
+    Adaptor::Get().UnregisterProcessor(*this, true);
+    mRemoveProcessorRegistered = false;
+  }
+
+  DALI_TRACE_END(gTraceFilter, "DALI_NPATCH_LOADER_PROCESS_REMOVE_QUEUE");
 }
 
-std::size_t NPatchLoader::Load( const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad )
+NPatchDataPtr NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
 {
-  std::size_t hash = CalculateHash( url );
-  OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID;
-  const OwnerContainer< Data* >::SizeType count = mCache.Count();
-  int cachedIndex = -1;
+  std::size_t                              hash  = CalculateHash(url.GetUrl());
+  std::vector<NPatchInfo>::size_type       index = UNINITIALIZED_ID;
+  const std::vector<NPatchInfo>::size_type count = mCache.size();
+
+  NPatchInfo* infoPtr = nullptr;
 
-  for( ; index < count; ++index )
+  for(; index < count; ++index)
   {
-    if( mCache[ index ]->hash == hash )
+    if(mCache[index].mData->GetHash() == hash)
     {
       // hash match, check url as well in case of hash collision
-      if( mCache[ index ]->url == url )
+      if(mCache[index].mData->GetUrl().GetUrl() == url.GetUrl())
       {
-        // Use cached data
-        if( mCache[ index ]->border == border )
+        // Use cached data. Need to fast-out return.
+        if(mCache[index].mData->GetBorder() == border)
         {
-          return index+1u; // valid indices are from 1 onwards
+          mCache[index].mReferenceCount++;
+          return mCache[index].mData;
         }
         else
         {
-          cachedIndex = index;
+          if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
+          {
+            // If we only found LOAD_FAILED case, replace current data. We can reuse texture
+            if(infoPtr == nullptr || infoPtr->mData->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE)
+            {
+              infoPtr = &mCache[index];
+            }
+          }
+          // Still loading pixel buffer. We cannot reuse cached texture yet. Skip checking
+          else if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOADING)
+          {
+            continue;
+          }
+          // if LOAD_FAILED, reuse this cached NPatchData, and try to load again.
+          else
+          {
+            if(infoPtr == nullptr)
+            {
+              infoPtr = &mCache[index];
+            }
+          }
         }
       }
     }
   }
 
-  if( cachedIndex != -1 )
+  // If this is new image loading, make new cache data
+  if(infoPtr == nullptr)
   {
-    // Same url but border is different - use the existing texture
-    Data* data = new Data();
-    data->hash = hash;
-    data->url = url;
-    data->croppedWidth = mCache[ cachedIndex ]->croppedWidth;
-    data->croppedHeight = mCache[ cachedIndex ]->croppedHeight;
-
-    data->textureSet = mCache[ cachedIndex ]->textureSet;
-
-    StretchRanges stretchRangesX;
-    stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) );
-
-    StretchRanges stretchRangesY;
-    stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) );
-
-    data->stretchPixelsX = stretchRangesX;
-    data->stretchPixelsY = stretchRangesY;
-    data->border = border;
-
-    mCache.PushBack( data );
-
-    return mCache.Count(); // valid ids start from 1u
+    NPatchInfo info(new NPatchData());
+    info.mData->SetId(GenerateUniqueNPatchDataId());
+    info.mData->SetHash(hash);
+    info.mData->SetUrl(url);
+    info.mData->SetBorder(border);
+    info.mData->SetPreMultiplyOnLoad(preMultiplyOnLoad);
+
+    mCache.emplace_back(std::move(info));
+    infoPtr = &mCache.back();
   }
-
-  // got to the end so no match, decode N patch and append new item to cache
-  Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromFile( url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
-  if( pixelBuffer )
+  // Else if LOAD_COMPLETE, Same url but border is different - use the existing texture
+  else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
-    Data* data = new Data();
-    data->hash = hash;
-    data->url = url;
+    NPatchInfo info(new NPatchData());
 
-    if( border == Rect< int >( 0, 0, 0, 0 ) )
-    {
-      NPatchBuffer::ParseBorders( pixelBuffer, data );
+    info.mData->SetId(GenerateUniqueNPatchDataId());
+    info.mData->SetHash(hash);
+    info.mData->SetUrl(url);
+    info.mData->SetCroppedWidth(infoPtr->mData->GetCroppedWidth());
+    info.mData->SetCroppedHeight(infoPtr->mData->GetCroppedHeight());
 
-      data->border = Rect< int >( 0, 0, 0, 0 );
+    info.mData->SetTextures(infoPtr->mData->GetTextures());
 
-      // Crop the image
-      pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
-    }
-    else
-    {
-      data->stretchPixelsX.PushBack( Uint16Pair( border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( border.right )) ? pixelBuffer.GetWidth() - border.right : 0 ) ) );
-      data->stretchPixelsY.PushBack( Uint16Pair( border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( border.bottom )) ? pixelBuffer.GetHeight() - border.bottom : 0 ) ) );
-      data->border = border;
-    }
+    NPatchUtility::StretchRanges stretchRangesX;
+    stretchRangesX.PushBack(Uint16Pair(border.left, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.right)) ? info.mData->GetCroppedHeight() - border.right : 0)));
 
-    data->croppedWidth = pixelBuffer.GetWidth();
-    data->croppedHeight = pixelBuffer.GetHeight();
+    NPatchUtility::StretchRanges stretchRangesY;
+    stretchRangesY.PushBack(Uint16Pair(border.top, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.bottom)) ? info.mData->GetCroppedHeight() - border.bottom : 0)));
 
-    if( preMultiplyOnLoad && Pixel::HasAlpha( pixelBuffer.GetPixelFormat() ) )
-    {
-      pixelBuffer.MultiplyColorByAlpha();
-    }
-    else
-    {
-      preMultiplyOnLoad = false;
-    }
-
-    PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
+    info.mData->SetStretchPixelsX(stretchRangesX);
+    info.mData->SetStretchPixelsY(stretchRangesY);
+    info.mData->SetBorder(border);
 
-    Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
-    texture.Upload( pixels );
+    info.mData->SetPreMultiplyOnLoad(infoPtr->mData->IsPreMultiplied());
 
-    data->textureSet = TextureSet::New();
-    data->textureSet.SetTexture( 0u, texture );
+    info.mData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
 
-    mCache.PushBack( data );
-
-    return mCache.Count(); // valid ids start from 1u
+    mCache.emplace_back(std::move(info));
+    infoPtr = &mCache.back();
   }
-
-  return 0u;
-}
-
-bool NPatchLoader::GetNPatchData( std::size_t id, const Data*& data )
-{
-  if( ( id > UNINITIALIZED_ID )&&( id <= mCache.Count() ) )
+  // Else, LOAD_FAILED. just increase reference so we can reuse it.
+  else
   {
-    data = mCache[ id - 1u ]; // id's start from 1u
-    return true;
+    infoPtr->mReferenceCount++;
   }
-  data = NULL;
-  return false;
+
+  DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
+
+  return infoPtr->mData;
 }
 
 } // namespace Internal