Added new API to download images remotely 95/118695/4
authorDavid Steele <david.steele@samsung.com>
Mon, 13 Mar 2017 11:00:52 +0000 (11:00 +0000)
committerDavid Steele <david.steele@samsung.com>
Mon, 20 Mar 2017 10:33:49 +0000 (03:33 -0700)
Added "DownloadImageSynchronously()" method, which invokes libcurl to
download a URL.

Added global object to ensure curl is globally initialized and cleaned up,
this should allow usage of the above method in multiple threads simultaneously.

Also added "GetClosestImageSize" method, which partially loads the image file
to determine it's size.

Change-Id: Ie27f69d729abecfe5de3d5a90eae01a34f92c5b1
Signed-off-by: David Steele <david.steele@samsung.com>
adaptors/common/adaptor-impl.h
adaptors/devel-api/adaptor-framework/image-loading.cpp
adaptors/devel-api/adaptor-framework/image-loading.h
automated-tests/src/dali-adaptor/utc-Dali-ImageLoading.cpp
platform-abstractions/tizen/resource-loader/network/file-download.cpp
platform-abstractions/tizen/resource-loader/network/file-download.h
platform-abstractions/tizen/resource-loader/network/http-utils.cpp
platform-abstractions/tizen/resource-loader/network/http-utils.h

index 479e10e..4cbbdc2 100644 (file)
@@ -563,7 +563,7 @@ private: // Data
 
   Any                                   mNativeWindow;                ///< window identifier
   RenderSurface*                        mSurface;                     ///< Current surface
-  TizenPlatform::TizenPlatformAbstraction*  mPlatformAbstraction;         ///< Platform abstraction
+  TizenPlatform::TizenPlatformAbstraction* mPlatformAbstraction;         ///< Platform abstraction
 
   EventHandler*                         mEventHandler;                ///< event handler
   CallbackManager*                      mCallbackManager;             ///< Used to install callbacks
index 68cb698..c194a76 100644 (file)
 
 // INTERNAL INCLUDES
 #include "image-loaders/image-loader.h"
