[Tizen] Make non-animated webp file can be loaded. 56/269356/2 accepted/tizen/6.0/unified/20220117.134654 submit/tizen_6.0/20220114.035101
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 13 Jan 2022 05:28:39 +0000 (14:28 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 13 Jan 2022 05:37:25 +0000 (14:37 +0900)
 - Make webp file that has wrong file extension can be loaded as a single image with image-visual.
 - Make webp file that hasn't animated option can be loaded with animated-image-visual without duplicated loading.

NOTE : This webp-loading and loader-webp file sync with version of dali_2.0.52

Change-Id: I5982136c36b8f469b73de5398f12a50ba3fa2781
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali/internal/imaging/common/image-loader.cpp
dali/internal/imaging/common/loader-webp.cpp [new file with mode: 0644]
dali/internal/imaging/common/loader-webp.h [new file with mode: 0644]
dali/internal/imaging/common/webp-loading.cpp
dali/internal/imaging/common/webp-loading.h
dali/internal/imaging/file.list

index b5dcf4b..1b22362 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 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.
@@ -22,6 +22,7 @@
 #include <dali/internal/imaging/common/loader-astc.h>
 #include <dali/internal/imaging/common/loader-bmp.h>
 #include <dali/internal/imaging/common/loader-gif.h>
+#include <dali/internal/imaging/common/loader-webp.h>
 #include <dali/internal/imaging/common/loader-ico.h>
 #include <dali/internal/imaging/common/loader-jpeg.h>
 #include <dali/internal/imaging/common/loader-ktx.h>
@@ -62,6 +63,7 @@ enum FileFormats
   FORMAT_JPEG,
   FORMAT_BMP,
   FORMAT_GIF,
+  FORMAT_WEBP,
   FORMAT_KTX,
   FORMAT_ASTC,
   FORMAT_ICO,
@@ -82,6 +84,7 @@ const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_CO
   { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
   { Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  LoadBmpHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
   { Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  LoadGifHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
+  { Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
   { Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  LoadKtxHeader,  Bitmap::BITMAP_COMPRESSED       },
   { Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED       },
   { Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  LoadIcoHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
@@ -105,6 +108,7 @@ const FormatExtension FORMAT_EXTENSIONS[] =
  { ".jpg",  FORMAT_JPEG },
  { ".bmp",  FORMAT_BMP  },
  { ".gif",  FORMAT_GIF  },
+ { ".webp", FORMAT_WEBP },
  { ".ktx",  FORMAT_KTX  },
  { ".astc", FORMAT_ASTC },
  { ".ico",  FORMAT_ICO  },
diff --git a/dali/internal/imaging/common/loader-webp.cpp b/dali/internal/imaging/common/loader-webp.cpp
new file mode 100644 (file)
index 0000000..8dcdb42
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/internal/imaging/common/loader-webp.h>
+
+// EXTERNAL INCLUDES
+#ifdef DALI_WEBP_AVAILABLE
+#include <webp/decode.h>
+#include <webp/demux.h>
+
+#if WEBP_DEMUX_ABI_VERSION > 0x0101
+#define DALI_ANIMATED_WEBP_ENABLED 1
+#endif
+#endif
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/debug.h>
+#include <cstring>
+#include <memory>
+
+typedef unsigned char WebPByteType;
+
+namespace Dali
+{
+namespace TizenPlatform
+{
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+bool ReadWebPInformation(FILE* const fp, WebPData& webPData)
+{
+  if(fp == NULL)
+  {
+    return false;
+  }
+
+  if(fseek(fp, 0, SEEK_END) <= -1)
+  {
+    return false;
+  }
+  WebPDataInit(&webPData);
+  webPData.size = ftell(fp);
+
+  if((!fseek(fp, 0, SEEK_SET)))
+  {
+    unsigned char* WebPDataBuffer;
+    WebPDataBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * webPData.size));
+    webPData.size  = fread(WebPDataBuffer, sizeof(WebPByteType), webPData.size, fp);
+    webPData.bytes = WebPDataBuffer;
+  }
+  else
+  {
+    return false;
+  }
+  return true;
+}
+
+void ReleaseResource(WebPData& webPData, WebPAnimDecoder* webPAnimDecoder)
+{
+  free((void*)webPData.bytes);
+  webPData.bytes = nullptr;
+  WebPDataInit(&webPData);
+  if(webPAnimDecoder)
+  {
+    WebPAnimDecoderDelete(webPAnimDecoder);
+  }
+}
+
+#endif
+
+bool LoadWebpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
+{
+  FILE* const fp = input.file;
+  if(fp == NULL)
+  {
+    return false;
+  }
+
+  if(fseek(fp, 0, SEEK_END) <= -1)
+  {
+    return false;
+  }
+
+  // If the image is non-animated webp
+#ifdef DALI_WEBP_AVAILABLE
+  size_t webPSize = ftell(fp);
+  if((!fseek(fp, 0, SEEK_SET)))
+  {
+    std::vector<uint8_t> encodedImage;
+    encodedImage.resize(webPSize, 0);
+    size_t readCount = fread(&encodedImage[0], sizeof(uint8_t), encodedImage.size(), fp);
+    if(readCount != encodedImage.size())
+    {
+      return false;
+    }
+    int32_t imageWidth, imageHeight;
+    if(WebPGetInfo(&encodedImage[0], encodedImage.size(), &imageWidth, &imageHeight))
+    {
+      width  = static_cast<uint32_t>(imageWidth);
+      height = static_cast<uint32_t>(imageHeight);
+      return true;
+    }
+  }
+#endif
+
+  // If the image is animated webp
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+  WebPData         webPData;
+  WebPAnimDecoder* webPAnimDecoder = nullptr;
+  WebPAnimInfo     webPAnimInfo;
+  if(ReadWebPInformation(fp, webPData))
+  {
+    WebPAnimDecoderOptions webPAnimDecoderOptions;
+    WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
+    webPAnimDecoderOptions.color_mode = MODE_RGBA;
+    webPAnimDecoder                   = WebPAnimDecoderNew(&webPData, &webPAnimDecoderOptions);
+    if(webPAnimDecoder != nullptr)
+    {
+      WebPAnimDecoderGetInfo(webPAnimDecoder, &webPAnimInfo);
+      width  = webPAnimInfo.canvas_width;
+      height = webPAnimInfo.canvas_height;
+      ReleaseResource(webPData, webPAnimDecoder);
+      return true;
+    }
+  }
+  ReleaseResource(webPData, webPAnimDecoder);
+#endif
+  DALI_LOG_ERROR("WebP file open failed.\n");
+  return false;
+}
+
+bool LoadBitmapFromWebp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
+{
+  FILE* const fp = input.file;
+  if(fp == NULL)
+  {
+    return false;
+  }
+
+  if(fseek(fp, 0, SEEK_END) <= -1)
+  {
+    return false;
+  }
+
+  // If the image is non-animated webp
+#ifdef DALI_WEBP_AVAILABLE
+  size_t webPSize = ftell(fp);
+  if((!fseek(fp, 0, SEEK_SET)))
+  {
+    std::vector<uint8_t> encodedImage;
+    encodedImage.resize(webPSize, 0);
+    size_t readCount = fread(&encodedImage[0], sizeof(uint8_t), encodedImage.size(), fp);
+    if(readCount != encodedImage.size())
+    {
+      DALI_LOG_ERROR("WebP image loading failed.\n");
+      return false;
+    }
+
+    int32_t width, height;
+    if(!WebPGetInfo(&encodedImage[0], encodedImage.size(), &width, &height))
+    {
+      DALI_LOG_ERROR("Cannot retrieve WebP image size information.\n");
+      return false;
+    }
+
+    WebPBitstreamFeatures features;
+    if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(&encodedImage[0], encodedImage.size(), &features))
+    {
+      DALI_LOG_ERROR("Cannot retrieve WebP image features.\n");
+      return false;
+    }
+
+    uint32_t      channelNumber = (features.has_alpha) ? 4 : 3;
+    Pixel::Format pixelFormat   = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
+    bitmap                      = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
+    uint8_t* frameBuffer        = nullptr;
+    if(channelNumber == 4)
+    {
+      frameBuffer = WebPDecodeRGBA(&encodedImage[0], encodedImage.size(), &width, &height);
+    }
+    else
+    {
+      frameBuffer = WebPDecodeRGB(&encodedImage[0], encodedImage.size(), &width, &height);
+    }
+
+    if(frameBuffer != nullptr)
+    {
+      const int32_t bufferSize = width * height * sizeof(uint8_t) * channelNumber;
+      memcpy(bitmap.GetBuffer(), frameBuffer, bufferSize);
+      free((void*)frameBuffer);
+      return true;
+    }
+  }
+#endif
+
+  // If the image is animated webp
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+  WebPData         webPData;
+  WebPAnimDecoder* webPAnimDecoder = nullptr;
+  WebPAnimInfo     webPAnimInfo;
+  if(ReadWebPInformation(fp, webPData))
+  {
+    WebPAnimDecoderOptions webPAnimDecoderOptions;
+    WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
+    webPAnimDecoderOptions.color_mode = MODE_RGBA;
+    webPAnimDecoder                   = WebPAnimDecoderNew(&webPData, &webPAnimDecoderOptions);
+    if(webPAnimDecoder != nullptr)
+    {
+      uint8_t* frameBuffer;
+      int      timestamp;
+      WebPAnimDecoderGetInfo(webPAnimDecoder, &webPAnimInfo);
+      WebPAnimDecoderReset(webPAnimDecoder);
+      WebPAnimDecoderGetNext(webPAnimDecoder, &frameBuffer, &timestamp);
+
+      bitmap                   = Dali::Devel::PixelBuffer::New(webPAnimInfo.canvas_width, webPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
+      const int32_t bufferSize = webPAnimInfo.canvas_width * webPAnimInfo.canvas_height * sizeof(uint32_t);
+      memcpy(bitmap.GetBuffer(), frameBuffer, bufferSize);
+      ReleaseResource(webPData, webPAnimDecoder);
+      return true;
+    }
+  }
+  ReleaseResource(webPData, webPAnimDecoder);
+#endif
+
+  DALI_LOG_ERROR("WebP image loading failed.\n");
+  return false;
+}
+
+} // namespace TizenPlatform
+
+} // namespace Dali
diff --git a/dali/internal/imaging/common/loader-webp.h b/dali/internal/imaging/common/loader-webp.h
new file mode 100644 (file)
index 0000000..782321e
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef DALI_TIZEN_PLATFORM_LOADER_WEBP_H
+#define DALI_TIZEN_PLATFORM_LOADER_WEBP_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/devel-api/adaptor-framework/image-loader-input.h>
+#include <cstdio>
+
+namespace Dali
+{
+namespace Devel
+{
+class PixelBuffer;
+}
+
+namespace TizenPlatform
+{
+class ResourceLoadingClient;
+
+namespace Webp
+{
+const unsigned char MAGIC_BYTE_1 = 0x52;
+const unsigned char MAGIC_BYTE_2 = 0x49;
+} // namespace Webp
+
+/**
+ * Loads the header of a Webp file and fills in the width and height appropriately.
+ * @param[in]  input   Information about the input image (including file pointer)
+ * @param[out] width   Is set with the width of the image
+ * @param[out] height  Is set with the height of the image
+ * @return true if the file's header was read successully, false otherwise
+ */
+bool LoadWebpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height);
+
+/**
+ * Loads the bitmap from a Webp file.  This function checks the header first
+ * and if it is not a Webp file, then it returns straight away.
+ * @note For animated Webps, only the first image is displayed
+ * @param[in]  input  Information about the input image (including file pointer)
+ * @param[out] bitmap The bitmap class where the decoded image will be stored
+ * @return  true if file decoded successfully, false otherwise
+ */
+bool LoadBitmapFromWebp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap);
+
+} // namespace TizenPlatform
+
+} // namespace Dali
+
+#endif // DALI_TIZEN_PLATFORM_LOADER_WEBP_H
index f6e65b0..1c3cd41 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 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 <webp/demux.h>
 
 #if WEBP_DEMUX_ABI_VERSION > 0x0101
-#define DALI_WEBP_ENABLED 1
+#define DALI_ANIMATED_WEBP_ENABLED 1
 #endif
 
 #endif
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/images/pixel-data.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/internal/imaging/common/file-download.h>
+#include <dali/internal/system/common/file-reader.h>
 #include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <cstring>
-#include <dali/internal/imaging/common/file-download.h>
-#include <dali/internal/system/common/file-reader.h>
-#include <dali/devel-api/threading/mutex.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/image-loading.h>
 
 typedef unsigned char WebPByteType;
 
 namespace Dali
 {
-
 namespace Internal
 {
-
 namespace Adaptor
 {
-
 namespace
 {
-
 #if defined(DEBUG_ENABLED)
-Debug::Filter *gWebPLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
+Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
 #endif
 
-constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE  = 50 * 1024 * 1024;
+constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
 
-}
+} // namespace
 
 struct WebPLoading::Impl
 {
 public:
-  Impl( const std::string& url, bool isLocalResource )
-  : mUrl( url ),
-    mMutex()
+  Impl(const std::string& url, bool isLocalResource)
+  : mUrl(url),
+    mFrameCount(1u),
+    mMutex(),
+    mBuffer(nullptr),
+    mBufferSize(0u),
+    mImageSize(),
+    mLoadSucceeded(true)
   {
-#ifdef DALI_WEBP_ENABLED
-    if( ReadWebPInformation( isLocalResource ) )
+    // mFrameCount will be 1 if the input image is non-animated image or animated image with single frame.
+    if(ReadWebPInformation(isLocalResource))
     {
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+      WebPDataInit(&mWebPData);
+      mWebPData.size  = mBufferSize;
+      mWebPData.bytes = mBuffer;
       WebPAnimDecoderOptions webPAnimDecoderOptions;
-      WebPAnimDecoderOptionsInit( &webPAnimDecoderOptions );
+      WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
       webPAnimDecoderOptions.color_mode = MODE_RGBA;
-      mWebPAnimDecoder = WebPAnimDecoderNew( &mWebPData, &webPAnimDecoderOptions );
-      WebPAnimDecoderGetInfo( mWebPAnimDecoder, &mWebPAnimInfo );
-      mTimeStamp.assign( mWebPAnimInfo.frame_count, 0 );
-    }
+      mWebPAnimDecoder                  = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
+      WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
+      mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
+      mFrameCount = mWebPAnimInfo.frame_count;
+      mImageSize  = ImageDimensions(mWebPAnimInfo.canvas_width, mWebPAnimInfo.canvas_height);
+#elif DALI_WEBP_AVAILABLE
+      int32_t imageWidth, imageHeight;
+      if(WebPGetInfo(mBuffer, mBufferSize, &imageWidth, &imageHeight))
+      {
+        mImageSize = ImageDimensions(imageWidth, imageHeight);
+      }
+#endif
+#ifndef DALI_WEBP_AVAILABLE
+      // If the system doesn't support webp, loading will be failed.
+      mFrameCount    = 0u;
+      mLoadSucceeded = false;
 #endif
+    }
+    else
+    {
+      mFrameCount    = 0u;
+      mLoadSucceeded = false;
+      DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str());
+    }
   }
 
-  bool ReadWebPInformation( bool isLocalResource )
+  bool ReadWebPInformation(bool isLocalResource)
   {
-#ifdef DALI_WEBP_ENABLED
-    WebPDataInit( &mWebPData );
-    if( isLocalResource )
+    FILE* fp = nullptr;
+    if(isLocalResource)
     {
-      Internal::Platform::FileReader fileReader( mUrl );
-      FILE *fp = fileReader.GetFile();
-      if( fp == NULL )
+      Internal::Platform::FileReader fileReader(mUrl);
+      fp = fileReader.GetFile();
+      if(fp == nullptr)
       {
         return false;
       }
 
-      if( fseek( fp, 0, SEEK_END ) <= -1 )
+      if(fseek(fp, 0, SEEK_END) <= -1)
       {
         return false;
       }
 
-      mWebPData.size = ftell( fp );
-      if( ( ! fseek( fp, 0, SEEK_SET ) ) )
-      {
-        unsigned char *WebPDataBuffer;
-        WebPDataBuffer = reinterpret_cast<WebPByteType*>( malloc(sizeof( WebPByteType ) * mWebPData.size ) );
-        mWebPData.size = fread( WebPDataBuffer, sizeof( WebPByteType ), mWebPData.size, fp );
-        mWebPData.bytes = WebPDataBuffer;
-      }
-      else
+      mBufferSize = ftell(fp);
+      if(!fseek(fp, 0, SEEK_SET))
       {
-        return false;
+        mBuffer     = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
+        mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
+        return true;
       }
     }
     else
     {
       // remote file
-      bool succeeded;
+      bool                  succeeded;
       Dali::Vector<uint8_t> dataBuffer;
-      size_t dataSize;
+      size_t                dataSize;
 
-      succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory( mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE );
-      if( succeeded )
+      succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
+      if(succeeded)
       {
-        size_t blobSize = dataBuffer.Size();
-        if( blobSize > 0U )
+        mBufferSize = dataBuffer.Size();
+        if(mBufferSize > 0U)
         {
           // Open a file handle on the memory buffer:
-          Dali::Internal::Platform::FileReader fileReader( dataBuffer, blobSize );
-          FILE * const fp = fileReader.GetFile();
-          if ( NULL != fp )
+          Internal::Platform::FileReader fileReader(dataBuffer, mBufferSize);
+          fp = fileReader.GetFile();
+          if(fp != nullptr)
           {
-            if( ( ! fseek( fp, 0, SEEK_SET ) ) )
+            if(!fseek(fp, 0, SEEK_SET))
             {
-              unsigned char *WebPDataBuffer;
-              WebPDataBuffer = reinterpret_cast<WebPByteType*>( malloc(sizeof( WebPByteType ) * blobSize ) );
-              mWebPData.size = fread( WebPDataBuffer, sizeof( WebPByteType ), mWebPData.size, fp );
-              mWebPData.bytes = WebPDataBuffer;
-            }
-            else
-            {
-              DALI_LOG_ERROR( "Error seeking within file\n" );
+              mBuffer     = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
+              mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
+              return true;
             }
           }
-          else
-          {
-            DALI_LOG_ERROR( "Error reading file\n" );
-          }
         }
       }
     }
-    return true;
-#else
     return false;
-#endif
   }
 
-  // Moveable but not copyable
-
-  Impl( const Impl& ) = delete;
-  Impl& operator=( const Impl& ) = delete;
-  Impl( Impl&& ) = default;
-  Impl& operator=( Impl&& ) = default;
-
-  ~Impl()
+  void ReleaseResource()
   {
-#ifdef DALI_WEBP_ENABLED
-    if( &mWebPData != NULL )
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+    if(&mWebPData != nullptr)
     {
-      free( (void*)mWebPData.bytes );
       mWebPData.bytes = nullptr;
-      WebPDataInit( &mWebPData );
+      WebPDataInit(&mWebPData);
     }
-    if( mWebPAnimDecoder )
+    if(mWebPAnimDecoder != nullptr)
     {
       WebPAnimDecoderDelete(mWebPAnimDecoder);
+      mWebPAnimDecoder = nullptr;
     }
 #endif
+    if(mBuffer != nullptr)
+    {
+      free((void*)mBuffer);
+      mBuffer = nullptr;
+    }
   }
 
-  std::string mUrl;
-  std::vector<uint32_t> mTimeStamp;
-  uint32_t mLoadingFrame{0};
-  Mutex mMutex;
+  // Moveable but not copyable
+
+  Impl(const Impl&) = delete;
+  Impl& operator=(const Impl&) = delete;
+  Impl(Impl&&)                 = default;
+  Impl& operator=(Impl&&) = default;
+
+  ~Impl()
+  {
+    ReleaseResource();
+  }
 
-#ifdef DALI_WEBP_ENABLED
-  WebPData mWebPData{0};
+  std::string           mUrl;
+  std::vector<uint32_t> mTimeStamp;
+  uint32_t              mLoadingFrame{0};
+  uint32_t              mFrameCount;
+  Mutex                 mMutex;
+  // For the case the system doesn't support DALI_ANIMATED_WEBP_ENABLED
+  unsigned char*  mBuffer;
+  uint32_t        mBufferSize;
+  ImageDimensions mImageSize;
+  bool            mLoadSucceeded;
+
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+  WebPData         mWebPData{0};
   WebPAnimDecoder* mWebPAnimDecoder{nullptr};
-  WebPAnimInfo mWebPAnimInfo{0};
+  WebPAnimInfo     mWebPAnimInfo{0};
 #endif
 };
 
-AnimatedImageLoadingPtr WebPLoading::New( const std::string &url, bool isLocalResource )
+AnimatedImageLoadingPtr WebPLoading::New(const std::string& url, bool isLocalResource)
 {
-#ifndef DALI_WEBP_ENABLED
-  DALI_LOG_ERROR( "The system does not support Animated WebP format.\n" );
+#ifndef DALI_ANIMATED_WEBP_ENABLED
+  DALI_LOG_ERROR("The system does not support Animated WebP format.\n");
 #endif
-  return AnimatedImageLoadingPtr( new WebPLoading( url, isLocalResource ) );
+  return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource));
 }
 
