Add features to download images over http protocol.
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / slp / resource-loader / resource-thread-image.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include "resource-thread-image.h"
19 #include <dali/public-api/common/ref-counted-dali-vector.h>
20 #include <dali/integration-api/bitmap.h>
21 #include <dali/integration-api/debug.h>
22 #include <dali/integration-api/resource-cache.h>
23 #include <dali/integration-api/resource-types.h>
24 #include <curl/curl.h>
25 #include "portable/file-closer.h"
26 #include "image-loaders/image-loader.h"
27
28 using namespace Dali::Integration;
29
30 namespace Dali
31 {
32
33 namespace SlpPlatform
34 {
35
36 ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader, bool forRemoteImage)
37 : ResourceThreadBase(resourceLoader)
38 {
39 }
40
41 ResourceThreadImage::~ResourceThreadImage()
42 {
43 }
44
45 void ResourceThreadImage::Load(const ResourceRequest& request)
46 {
47   DALI_LOG_TRACE_METHOD( mLogFilter );
48   DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
49
50   LoadImageFromLocalFile(request);
51 }
52
53 void ResourceThreadImage::Download(const ResourceRequest& request)
54 {
55   bool succeeded;
56
57   DALI_LOG_TRACE_METHOD( mLogFilter );
58   DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
59
60   Dali::Vector<uint8_t> dataBuffer;
61   size_t dataSize;
62   succeeded = DownloadRemoteImageIntoMemory( request, dataBuffer, dataSize );
63   if( succeeded )
64   {
65     DecodeImageFromMemory(static_cast<void*>(&dataBuffer[0]), dataBuffer.Size(), request);
66   }
67 }
68
69 void ResourceThreadImage::Decode(const ResourceRequest& request)
70 {
71   DALI_LOG_TRACE_METHOD( mLogFilter );
72   DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
73
74   // Get the blob of binary data that we need to decode:
75   DALI_ASSERT_DEBUG( request.GetResource() );
76
77   DALI_ASSERT_DEBUG( 0 != dynamic_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() ) && "Only blobs of binary data can be decoded." );
78   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() );
79
80   if( 0 != encodedBlob )
81   {
82     const size_t blobSize     = encodedBlob->GetVector().Size();
83     uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
84     DecodeImageFromMemory(blobBytes, blobSize, request);
85   }
86   else
87   {
88     FailedResource resource(request.GetId(), FailureUnknown);
89     mResourceLoader.AddFailedLoad(resource);
90   }
91 }
92
93 void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
94 {
95   DALI_LOG_TRACE_METHOD( mLogFilter );
96   DALI_ASSERT_DEBUG( request.GetType()->id == ResourceBitmap );
97   DALI_LOG_WARNING( "Image saving not supported on background resource threads." );
98 }
99
100 bool ResourceThreadImage::DownloadRemoteImageIntoMemory(const Integration::ResourceRequest& request, Dali::Vector<uint8_t>& dataBuffer, size_t& dataSize)
101 {
102   bool succeeded = true;
103   CURLcode cresult;
104
105   CURL* curl_handle = curl_easy_init();
106   curl_easy_setopt( curl_handle, CURLOPT_VERBOSE, 0 );
107   curl_easy_setopt( curl_handle, CURLOPT_URL, request.GetPath().c_str() );
108   curl_easy_setopt( curl_handle, CURLOPT_FAILONERROR, 1 );
109
110   // Download header first to get data size
111   char* headerBytes = NULL;
112   size_t headerSize = 0;
113   FILE* header_fp = open_memstream( &headerBytes, &headerSize );
114   double size;
115
116   if( NULL != header_fp)
117   {
118     curl_easy_setopt( curl_handle, CURLOPT_HEADER, 1 );
119     curl_easy_setopt( curl_handle, CURLOPT_NOBODY, 1 );
120     curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, header_fp );
121
122     cresult = curl_easy_perform( curl_handle );
123     if( cresult == CURLE_OK )
124     {
125       curl_easy_getinfo( curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size );
126     }
127     else
128     {
129       DALI_LOG_WARNING( "Failed to download file to load \"%s\"\n", request.GetPath().c_str() );
130       succeeded = false;
131     }
132
133     fclose( header_fp );
134   }
135   else
136   {
137     succeeded = false;
138   }
139
140   if( NULL != headerBytes )
141   {
142     free( headerBytes );
143   }
144
145   if( succeeded )
146   {
147     // Download file data
148     dataSize = static_cast<size_t>( size );
149     dataBuffer.Reserve( dataSize );
150     dataBuffer.Resize( dataSize );
151
152     Dali::Internal::Platform::FileCloser fileCloser( static_cast<void*>(&dataBuffer[0]), dataSize, "wb" );
153     FILE* data_fp = fileCloser.GetFile();
154     if( NULL != data_fp )
155     {
156       curl_easy_setopt( curl_handle, CURLOPT_HEADER, 0 );
157       curl_easy_setopt( curl_handle, CURLOPT_NOBODY, 0 );
158       curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, data_fp );
159
160       cresult = curl_easy_perform( curl_handle );
161       if( CURLE_OK != cresult )
162       {
163         DALI_LOG_WARNING( "Failed to download file to load \"%s\"\n", request.GetPath().c_str() );
164         succeeded = false;
165       }
166     }
167     else
168     {
169       succeeded = false;
170     }
171   }
172
173   curl_easy_cleanup( curl_handle );
174
175   if( !succeeded )
176   {
177     FailedResource resource(request.GetId(), FailureUnknown);
178     mResourceLoader.AddFailedLoad(resource);
179   }
180
181   return succeeded;
182 }
183
184 void ResourceThreadImage::LoadImageFromLocalFile(const Integration::ResourceRequest& request)
185 {
186   bool fileNotFound = false;
187   BitmapPtr bitmap = 0;
188   bool result = false;
189
190   Dali::Internal::Platform::FileCloser fileCloser( request.GetPath().c_str(), "rb" );
191   FILE * const fp = fileCloser.GetFile();
192
193   if( NULL != fp )
194   {
195     result = ImageLoader::ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, *this, bitmap );
196     // Last chance to interrupt a cancelled load before it is reported back to clients
197     // which have already stopped tracking it:
198     InterruptionPoint(); // Note: This can throw an exception.
199     if( result && bitmap )
200     {
201       // Construct LoadedResource and ResourcePointer for image data
202       LoadedResource resource( request.GetId(), request.GetType()->id, ResourcePointer( bitmap.Get() ) );
203       // Queue the loaded resource
204       mResourceLoader.AddLoadedResource( resource );
205     }
206     else
207     {
208       DALI_LOG_WARNING( "Unable to decode %s\n", request.GetPath().c_str() );
209     }
210   }
211   else
212   {
213     DALI_LOG_WARNING( "Failed to open file to load \"%s\"\n", request.GetPath().c_str() );
214     fileNotFound = true;
215   }
216
217   if ( !bitmap )
218   {
219     if( fileNotFound )
220     {
221       FailedResource resource(request.GetId(), FailureFileNotFound  );
222       mResourceLoader.AddFailedLoad(resource);
223     }
224     else
225     {
226       FailedResource resource(request.GetId(), FailureUnknown);
227       mResourceLoader.AddFailedLoad(resource);
228     }
229   }
230 }
231
232 void ResourceThreadImage::DecodeImageFromMemory(void* blobBytes, size_t blobSize, const Integration::ResourceRequest& request)
233 {
234   BitmapPtr bitmap = 0;
235
236   DALI_ASSERT_DEBUG( blobSize > 0U );
237   DALI_ASSERT_DEBUG( blobBytes != 0U );
238
239   if( blobBytes != 0 && blobSize > 0U )
240   {
241     // Open a file handle on the memory buffer:
242     Dali::Internal::Platform::FileCloser fileCloser( blobBytes, blobSize, "rb" );
243     FILE * const fp = fileCloser.GetFile();
244     if ( NULL != fp )
245     {
246       bool result = ImageLoader::ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, StubbedResourceLoadingClient(), bitmap );
247       if ( result && bitmap )
248       {
249         // Construct LoadedResource and ResourcePointer for image data
250         LoadedResource resource( request.GetId(), request.GetType()->id, ResourcePointer( bitmap.Get() ) );
251         // Queue the loaded resource
252         mResourceLoader.AddLoadedResource( resource );
253       }
254       else
255       {
256         DALI_LOG_WARNING( "Unable to decode bitmap supplied as in-memory blob.\n" );
257       }
258     }
259   }
260
261   if (!bitmap)
262   {
263     FailedResource resource(request.GetId(), FailureUnknown);
264     mResourceLoader.AddFailedLoad(resource);
265   }
266 }
267
268 } // namespace SlpPlatform
269
270 } // namespace Dali