1 #ifndef DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H
2 #define DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H
5 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
24 #include <dali/public-api/common/dali-vector.h>
25 #include <dali/public-api/object/ref-object.h>
26 #include <dali/public-api/rendering/texture-set.h>
27 #include <dali/devel-api/common/owner-container.h>
28 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
31 #include <dali-toolkit/devel-api/image-loader/async-image-loader-devel.h>
32 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
33 #include <dali-toolkit/public-api/image-loader/async-image-loader.h>
34 #include <dali-toolkit/internal/visuals/texture-upload-observer.h>
35 #include <dali-toolkit/internal/visuals/visual-url.h>
36 #include <dali-toolkit/internal/helpers/round-robin-container-view.h>
37 #include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
50 * The TextureManager provides a common Image loading API for Visuals.
52 * The TextureManager is responsible for providing sync, async, atlased and non-atlased loads.
53 * Texture caching is provided and performed when possible.
54 * Broken Images are automatically provided on load failure.
56 class TextureManager : public ConnectionTracker
60 typedef int32_t TextureId; ///< The TextureId type. This is used as a handle to refer to a particular Texture.
61 static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error
64 * Whether the texture should be atlased or uploaded into it's own GPU texture
73 * Whether the pixel data should be kept in TextureManager, or uploaded for rendering
82 * Whether the texture should be loaded synchronously or asynchronously.
91 * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle.
95 NOT_STARTED, ///< Default
96 LOADING, ///< Loading has been started, but not finished.
97 LOAD_FINISHED, ///< Loading has finished. (for CPU storage only)
98 WAITING_FOR_MASK,///< Loading has finished, but waiting for mask image
99 UPLOADED, ///< Uploaded and ready. (For GPU upload only)
100 CANCELLED, ///< Removed before loading completed
101 LOAD_FAILED ///< Async loading failed, e.g. connection problem
117 // TextureManager Main API:
120 * @brief Requests an image load of the given URL.
122 * The parameters are used to specify how the image is loaded.
123 * The observer has the UploadComplete method called when the load is ready.
125 * When the client has finished with the Texture, Remove() should be called.
127 * @param[in] url The URL of the image to load
128 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
129 * @param[in] fittingMode The FittingMode to use
130 * @param[in] samplingMode The SamplingMode to use
131 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
132 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
133 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
134 * This is called when an image load completes (or fails).
135 * @return A TextureId to use as a handle to reference this Texture
137 TextureId RequestLoad( const VisualUrl& url,
138 const ImageDimensions desiredSize,
139 FittingMode::Type fittingMode,
140 Dali::SamplingMode::Type samplingMode,
141 const UseAtlas useAtlasing,
142 TextureUploadObserver* observer );
145 * @brief Requests an image load of the given URL, when the texture has
146 * have loaded, it will perform a blend with the image mask, and upload
147 * the blended texture.
149 * The parameters are used to specify how the image is loaded.
150 * The observer has the UploadComplete method called when the load is ready.
152 * When the client has finished with the Texture, Remove() should be called.
154 * @param[in] url The URL of the image to load
155 * @param[in] maskTextureId The texture id of an image to mask this with (can be INVALID if no masking required)
156 * @param[in] contentScale The scale factor to apply to the image before masking
157 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
158 * @param[in] fittingMode The FittingMode to use
159 * @param[in] samplingMode The SamplingMode to use
160 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
161 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
162 * @param[in] cropToMask Only used with masking, this will crop the scaled image to the mask size. If false, then the mask will be scaled to fit the image before being applied.
163 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
164 * This is called when an image load completes (or fails).
165 * @return A TextureId to use as a handle to reference this Texture
167 TextureId RequestLoad( const VisualUrl& url,
168 TextureId maskTextureId,
170 const ImageDimensions desiredSize,
171 FittingMode::Type fittingMode,
172 Dali::SamplingMode::Type samplingMode,
173 const UseAtlas useAtlasing,
175 TextureUploadObserver* observer );
178 * Requests a masking image to be loaded. This mask is not uploaded to GL,
179 * instead, it is stored in CPU memory, and can be used for CPU blending.
181 TextureId RequestMaskLoad( const VisualUrl& maskUrl );
184 * @brief Remove a Texture from the TextureManager.
186 * Textures are cached and therefore only the removal of the last
187 * occurrence of a Texture will cause its removal internally.
189 * @param[in] textureId The ID of the Texture to remove.
191 void Remove( const TextureManager::TextureId textureId );
194 * Get the visualUrl associated with the texture id
196 const VisualUrl& GetVisualUrl( TextureId textureId );
199 * @brief Get the current state of a texture
200 * @param[in] textureId The texture id to query
201 * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
204 LoadState GetTextureState( TextureId textureId );
207 * @brief Get the associated texture set if the texture id is valid
208 * @param[in] textureId The texture Id to look up
209 * @return the associated texture set, or an empty handle if textureId is not valid
211 TextureSet GetTextureSet( TextureId textureId );
214 * Adds an external texture to the texture manager
215 * @param[in] texture The texture to add
216 * @return string containing the URL for the texture
218 std::string AddExternalTexture( TextureSet& texture );
221 * Removes an external texture from texture manager
222 * @param[in] url The string containing the texture to remove
223 * @return handle to the texture
225 TextureSet RemoveExternalTexture( const std::string& url );
230 * @brief Requests an image load of the given URL, when the texture has
231 * have loaded, if there is a valid maskTextureId, it will perform a
232 * CPU blend with the mask, and upload the blend texture.
234 * The parameters are used to specify how the image is loaded.
235 * The observer has the UploadComplete method called when the load is ready.
237 * When the client has finished with the Texture, Remove() should be called.
239 * @param[in] url The URL of the image to load
240 * @param[in] maskTextureId The texture id of an image to use as a mask. If no mask is required, then set to INVALID_TEXTURE_ID
241 * @param[in] contentScale The scaling factor to apply to the content when masking
242 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
243 * @param[in] fittingMode The FittingMode to use
244 * @param[in] samplingMode The SamplingMode to use
245 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
246 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
247 * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before masking.
248 * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU
249 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
250 * This is called when an image load completes (or fails).
251 * @return A TextureId to use as a handle to reference this Texture
253 TextureId RequestLoadInternal(
254 const VisualUrl& url,
255 TextureId maskTextureId,
257 const ImageDimensions desiredSize,
258 FittingMode::Type fittingMode,
259 Dali::SamplingMode::Type samplingMode,
262 StorageType storageType,
263 TextureUploadObserver* observer );
266 typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
269 * @brief This struct is used to manage the life-cycle of Texture loading and caching.
273 TextureInfo( TextureId textureId,
274 TextureId maskTextureId,
275 const VisualUrl& url,
276 ImageDimensions desiredSize,
278 FittingMode::Type fittingMode,
279 Dali::SamplingMode::Type samplingMode,
280 bool loadSynchronously,
283 TextureManager::TextureHash hash )
285 desiredSize( desiredSize ),
286 useSize( desiredSize ),
287 atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
288 textureId( textureId ),
289 maskTextureId( maskTextureId ),
291 scaleFactor( scaleFactor ),
292 referenceCount( 1u ),
293 loadState( NOT_STARTED ),
294 fittingMode( fittingMode ),
295 samplingMode( samplingMode ),
296 storageType( UPLOAD_TO_TEXTURE ),
297 loadSynchronously( loadSynchronously ),
298 useAtlas( useAtlas ),
299 cropToMask( cropToMask )
304 * Container type used to store all observer clients of this Texture
306 typedef Dali::Vector< TextureUploadObserver* > ObserverListType;
308 ObserverListType observerList; ///< Container used to store all observer clients of this Texture
309 Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any)
310 Devel::PixelBuffer pixelBuffer;///< The PixelBuffer holding the image data (May be empty after upload)
311 TextureSet textureSet; ///< The TextureSet holding the Texture
312 VisualUrl url; ///< The URL of the image
313 ImageDimensions desiredSize; ///< The size requested
314 ImageDimensions useSize; ///< The size used
315 Vector4 atlasRect; ///< The atlas rect used if atlased
316 TextureId textureId; ///< The TextureId associated with this Texture
317 TextureId maskTextureId; ///< The mask TextureId to be applied on load
318 TextureManager::TextureHash hash; ///< The hash used to cache this Texture
319 float scaleFactor; ///< The scale factor to apply to the Texture when masking
320 int16_t referenceCount; ///< The reference count of clients using this Texture
321 LoadState loadState:3; ///< The load state showing the load progress of the Texture
322 FittingMode::Type fittingMode:2; ///< The requested FittingMode
323 Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
324 StorageType storageType:1; ///< CPU storage / GPU upload;
325 bool loadSynchronously:1; ///< True if synchronous loading was requested
326 UseAtlas useAtlas:1; ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
327 bool cropToMask:1; ///< true if the image should be cropped to the mask size.
333 * Struct to hold information about a requested Async load.
334 * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id.
336 struct AsyncLoadingInfo
338 AsyncLoadingInfo( TextureId textureId )
339 : textureId( textureId ),
344 TextureId textureId; ///< The external Texture Id assigned to this load
345 unsigned short loadId; ///< The load Id used by the async loader to reference this load
349 * @brief This struct is used within a container to manage atlas creation and destruction.
353 AtlasInfo( Toolkit::ImageAtlas atlas, TextureSet textureSet )
355 textureSet( textureSet )
359 Toolkit::ImageAtlas atlas; ///< The ImageAtlas object
360 TextureSet textureSet; ///< The TextureSet is kept in the struct to allow fast lookup of TextureSet to Atlas
365 typedef std::deque<AsyncLoadingInfo> AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress
366 typedef std::vector<AtlasInfo> AtlasInfoContainerType; ///< The container type used to manage Atlas creation and destruction
367 typedef std::vector<TextureInfo> TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
370 * @brief Used internally to initiate a load.
371 * @param[in] textureInfo The TextureInfo struct associated with the Texture
372 * @return True if the load was initiated
374 bool LoadTexture( TextureInfo& textureInfo );
377 * Add the observer to the observer list
378 * @param[in] textureInfo The TextureInfo struct associated with the texture
379 * observer The observer wishing to observe the texture upload
381 void ObserveTexture( TextureInfo & textureInfo, TextureUploadObserver* observer );
384 * @brief This signal handler is called when the async local loader finishes loading.
385 * @param[in] id This is the async image loaders Id
386 * @param[in] pixelBuffer The loaded image data
388 void AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
391 * @brief This signal handler is called when the async local loader finishes loading.
392 * @param[in] id This is the async image loaders Id
393 * @param[in] pixelBuffer The loaded image data
395 void AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
398 * Common method to handle loading completion
399 * @param[in] container The Async loading container
400 * @param[in] id This is the async image loaders Id
401 * @param[in] pixelBuffer The loaded image data
403 void AsyncLoadComplete( AsyncLoadingInfoContainerType& container, uint32_t id, Devel::PixelBuffer pixelBuffer );
406 * @brief Performs Post-Load steps including atlasing.
407 * @param[in] textureInfo The struct associated with this Texture
408 * @param[in] pixelBuffer The image pixelBuffer
409 * @return True if successful
411 void PostLoad( TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer );
414 * Check if there is a texture waiting to be masked. If there
415 * is then apply this mask and upload it.
416 * @param[in] maskTextureInfo The texture info of the mask that has just loaded.
418 void CheckForWaitingTexture( TextureInfo& maskTextureInfo );
421 * Apply the mask to the pixelBuffer.
422 * @param[in] pixelBuffer The pixelBuffer to apply the mask to
423 * @param[in] maskTextureId The texture id of the mask.
424 * @param[in] contentScale The factor to scale the content
425 * @param[in] cropToMask Whether to crop the content to the mask size
427 void ApplyMask( Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
428 float contentScale, bool cropToMask );
431 * Upload the texture specified in pixelBuffer to the appropriate location
432 * @param[in] pixelBuffer The image data to upload
433 * @param[in] textureInfo The texture info containing the location to
436 void UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo );
439 * Mark the texture as complete, and inform observers
440 * @param[in] textureInfo The struct associated with this Texture
442 void UploadComplete( TextureInfo& textureInfo );
445 * Notify the current observers that the texture upload is complete,
446 * then remove the observers from the list.
447 * @param[in] textureInfo The struct associated with this Texture
448 * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU
450 void NotifyObservers( TextureInfo& textureInfo, bool success );
453 * @brief Generates a new, unique TextureId
454 * @return A unique TextureId
456 TextureManager::TextureId GenerateUniqueTextureId();
459 * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
460 * @param[in] textureId The TextureId to look up
461 * @return The cache index
463 int GetCacheIndexFromId( TextureId textureId );
467 * @brief Generates a hash for caching based on the input parameters.
468 * Only applies size, fitting mode andsampling mode if the size is specified.
469 * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
470 * Always applies useAtlas.
471 * @param[in] url The URL of the image to load
472 * @param[in] size The image size
473 * @param[in] fittingMode The FittingMode to use
474 * @param[in] samplingMode The SamplingMode to use
475 * @param[in] useAtlas True if atlased
476 * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
477 * @return A hash of the provided data for caching.
479 TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
480 const FittingMode::Type fittingMode,
481 const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
482 TextureId maskTextureId );
484 * @brief Looks up a cached texture by its hash.
485 * If found, the given parameters are used to check there is no hash-collision.
486 * @param[in] hash The hash to look up
487 * @param[in] url The URL of the image to load
488 * @param[in] size The image size
489 * @param[in] fittingMode The FittingMode to use
490 * @param[in] samplingMode The SamplingMode to use
491 * @param[in] useAtlas True if atlased
492 * @param[in] maskTextureId Optional texture ID to use to mask this image
493 * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
495 TextureManager::TextureId FindCachedTexture(
496 const TextureManager::TextureHash hash,
497 const std::string& url,
498 const ImageDimensions size,
499 const FittingMode::Type fittingMode,
500 const Dali::SamplingMode::Type samplingMode,
502 TextureId maskTextureId );
507 * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container
509 class AsyncLoadingHelper : public ConnectionTracker
513 * @brief Create an AsyncLoadingHelper.
514 * @param[in] textureManager Reference to the texture manager
516 AsyncLoadingHelper(TextureManager& textureManager);
519 * @brief Load a new texture.
520 * @param[in] textureId TextureId to reference the texture that will be loaded
521 * @param[in] url The URL of the image to load
522 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
523 * @param[in] fittingMode The FittingMode to use
524 * @param[in] samplingMode The SamplingMode to use
525 * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, e.g., from portrait to landscape
527 void Load(TextureId textureId,
528 const VisualUrl& url,
529 ImageDimensions desiredSize,
530 FittingMode::Type fittingMode,
531 SamplingMode::Type samplingMode,
532 bool orientationCorrection);
535 AsyncLoadingHelper(const AsyncLoadingHelper&) = delete;
536 AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete;
538 AsyncLoadingHelper(AsyncLoadingHelper&& rhs);
539 AsyncLoadingHelper& operator=(AsyncLoadingHelper&&rhs) = delete;
543 * @brief Main constructor that used by all other constructors
545 AsyncLoadingHelper(Toolkit::AsyncImageLoader loader,
546 TextureManager& textureManager,
547 AsyncLoadingInfoContainerType&& loadingInfoContainer);
550 * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager.
551 * @param[in] id Loader id
552 * @param[in] pixelBuffer Image data
554 void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer);
557 Toolkit::AsyncImageLoader mLoader;
558 TextureManager& mTextureManager;
559 AsyncLoadingInfoContainerType mLoadingInfoContainer;
562 struct ExternalTextureInfo
565 TextureSet textureSet;
571 * Deleted copy constructor.
573 TextureManager( const TextureManager& ) = delete;
576 * Deleted assignment operator.
578 TextureManager& operator=( const TextureManager& rhs ) = delete;
581 * This is called by the TextureManagerUploadObserver when an observer is destroyed.
582 * We use the callback to know when to remove an observer from our notify list.
583 * @param[in] observer The observer that generated the callback
585 void ObserverDestroyed( TextureUploadObserver* observer );
587 private: // Member Variables:
589 AtlasInfoContainerType mAtlasContainer; ///< Used to manage Atlas creation and destruction
590 TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures
591 RoundRobinContainerView< AsyncLoadingHelper > mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
592 RoundRobinContainerView< AsyncLoadingHelper > mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
593 std::vector< ExternalTextureInfo > mExternalTextures; ///< Externally provided textures
594 TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation
601 } // namespace Toolkit
605 #endif // DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H