1 #ifndef DALI_TOOLKIT_TEXTURE_MANAGER_H
2 #define DALI_TOOLKIT_TEXTURE_MANAGER_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>
49 class MaskTextureObserver;
52 * The TextureManager provides a common Image loading API for Visuals.
54 * The TextureManager is responsible for providing sync, async, atlased and non-atlased loads.
55 * Texture caching is provided and performed when possible.
56 * Broken Images are automatically provided on load failure.
58 class TextureManager : public ConnectionTracker
62 typedef int32_t TextureId; ///< The TextureId type. This is used as a handle to refer to a particular Texture.
63 static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error
66 * Whether the texture should be atlased or uploaded into it's own GPU texture
75 * Whether the pixel data should be kept in TextureManager, or uploaded for rendering
84 * Whether the texture should be loaded synchronously or asynchronously.
93 * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle.
97 NOT_STARTED, ///< Default
98 LOADING, ///< Loading has been started, but not finished.
99 LOAD_FINISHED, ///< Loading has finished. (for CPU storage only)
100 WAITING_FOR_MASK,///< Loading has finished, but waiting for mask image
101 UPLOADED, ///< Uploaded and ready. (For GPU upload only)
102 CANCELLED, ///< Removed before loading completed
103 LOAD_FAILED ///< Async loading failed, e.g. connection problem
119 // TextureManager Main API:
122 * @brief Requests an image load of the given URL.
124 * The parameters are used to specify how the image is loaded.
125 * The observer has the UploadComplete method called when the load is ready.
127 * When the client has finished with the Texture, Remove() should be called.
129 * @param[in] url The URL of the image to load
130 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
131 * @param[in] fittingMode The FittingMode to use
132 * @param[in] samplingMode The SamplingMode to use
133 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
134 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
135 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
136 * This is called when an image load completes (or fails).
137 * @return A TextureId to use as a handle to reference this Texture
139 TextureId RequestLoad( const VisualUrl& url,
140 const ImageDimensions desiredSize,
141 FittingMode::Type fittingMode,
142 Dali::SamplingMode::Type samplingMode,
143 const UseAtlas useAtlasing,
144 TextureUploadObserver* observer );
147 * @brief Requests an image load of the given URL, when the texture has
148 * have loaded, it will perform a blend with the image mask, and upload
149 * the blended texture.
151 * The parameters are used to specify how the image is loaded.
152 * The observer has the UploadComplete method called when the load is ready.
154 * When the client has finished with the Texture, Remove() should be called.
156 * @param[in] url The URL of the image to load
157 * @param[in] maskTextureId The texture id of an image to mask this with (can be INVALID if no masking required)
158 * @param[in] contentScale The scale factor to apply to the image before masking
159 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
160 * @param[in] fittingMode The FittingMode to use
161 * @param[in] samplingMode The SamplingMode to use
162 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
163 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
164 * @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.
165 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
166 * This is called when an image load completes (or fails).
167 * @return A TextureId to use as a handle to reference this Texture
169 TextureId RequestLoad( const VisualUrl& url,
170 TextureId maskTextureId,
172 const ImageDimensions desiredSize,
173 FittingMode::Type fittingMode,
174 Dali::SamplingMode::Type samplingMode,
175 const UseAtlas useAtlasing,
177 TextureUploadObserver* observer );
180 * Requests a masking image to be loaded. This mask is not uploaded to GL,
181 * instead, it is stored in CPU memory, and can be used for CPU blending.
183 TextureId RequestMaskLoad( const VisualUrl& maskUrl );
186 * @brief Remove a Texture from the TextureManager.
188 * Textures are cached and therefore only the removal of the last
189 * occurrence of a Texture will cause its removal internally.
191 * @param[in] textureId The ID of the Texture to remove.
193 void Remove( const TextureManager::TextureId textureId );
196 * Get the visualUrl associated with the texture id
198 const VisualUrl& GetVisualUrl( TextureId textureId );
201 * @brief Get the current state of a texture
202 * @param[in] textureId The texture id to query
203 * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
206 LoadState GetTextureState( TextureId textureId );
209 * @brief Get the associated texture set if the texture id is valid
210 * @param[in] textureId The texture Id to look up
211 * @return the associated texture set, or an empty handle if textureId is not valid
213 TextureSet GetTextureSet( TextureId textureId );
218 * @brief Requests an image load of the given URL, when the texture has
219 * have loaded, if there is a valid maskTextureId, it will perform a
220 * CPU blend with the mask, and upload the blend texture.
222 * The parameters are used to specify how the image is loaded.
223 * The observer has the UploadComplete method called when the load is ready.
225 * When the client has finished with the Texture, Remove() should be called.
227 * @param[in] url The URL of the image to load
228 * @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
229 * @param[in] contentScale The scaling factor to apply to the content when masking
230 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
231 * @param[in] fittingMode The FittingMode to use
232 * @param[in] samplingMode The SamplingMode to use
233 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
234 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
235 * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before masking.
236 * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU
237 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
238 * This is called when an image load completes (or fails).
239 * @return A TextureId to use as a handle to reference this Texture
241 TextureId RequestLoadInternal(
242 const VisualUrl& url,
243 TextureId maskTextureId,
245 const ImageDimensions desiredSize,
246 FittingMode::Type fittingMode,
247 Dali::SamplingMode::Type samplingMode,
250 StorageType storageType,
251 TextureUploadObserver* observer );
254 typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
257 * @brief This struct is used to manage the life-cycle of Texture loading and caching.
261 TextureInfo( TextureId textureId,
262 TextureId maskTextureId,
263 const VisualUrl& url,
264 ImageDimensions desiredSize,
266 FittingMode::Type fittingMode,
267 Dali::SamplingMode::Type samplingMode,
268 bool loadSynchronously,
271 TextureManager::TextureHash hash )
273 desiredSize( desiredSize ),
274 useSize( desiredSize ),
275 atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
276 textureId( textureId ),
277 maskTextureId( maskTextureId ),
279 scaleFactor( scaleFactor ),
280 referenceCount( 1u ),
281 loadState( NOT_STARTED ),
282 fittingMode( fittingMode ),
283 samplingMode( samplingMode ),
284 storageType( UPLOAD_TO_TEXTURE ),
285 loadSynchronously( loadSynchronously ),
286 useAtlas( useAtlas ),
287 cropToMask( cropToMask )
292 * Container type used to store all observer clients of this Texture
294 typedef Dali::Vector< TextureUploadObserver* > ObserverListType;
296 ObserverListType observerList; ///< Container used to store all observer clients of this Texture
297 Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any)
298 Devel::PixelBuffer pixelBuffer;///< The PixelBuffer holding the image data (May be empty after upload)
299 TextureSet textureSet; ///< The TextureSet holding the Texture
300 VisualUrl url; ///< The URL of the image
301 ImageDimensions desiredSize; ///< The size requested
302 ImageDimensions useSize; ///< The size used
303 Vector4 atlasRect; ///< The atlas rect used if atlased
304 TextureId textureId; ///< The TextureId associated with this Texture
305 TextureId maskTextureId; ///< The mask TextureId to be applied on load
306 TextureManager::TextureHash hash; ///< The hash used to cache this Texture
307 float scaleFactor; ///< The scale factor to apply to the Texture when masking
308 int16_t referenceCount; ///< The reference count of clients using this Texture
309 LoadState loadState:3; ///< The load state showing the load progress of the Texture
310 FittingMode::Type fittingMode:2; ///< The requested FittingMode
311 Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
312 StorageType storageType:1; ///< CPU storage / GPU upload;
313 bool loadSynchronously:1; ///< True if synchronous loading was requested
314 UseAtlas useAtlas:1; ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
315 bool cropToMask:1; ///< true if the image should be cropped to the mask size.
321 * Struct to hold information about a requested Async load.
322 * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id.
324 struct AsyncLoadingInfo
326 AsyncLoadingInfo( TextureId textureId )
327 : textureId( textureId ),
332 TextureId textureId; ///< The external Texture Id assigned to this load
333 unsigned short loadId; ///< The load Id used by the async loader to reference this load
337 * @brief This struct is used within a container to manage atlas creation and destruction.
341 AtlasInfo( Toolkit::ImageAtlas atlas, TextureSet textureSet )
343 textureSet( textureSet )
347 Toolkit::ImageAtlas atlas; ///< The ImageAtlas object
348 TextureSet textureSet; ///< The TextureSet is kept in the struct to allow fast lookup of TextureSet to Atlas
353 typedef std::deque<AsyncLoadingInfo> AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress
354 typedef std::vector<AtlasInfo> AtlasInfoContainerType; ///< The container type used to manage Atlas creation and destruction
355 typedef std::vector<TextureInfo> TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
358 * @brief Used internally to initiate a load.
359 * @param[in] textureInfo The TextureInfo struct associated with the Texture
360 * @return True if the load was initiated
362 bool LoadTexture( TextureInfo& textureInfo );
365 * Add the observer to the observer list
366 * @param[in] textureInfo The TextureInfo struct associated with the texture
367 * observer The observer wishing to observe the texture upload
369 void ObserveTexture( TextureInfo & textureInfo, TextureUploadObserver* observer );
372 * @brief This signal handler is called when the async local loader finishes loading.
373 * @param[in] id This is the async image loaders Id
374 * @param[in] pixelBuffer The loaded image data
376 void AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
379 * @brief This signal handler is called when the async local loader finishes loading.
380 * @param[in] id This is the async image loaders Id
381 * @param[in] pixelBuffer The loaded image data
383 void AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
386 * Common method to handle loading completion
387 * @param[in] container The Async loading container
388 * @param[in] id This is the async image loaders Id
389 * @param[in] pixelBuffer The loaded image data
391 void AsyncLoadComplete( AsyncLoadingInfoContainerType& container, uint32_t id, Devel::PixelBuffer pixelBuffer );
394 * @brief Performs Post-Load steps including atlasing.
395 * @param[in] textureInfo The struct associated with this Texture
396 * @param[in] pixelBuffer The image pixelBuffer
397 * @return True if successful
399 void PostLoad( TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer );
402 * Check if there is a texture waiting to be masked. If there
403 * is then apply this mask and upload it.
404 * @param[in] maskTextureInfo The texture info of the mask that has just loaded.
406 void CheckForWaitingTexture( TextureInfo& maskTextureInfo );
409 * Apply the mask to the pixelBuffer.
410 * @param[in] pixelBuffer The pixelBuffer to apply the mask to
411 * @param[in] maskTextureId The texture id of the mask.
412 * @param[in] contentScale The factor to scale the content
413 * @param[in] cropToMask Whether to crop the content to the mask size
415 void ApplyMask( Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
416 float contentScale, bool cropToMask );
419 * Upload the texture specified in pixelBuffer to the appropriate location
420 * @param[in] pixelBuffer The image data to upload
421 * @param[in] textureInfo The texture info containing the location to
424 void UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo );
427 * Mark the texture as complete, and inform observers
428 * @param[in] textureInfo The struct associated with this Texture
430 void UploadComplete( TextureInfo& textureInfo );
433 * Notify the current observers that the texture upload is complete,
434 * then remove the observers from the list.
435 * @param[in] textureInfo The struct associated with this Texture
436 * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU
438 void NotifyObservers( TextureInfo& textureInfo, bool success );
441 * @brief Generates a new, unique TextureId
442 * @return A unique TextureId
444 TextureManager::TextureId GenerateUniqueTextureId();
447 * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
448 * @param[in] textureId The TextureId to look up
449 * @return The cache index
451 int GetCacheIndexFromId( TextureId textureId );
455 * @brief Generates a hash for caching based on the input parameters.
456 * Only applies size, fitting mode andsampling mode if the size is specified.
457 * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
458 * Always applies useAtlas.
459 * @param[in] url The URL of the image to load
460 * @param[in] size The image size
461 * @param[in] fittingMode The FittingMode to use
462 * @param[in] samplingMode The SamplingMode to use
463 * @param[in] useAtlas True if atlased
464 * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
465 * @return A hash of the provided data for caching.
467 TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
468 const FittingMode::Type fittingMode,
469 const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
470 TextureId maskTextureId );
472 * @brief Looks up a cached texture by its hash.
473 * If found, the given parameters are used to check there is no hash-collision.
474 * @param[in] hash The hash to look up
475 * @param[in] url The URL of the image to load
476 * @param[in] size The image size
477 * @param[in] fittingMode The FittingMode to use
478 * @param[in] samplingMode The SamplingMode to use
479 * @param[in] useAtlas True if atlased
480 * @param[in] maskTextureId Optional texture ID to use to mask this image
481 * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
483 TextureManager::TextureId FindCachedTexture(
484 const TextureManager::TextureHash hash,
485 const std::string& url,
486 const ImageDimensions size,
487 const FittingMode::Type fittingMode,
488 const Dali::SamplingMode::Type samplingMode,
490 TextureId maskTextureId );
495 * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container
497 class AsyncLoadingHelper : public ConnectionTracker
501 * @brief Create an AsyncLoadingHelper.
502 * @param[in] textureManager Reference to the texture manager
504 AsyncLoadingHelper(TextureManager& textureManager);
507 * @brief Load a new texture.
508 * @param[in] textureId TextureId to reference the texture that will be loaded
509 * @param[in] url The URL of the image to load
510 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
511 * @param[in] fittingMode The FittingMode to use
512 * @param[in] samplingMode The SamplingMode to use
513 * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, e.g., from portrait to landscape
515 void Load(TextureId textureId,
516 const VisualUrl& url,
517 ImageDimensions desiredSize,
518 FittingMode::Type fittingMode,
519 SamplingMode::Type samplingMode,
520 bool orientationCorrection);
523 AsyncLoadingHelper(const AsyncLoadingHelper&) = delete;
524 AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete;
526 AsyncLoadingHelper(AsyncLoadingHelper&& rhs);
527 AsyncLoadingHelper& operator=(AsyncLoadingHelper&&rhs) = delete;
531 * @brief Main constructor that used by all other constructors
533 AsyncLoadingHelper(Toolkit::AsyncImageLoader loader,
534 TextureManager& textureManager,
535 AsyncLoadingInfoContainerType&& loadingInfoContainer);
538 * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager.
539 * @param[in] id Loader id
540 * @param[in] pixelBuffer Image data
542 void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer);
545 Toolkit::AsyncImageLoader mLoader;
546 TextureManager& mTextureManager;
547 AsyncLoadingInfoContainerType mLoadingInfoContainer;
553 * Deleted copy constructor.
555 TextureManager( const TextureManager& ) = delete;
558 * Deleted assignment operator.
560 TextureManager& operator=( const TextureManager& rhs ) = delete;
563 * This is called by the TextureManagerUploadObserver when an observer is destroyed.
564 * We use the callback to know when to remove an observer from our notify list.
565 * @param[in] observer The observer that generated the callback
567 void ObserverDestroyed( TextureUploadObserver* observer );
569 private: // Member Variables:
571 AtlasInfoContainerType mAtlasContainer; ///< Used to manage Atlas creation and destruction
572 TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures
573 TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation
575 RoundRobinContainerView<AsyncLoadingHelper> mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
576 RoundRobinContainerView<AsyncLoadingHelper> mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
582 } // namespace Toolkit
586 #endif // DALI_TOOLKIT_TEXTURE_MANAGER_H