+#include <resource-loader/network/file-download.h>
+#include <platform-abstractions/portable/file-closer.h>
+#include <platform-abstractions/tizen/resource-loader/resource-loading-client.h>
 
 namespace Dali
 {
 
+namespace
+{
+// limit maximum image down load size to 50 MB
+const size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE  = 50 * 1024 * 1024 ;
+}
+
 PixelData LoadImageFromFile( const std::string& url, ImageDimensions size, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection )
 {
   Integration::BitmapResourceType resourceType( size, fittingMode, samplingMode, orientationCorrection );
@@ -44,4 +53,68 @@ PixelData LoadImageFromFile( const std::string& url, ImageDimensions size, Fitti
   return Dali::PixelData();
 }
 
+ImageDimensions GetClosestImageSize( const std::string& filename,
+                                     ImageDimensions size,
+                                     FittingMode::Type fittingMode,
+                                     SamplingMode::Type samplingMode,
+                                     bool orientationCorrection )
+{
+  return TizenPlatform::ImageLoader::GetClosestImageSize( filename, size, fittingMode, samplingMode, orientationCorrection );
+}
+
+
+PixelData DownloadImageSynchronously( const std::string& url, ImageDimensions size, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection )
+{
+  Integration::BitmapResourceType resourceType( size, fittingMode, samplingMode, orientationCorrection );
+
+  bool succeeded;
+  Dali::Vector<uint8_t> dataBuffer;
+  size_t dataSize;
+
+  succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory( url, dataBuffer, dataSize,
+                                                                    MAXIMUM_DOWNLOAD_IMAGE_SIZE );
+  if( succeeded )
+  {
+    void *blobBytes = static_cast<void*>(&dataBuffer[0]);
+    size_t blobSize = dataBuffer.Size();
+
+    DALI_ASSERT_DEBUG( blobSize > 0U );
+    DALI_ASSERT_DEBUG( blobBytes != 0U );
+
+    if( blobBytes != 0 && blobSize > 0U )
+    {
+      // Open a file handle on the memory buffer:
+      Dali::Internal::Platform::FileCloser fileCloser( blobBytes, blobSize, "rb" );
+      FILE * const fp = fileCloser.GetFile();
+      if ( NULL != fp )
+      {
+        Integration::BitmapPtr bitmap;
+        bool result = TizenPlatform::ImageLoader::ConvertStreamToBitmap(
+          resourceType,
+          url,
+          fp,
+          TizenPlatform::StubbedResourceLoadingClient(),
+          bitmap );
+
+        if ( result && bitmap )
+        {
+          return Dali::PixelData::New( bitmap->GetBufferOwnership(),
+                                       bitmap->GetBufferSize(),
+                                       bitmap->GetImageWidth(),
+                                       bitmap->GetImageHeight(),
+                                       bitmap->GetPixelFormat(),
+                                       Dali::PixelData::FREE );
+        }
+        else
+        {
+          DALI_LOG_WARNING( "Unable to decode bitmap supplied as in-memory blob.\n" );
+        }
+      }
+    }
+
+  }
+  return Dali::PixelData();
+}
+
+
 } // namespace Dali
index 3f20bb4..124b27b 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_IMAGE_LOADING_H
 
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
@@ -38,11 +38,54 @@ namespace Dali
  * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
  * @return handle to the loaded PixelData object or an empty handle in case loading failed.
  */
-DALI_IMPORT_API PixelData LoadImageFromFile( const std::string& url,
-                                             ImageDimensions size = ImageDimensions( 0, 0 ),
-                                             FittingMode::Type fittingMode = FittingMode::DEFAULT,
-                                             SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR,
-                                             bool orientationCorrection = true );
+DALI_IMPORT_API PixelData LoadImageFromFile(
+  const std::string& url,
+  ImageDimensions size = ImageDimensions( 0, 0 ),
+  FittingMode::Type fittingMode = FittingMode::DEFAULT,
+  SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR,
+  bool orientationCorrection = true );
+
+/**
+ * @brief Determine the size of an image that LoadImageFromFile will provide when
+ * given the same image loading parameters.
+ *
+ * This is a synchronous request.
+ * This function is used to determine the size of an image before it has loaded.
+ * @param[in] filename name of the image.
+ * @param[in] size The requested size for the image.
+ * @param[in] fittingMode The method to use to map the source image to the desired
+ * dimensions.
+ * @param[in] samplingMode The image filter to use if the image needs to be
+ * downsampled to the requested size.
+ * @param[in] orientationCorrection Whether to use image metadata to rotate or
+ * flip the image, e.g., from portrait to landscape.
+ * @return dimensions that image will have if it is loaded with given parameters.
+ */
+DALI_IMPORT_API ImageDimensions GetClosestImageSize(
+  const std::string& filename,
+  ImageDimensions size = ImageDimensions(0, 0),
+  FittingMode::Type fittingMode = FittingMode::DEFAULT,
+  SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR ,
+  bool orientationCorrection = true );
+
+/**
+ * @brief Load an image synchronously from a remote resource.
+ *
+ * @param [in] url The URL of the image file to load.
+ * @param [in] size The width and height to fit the loaded image to, 0.0 means whole image
+ * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+ * @param [in] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ *
+ * @return handle to the loaded PixelData object or an empty handle in case downloading or decoding failed.
+ */
+DALI_IMPORT_API PixelData DownloadImageSynchronously(
+  const std::string& url,
+  ImageDimensions size = ImageDimensions( 0, 0 ),
+  FittingMode::Type fittingMode = FittingMode::DEFAULT,
+  SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR,
+  bool orientationCorrection = true );
+
 
 } // Dali
 
index 75c2957..a4682bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
@@ -67,3 +67,35 @@ int UtcDaliLoadImageN(void)
 
   END_TEST;
 }