-WebPLoading::WebPLoading( const std::string &url, bool isLocalResource )
-: mImpl( new WebPLoading::Impl( url, isLocalResource ) )
+WebPLoading::WebPLoading(const std::string& url, bool isLocalResource)
+: mImpl(new WebPLoading::Impl(url, isLocalResource))
 {
 }
 
@@ -209,103 +234,103 @@ WebPLoading::~WebPLoading()
   delete mImpl;
 }
 
-bool WebPLoading::LoadNextNFrames( uint32_t frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
+bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
 {
-#ifdef DALI_WEBP_ENABLED
-  Mutex::ScopedLock lock( mImpl->mMutex );
-  if( frameStartIndex  >= mImpl->mWebPAnimInfo.frame_count )
+  for(int i = 0; i < count; ++i)
   {
-    return false;
+    Dali::Devel::PixelBuffer pixelBuffer = LoadFrame((frameStartIndex + i) % mImpl->mFrameCount);
+    Dali::PixelData          imageData   = Devel::PixelBuffer::Convert(pixelBuffer);
+    pixelData.push_back(imageData);
   }
-
-  DALI_LOG_INFO( gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
-
-  if( mImpl->mLoadingFrame > frameStartIndex  )
+  if(pixelData.size() != static_cast<uint32_t>(count))
   {
-    mImpl->mLoadingFrame = 0;
-    WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
+    return false;
   }
+  return true;
+}
 
-  for( ; mImpl->mLoadingFrame < frameStartIndex ; ++mImpl->mLoadingFrame )
-  {
-    uint8_t* frameBuffer;
-    int timestamp;
-    WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
-    mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
-  }
+Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex)
+{
+  Dali::Devel::PixelBuffer pixelBuffer;
 
-  for( int i = 0; i < count; ++i )
+  // WebPDecodeRGBA is faster than to use demux API for loading non-animated image.
+  // If frame count is 1, use WebPDecodeRGBA api.
+#ifdef DALI_WEBP_AVAILABLE
+  if(mImpl->mFrameCount == 1)
   {
-    const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof( uint32_t );
-    uint8_t* frameBuffer;
-    int timestamp;
-    WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
+    int32_t width, height;
+    if(!WebPGetInfo(mImpl->mBuffer, mImpl->mBufferSize, &width, &height))
+    {
+      return pixelBuffer;
+    }
 
-    auto pixelBuffer = new uint8_t[ bufferSize ];
-    memcpy( pixelBuffer, frameBuffer, bufferSize );
-    mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
+    WebPBitstreamFeatures features;
+    if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(mImpl->mBuffer, mImpl->mBufferSize, &features))
+    {
+      return pixelBuffer;
+    }
 
-    if( pixelBuffer )
+    uint32_t      channelNumber = (features.has_alpha) ? 4 : 3;
+    Pixel::Format pixelFormat   = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
+    pixelBuffer                 = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
+    uint8_t* frameBuffer        = nullptr;
+    if(channelNumber == 4)
     {
-      pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
-                                                 mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height,
-                                                 Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY) );
+      frameBuffer = WebPDecodeRGBA(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
+    }
+    else
+    {
+      frameBuffer = WebPDecodeRGB(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
     }
 
-    mImpl->mLoadingFrame++;
-    if( mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count )
+    if(frameBuffer != nullptr)
     {
-      mImpl->mLoadingFrame = 0;
-      WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
+      const int32_t imageBufferSize = width * height * sizeof(uint8_t) * channelNumber;
+      memcpy(pixelBuffer.GetBuffer(), frameBuffer, imageBufferSize);
+      free((void*)frameBuffer);
     }
+    mImpl->ReleaseResource();
+    return pixelBuffer;
   }
-
-  return true;
-#else
-  return false;
 #endif
-}
 