+
+
+int UtcDaliDownloadImageP(void)
+{
+  std::string url("file://");
+  url.append( gImage_34_RGBA );
+
+  std::string url2("file://");
+  url2.append( gImage_128_RGB );
+
+  PixelData pixelData = Dali::DownloadImageSynchronously( url );
+  DALI_TEST_CHECK( pixelData );
+  DALI_TEST_EQUALS( pixelData.GetWidth(), 34u, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelData.GetHeight(), 34u, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION  );
+
+  PixelData pixelData2 = Dali::DownloadImageSynchronously( url2 );
+  DALI_TEST_CHECK( pixelData2 );
+  DALI_TEST_EQUALS( pixelData2.GetWidth(), 128u, TEST_LOCATION  );
+  DALI_TEST_EQUALS( pixelData2.GetHeight(), 128u, TEST_LOCATION  );
+  DALI_TEST_EQUALS( pixelData2.GetPixelFormat(), Pixel::RGB888, TEST_LOCATION  );
+
+  END_TEST;
+}
+
+int UtcDaliDownloadImageN(void)
+{
+  PixelData pixelData = Dali::DownloadImageSynchronously( gImageNonExist );
+  DALI_TEST_CHECK( !pixelData );
+
+  END_TEST;
+}
index 0352421..1e33dc3 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
@@ -48,6 +48,13 @@ const long INCLUDE_HEADER = 1L;
 const long INCLUDE_BODY = 0L;
 const long EXCLUDE_BODY = 1L;
 
+/**
+ * Curl library environment. Direct initialize ensures it's constructed before adaptor
+ * or application creates any threads.
+ */
+static Dali::TizenPlatform::Network::CurlEnvironment gCurlEnvironment;
+
+
 void ConfigureCurlOptions( CURL* curl_handle, const std::string& url )
 {
   curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str() );
@@ -138,14 +145,31 @@ bool DownloadFile( CURL* curl_handle,
   }
   return true;
 }
+
+
 } // unnamed namespace
 
 
+namespace Network
+{
+
+CurlEnvironment::CurlEnvironment()
+{
+  // Must be called before we attempt any loads. e.g. by using curl_easy_init()
+  // and before we start any threads.
+  curl_global_init(CURL_GLOBAL_ALL);
+}
+
+CurlEnvironment::~CurlEnvironment()
+{
+  curl_global_cleanup();
+}
+
 
-bool Network::DownloadRemoteFileIntoMemory( const std::string& url,
-                                            Dali::Vector<uint8_t>& dataBuffer,
-                                            size_t& dataSize,
-                                            size_t maximumAllowedSizeBytes )
+bool DownloadRemoteFileIntoMemory( const std::string& url,
+                                   Dali::Vector<uint8_t>& dataBuffer,
+                                   size_t& dataSize,
+                                   size_t maximumAllowedSizeBytes )
 {
   if( url.empty() )
   {
@@ -171,6 +195,7 @@ bool Network::DownloadRemoteFileIntoMemory( const std::string& url,
   return result;
 }
 
+} // namespace Network
 
 } // namespace TizenPlatform
 
index 0d31953..ee3d0f5 100644 (file)
@@ -2,7 +2,7 @@
 #define __DALI_TIZEN_PLATFORM_NETWORK_FILE_DOWNLOAD_H__
 
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
@@ -33,8 +33,30 @@ namespace Network
 {
 
 /**
+ * Set up the cURL environment - this will ensure curl_global_init is called on startup
+ * and curl_global_cleanup is called on shutdown.
+ * Having this environment enables curl to be used in a single or multi-threaded
+ * program safely.
+ */
+class CurlEnvironment
+{
+public:
+  /**
+   * Constructor calls curl_global_init()
+   */
+  CurlEnvironment();
+
+  /**
+   * Destructor calls curl_global_cleanup()
+   */
+  ~CurlEnvironment();
+};
+
+
+/**
  * Download a requested file into a memory buffer.
- * Threading notes: This function can be called from multiple threads, however l
+ *
+ * @note Threading notes: This function can be called from multiple threads, however
  * we must explicitly call curl_global_init() from a single thread before using curl
  * as the global function calls are not thread safe.
  *
index 517dfbd..a20142d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
index e7d225c..01bd89c 100644 (file)
@@ -2,7 +2,7 @@
 #define __DALI_TIZEN_PLATFORM_NETWORK_UTILS_H__
 
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.