-Dali::Devel::PixelBuffer WebPLoading::LoadFrame( uint32_t frameIndex )
-{
-  Dali::Devel::PixelBuffer pixelBuffer;
-#ifdef DALI_WEBP_ENABLED
-  Mutex::ScopedLock lock( mImpl->mMutex );
-  if( frameIndex  >= mImpl->mWebPAnimInfo.frame_count )
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+  Mutex::ScopedLock lock(mImpl->mMutex);
+  if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
   {
     return pixelBuffer;
   }
 
-  DALI_LOG_INFO( gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameIndex:%d )\n", frameIndex );
+  DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
 
-  if( mImpl->mLoadingFrame > frameIndex  )
+  if(mImpl->mLoadingFrame > frameIndex)
   {
     mImpl->mLoadingFrame = 0;
-    WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
+    WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
   }
 
-  for( ; mImpl->mLoadingFrame < frameIndex ; ++mImpl->mLoadingFrame )
+  for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
   {
     uint8_t* frameBuffer;
-    int timestamp;
-    WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
+    int      timestamp;
+    WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
   }
 
-  const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof( uint32_t );
-  uint8_t* frameBuffer;
-  int timestamp;
-  WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
+  const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
+  uint8_t*  frameBuffer;
+  int       timestamp;
+  WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
 
-  pixelBuffer = Dali::Devel::PixelBuffer::New( mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888 );
-  memcpy( pixelBuffer.GetBuffer(), frameBuffer, bufferSize );
+  pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
+  memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
   mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
 
   mImpl->mLoadingFrame++;
-  if( mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count )
+  if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
   {
     mImpl->mLoadingFrame = 0;
-    WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
+    WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
   }
 #endif
   return pixelBuffer;
@@ -313,31 +338,24 @@ Dali::Devel::PixelBuffer WebPLoading::LoadFrame( uint32_t frameIndex )
 
 ImageDimensions WebPLoading::GetImageSize() const
 {
-#ifdef DALI_WEBP_ENABLED
-  return ImageDimensions( mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height );
-#else
-  return ImageDimensions();
-#endif
+  return mImpl->mImageSize;
 }
 
 uint32_t WebPLoading::GetImageCount() const
 {
-#ifdef DALI_WEBP_ENABLED
-  return mImpl->mWebPAnimInfo.frame_count;
-#else
-  return 0u;
-#endif
+  return mImpl->mFrameCount;
 }
 
-uint32_t WebPLoading::GetFrameInterval( uint32_t frameIndex ) const
+uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
 {
-  if( frameIndex >= GetImageCount() )
+  // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
+  if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
   {
     return 0u;
   }
   else
   {
-    if( frameIndex > 0 )
+    if(frameIndex > 0)
     {
       return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
     }
index 430cf53..63d1f21 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_WEBP_LOADING_H
 
 /*
- * Copyright (c) 2020 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.
  *
  */
 // EXTERNAL INCLUDES
-#include <cstdint>
-#include <memory>
-#include <dali/public-api/math/rect.h>
 #include <dali/public-api/common/dali-vector.h>
 #include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/math/rect.h>
 #include <dali/public-api/math/uint-16-pair.h>
+#include <cstdint>
+#include <memory>
 
 // INTERNAL INCLUDES
-#include <dali/public-api/dali-adaptor-common.h>
 #include <dali/internal/imaging/common/animated-image-loading-impl.h>
+#include <dali/public-api/dali-adaptor-common.h>
 
 namespace Dali
 {
@@ -36,10 +36,8 @@ typedef Dali::Uint16Pair ImageDimensions;
 
 namespace Internal
 {
-
 namespace Adaptor
 {
-
 /**
  * Class to manage loading frames of an animated webp in small chunks. Lazy initializes only when
  * data is actually needed.
@@ -47,17 +45,16 @@ namespace Adaptor
  * is released. (This is to speed up frame loads, which would otherwise have to re-acquire the
  * data from disk)
  */
-class WebPLoading: public Internal::Adaptor::AnimatedImageLoading
+class WebPLoading : public Internal::Adaptor::AnimatedImageLoading
 {
 public:
-
   /**
    * Create a WebPLoading with the given url and resourceType.
    * @param[in] url The url of the webp image to load
    * @param[in] isLocalResource The true or false whether this is a local resource.
    * @return A newly created WebPLoading.
    */
-  static AnimatedImageLoadingPtr New( const std::string& url, bool isLocalResource );
+  static AnimatedImageLoadingPtr New(const std::string& url, bool isLocalResource);
 
   /**
    * @brief Constructor
@@ -66,8 +63,7 @@ public:
    * @param[in] url The url of the webp image to load
    * @param[in] isLocalResource The true or false whether this is a local resource.
    */
-  WebPLoading( const std::string& url, bool isLocalResource );
-
+  WebPLoading(const std::string& url, bool isLocalResource);
 
   /**
    * @brief Destructor
@@ -84,9 +80,9 @@ public:
    * @param[out] pixelData The vector in which to return the frame data
    * @return True if the frame data was successfully loaded
    */
-  bool LoadNextNFrames( uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData ) override;
+  bool LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData) override;
 
-   /**
+  /**
    * @brief Load the next Frame of the animated image.
    *
    * @note This function will load the entire animated image into memory if not already loaded.
@@ -94,7 +90,7 @@ public:
    * @return Dali::Devel::PixelBuffer The loaded PixelBuffer. If loading is fail, return empty handle.
    */
 
-  Dali::Devel::PixelBuffer LoadFrame( uint32_t frameIndex ) override;
+  Dali::Devel::PixelBuffer LoadFrame(uint32_t frameIndex) override;
 
   /**
    * @brief Get the size of a webp image.
@@ -115,8 +111,13 @@ public:
    *
    * @return The time interval of the frame(microsecond).
    */
-  uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+  uint32_t GetFrameInterval(uint32_t frameIndex) const override;
 
+  /**
+   * @brief Get the animated image file URL
+   *
+   * @return The URL string of the animated image file
+   */
   std::string GetUrl() const override;
 
 private:
index c91ffcf..941172c 100644 (file)
@@ -17,6 +17,7 @@ SET( adaptor_imaging_common_src_files
     ${adaptor_imaging_dir}/common/loader-ktx.cpp
     ${adaptor_imaging_dir}/common/loader-png.cpp
     ${adaptor_imaging_dir}/common/loader-wbmp.cpp
+    ${adaptor_imaging_dir}/common/loader-webp.cpp
     ${adaptor_imaging_dir}/common/pixel-manipulation.cpp
     ${adaptor_imaging_dir}/common/gif-loading.cpp
     ${adaptor_imaging_dir}/common/webp-loading.cpp