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>
48 class ImageAtlasManager;
49 typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
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
111 ~MaskingData() = default;
113 VisualUrl mAlphaMaskUrl;
114 TextureManager::TextureId mAlphaMaskId;
115 float mContentScaleFactor;
127 ~TextureManager() = default;
130 // TextureManager Main API:
132 TextureSet LoadTexture(VisualUrl& url, Dali::ImageDimensions desiredSize,
133 Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode,
134 MaskingData* maskInfo, bool synchronousLoading,
135 TextureManager::TextureId& textureId, Vector4& textureRect,
136 bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU,
137 Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver,
138 AtlasUploadObserver* atlasObserver,
139 ImageAtlasManagerPtr imageAtlasManager);
142 * @brief Requests an image load of the given URL.
144 * The parameters are used to specify how the image is loaded.
145 * The observer has the UploadComplete method called when the load is ready.
147 * When the client has finished with the Texture, Remove() should be called.
149 * @param[in] url The URL of the image to load
150 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
151 * @param[in] fittingMode The FittingMode to use
152 * @param[in] samplingMode The SamplingMode to use
153 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
154 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
155 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
156 * This is called when an image load completes (or fails).
157 * @return A TextureId to use as a handle to reference this Texture
159 TextureId RequestLoad( const VisualUrl& url,
160 const ImageDimensions desiredSize,
161 FittingMode::Type fittingMode,
162 Dali::SamplingMode::Type samplingMode,
163 const UseAtlas useAtlasing,
164 TextureUploadObserver* observer );
167 * @brief Requests an image load of the given URL, when the texture has
168 * have loaded, it will perform a blend with the image mask, and upload
169 * the blended texture.
171 * The parameters are used to specify how the image is loaded.
172 * The observer has the UploadComplete method called when the load is ready.
174 * When the client has finished with the Texture, Remove() should be called.
176 * @param[in] url The URL of the image to load
177 * @param[in] maskTextureId The texture id of an image to mask this with (can be INVALID if no masking required)
178 * @param[in] contentScale The scale factor to apply to the image before masking
179 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
180 * @param[in] fittingMode The FittingMode to use
181 * @param[in] samplingMode The SamplingMode to use
182 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
183 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
184 * @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.
185 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
186 * This is called when an image load completes (or fails).
187 * @return A TextureId to use as a handle to reference this Texture
189 TextureId RequestLoad( const VisualUrl& url,
190 TextureId maskTextureId,
192 const ImageDimensions desiredSize,
193 FittingMode::Type fittingMode,
194 Dali::SamplingMode::Type samplingMode,
195 const UseAtlas useAtlasing,
197 TextureUploadObserver* observer );
200 * Requests a masking image to be loaded. This mask is not uploaded to GL,
201 * instead, it is stored in CPU memory, and can be used for CPU blending.
203 TextureId RequestMaskLoad( const VisualUrl& maskUrl );
206 * @brief Remove a Texture from the TextureManager.
208 * Textures are cached and therefore only the removal of the last
209 * occurrence of a Texture will cause its removal internally.
211 * @param[in] textureId The ID of the Texture to remove.
213 void Remove( const TextureManager::TextureId textureId );
216 * Get the visualUrl associated with the texture id
218 const VisualUrl& GetVisualUrl( TextureId textureId );
221 * @brief Get the current state of a texture
222 * @param[in] textureId The texture id to query
223 * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
226 LoadState GetTextureState( TextureId textureId );
229 * @brief Get the associated texture set if the texture id is valid
230 * @param[in] textureId The texture Id to look up
231 * @return the associated texture set, or an empty handle if textureId is not valid
233 TextureSet GetTextureSet( TextureId textureId );
236 * Adds an external texture to the texture manager
237 * @param[in] texture The texture to add
238 * @return string containing the URL for the texture
240 std::string AddExternalTexture( TextureSet& texture );
243 * Removes an external texture from texture manager
244 * @param[in] url The string containing the texture to remove
245 * @return handle to the texture
247 TextureSet RemoveExternalTexture( const std::string& url );
252 * @brief Requests an image load of the given URL, when the texture has
253 * have loaded, if there is a valid maskTextureId, it will perform a
254 * CPU blend with the mask, and upload the blend texture.
256 * The parameters are used to specify how the image is loaded.
257 * The observer has the UploadComplete method called when the load is ready.
259 * When the client has finished with the Texture, Remove() should be called.
261 * @param[in] url The URL of the image to load
262 * @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
263 * @param[in] contentScale The scaling factor to apply to the content when masking
264 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
265 * @param[in] fittingMode The FittingMode to use
266 * @param[in] samplingMode The SamplingMode to use
267 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
268 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
269 * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before masking.
270 * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU
271 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
272 * This is called when an image load completes (or fails).
273 * @return A TextureId to use as a handle to reference this Texture
275 TextureId RequestLoadInternal(
276 const VisualUrl& url,
277 TextureId maskTextureId,
279 const ImageDimensions desiredSize,
280 FittingMode::Type fittingMode,
281 Dali::SamplingMode::Type samplingMode,
284 StorageType storageType,
285 TextureUploadObserver* observer );
288 typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
291 * @brief This struct is used to manage the life-cycle of Texture loading and caching.
295 TextureInfo( TextureId textureId,
296 TextureId maskTextureId,
297 const VisualUrl& url,
298 ImageDimensions desiredSize,
300 FittingMode::Type fittingMode,
301 Dali::SamplingMode::Type samplingMode,
302 bool loadSynchronously,
305 TextureManager::TextureHash hash )
307 desiredSize( desiredSize ),
308 useSize( desiredSize ),
309 atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
310 textureId( textureId ),
311 maskTextureId( maskTextureId ),
313 scaleFactor( scaleFactor ),
314 referenceCount( 1u ),
315 loadState( NOT_STARTED ),
316 fittingMode( fittingMode ),
317 samplingMode( samplingMode ),
318 storageType( UPLOAD_TO_TEXTURE ),
319 loadSynchronously( loadSynchronously ),
320 useAtlas( useAtlas ),
321 cropToMask( cropToMask )
326 * Container type used to store all observer clients of this Texture
328 typedef Dali::Vector< TextureUploadObserver* > ObserverListType;
330 ObserverListType observerList; ///< Container used to store all observer clients of this Texture
331 Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any)
332 Devel::PixelBuffer pixelBuffer;///< The PixelBuffer holding the image data (May be empty after upload)
333 TextureSet textureSet; ///< The TextureSet holding the Texture
334 VisualUrl url; ///< The URL of the image
335 ImageDimensions desiredSize; ///< The size requested
336 ImageDimensions useSize; ///< The size used
337 Vector4 atlasRect; ///< The atlas rect used if atlased
338 TextureId textureId; ///< The TextureId associated with this Texture
339 TextureId maskTextureId; ///< The mask TextureId to be applied on load
340 TextureManager::TextureHash hash; ///< The hash used to cache this Texture
341 float scaleFactor; ///< The scale factor to apply to the Texture when masking
342 int16_t referenceCount; ///< The reference count of clients using this Texture
343 LoadState loadState:3; ///< The load state showing the load progress of the Texture
344 FittingMode::Type fittingMode:2; ///< The requested FittingMode
345 Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
346 StorageType storageType:1; ///< CPU storage / GPU upload;
347 bool loadSynchronously:1; ///< True if synchronous loading was requested
348 UseAtlas useAtlas:1; ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
349 bool cropToMask:1; ///< true if the image should be cropped to the mask size.
355 * Struct to hold information about a requested Async load.
356 * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id.
358 struct AsyncLoadingInfo
360 AsyncLoadingInfo( TextureId textureId )
361 : textureId( textureId ),
366 TextureId textureId; ///< The external Texture Id assigned to this load
367 unsigned short loadId; ///< The load Id used by the async loader to reference this load
372 typedef std::deque<AsyncLoadingInfo> AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress
373 typedef std::vector<TextureInfo> TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
376 * @brief Used internally to initiate a load.
377 * @param[in] textureInfo The TextureInfo struct associated with the Texture
378 * @return True if the load was initiated
380 bool LoadTexture( TextureInfo& textureInfo );
383 * Add the observer to the observer list
384 * @param[in] textureInfo The TextureInfo struct associated with the texture
385 * observer The observer wishing to observe the texture upload
387 void ObserveTexture( TextureInfo & textureInfo, TextureUploadObserver* observer );
390 * @brief This signal handler is called when the async local loader finishes loading.
391 * @param[in] id This is the async image loaders Id
392 * @param[in] pixelBuffer The loaded image data
394 void AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
397 * @brief This signal handler is called when the async local loader finishes loading.
398 * @param[in] id This is the async image loaders Id
399 * @param[in] pixelBuffer The loaded image data
401 void AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
404 * Common method to handle loading completion
405 * @param[in] container The Async loading container
406 * @param[in] id This is the async image loaders Id
407 * @param[in] pixelBuffer The loaded image data
409 void AsyncLoadComplete( AsyncLoadingInfoContainerType& container, uint32_t id, Devel::PixelBuffer pixelBuffer );
412 * @brief Performs Post-Load steps including atlasing.
413 * @param[in] textureInfo The struct associated with this Texture
414 * @param[in] pixelBuffer The image pixelBuffer
415 * @return True if successful
417 void PostLoad( TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer );
420 * Check if there is a texture waiting to be masked. If there
421 * is then apply this mask and upload it.
422 * @param[in] maskTextureInfo The texture info of the mask that has just loaded.
424 void CheckForWaitingTexture( TextureInfo& maskTextureInfo );
427 * Apply the mask to the pixelBuffer.
428 * @param[in] pixelBuffer The pixelBuffer to apply the mask to
429 * @param[in] maskTextureId The texture id of the mask.
430 * @param[in] contentScale The factor to scale the content
431 * @param[in] cropToMask Whether to crop the content to the mask size
433 void ApplyMask( Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
434 float contentScale, bool cropToMask );
437 * Upload the texture specified in pixelBuffer to the appropriate location
438 * @param[in] pixelBuffer The image data to upload
439 * @param[in] textureInfo The texture info containing the location to
442 void UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo );
445 * Mark the texture as complete, and inform observers
446 * @param[in] textureInfo The struct associated with this Texture
448 void UploadComplete( TextureInfo& textureInfo );
451 * Notify the current observers that the texture upload is complete,
452 * then remove the observers from the list.
453 * @param[in] textureInfo The struct associated with this Texture
454 * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU
456 void NotifyObservers( TextureInfo& textureInfo, bool success );
459 * @brief Generates a new, unique TextureId
460 * @return A unique TextureId
462 TextureManager::TextureId GenerateUniqueTextureId();
465 * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
466 * @param[in] textureId The TextureId to look up
467 * @return The cache index
469 int GetCacheIndexFromId( TextureId textureId );
473 * @brief Generates a hash for caching based on the input parameters.
474 * Only applies size, fitting mode andsampling mode if the size is specified.
475 * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
476 * Always applies useAtlas.
477 * @param[in] url The URL of the image to load
478 * @param[in] size The image size
479 * @param[in] fittingMode The FittingMode to use
480 * @param[in] samplingMode The SamplingMode to use
481 * @param[in] useAtlas True if atlased
482 * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
483 * @return A hash of the provided data for caching.
485 TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
486 const FittingMode::Type fittingMode,
487 const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
488 TextureId maskTextureId );
490 * @brief Looks up a cached texture by its hash.
491 * If found, the given parameters are used to check there is no hash-collision.
492 * @param[in] hash The hash to look up
493 * @param[in] url The URL of the image to load
494 * @param[in] size The image size
495 * @param[in] fittingMode The FittingMode to use
496 * @param[in] samplingMode The SamplingMode to use
497 * @param[in] useAtlas True if atlased
498 * @param[in] maskTextureId Optional texture ID to use to mask this image
499 * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
501 TextureManager::TextureId FindCachedTexture(
502 const TextureManager::TextureHash hash,
503 const std::string& url,
504 const ImageDimensions size,
505 const FittingMode::Type fittingMode,
506 const Dali::SamplingMode::Type samplingMode,
508 TextureId maskTextureId );
513 * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container
515 class AsyncLoadingHelper : public ConnectionTracker
519 * @brief Create an AsyncLoadingHelper.
520 * @param[in] textureManager Reference to the texture manager
522 AsyncLoadingHelper(TextureManager& textureManager);
525 * @brief Load a new texture.
526 * @param[in] textureId TextureId to reference the texture that will be loaded
527 * @param[in] url The URL of the image to load
528 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
529 * @param[in] fittingMode The FittingMode to use
530 * @param[in] samplingMode The SamplingMode to use
531 * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, e.g., from portrait to landscape
533 void Load(TextureId textureId,
534 const VisualUrl& url,
535 ImageDimensions desiredSize,
536 FittingMode::Type fittingMode,
537 SamplingMode::Type samplingMode,
538 bool orientationCorrection);
541 AsyncLoadingHelper(const AsyncLoadingHelper&) = delete;
542 AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete;
544 AsyncLoadingHelper(AsyncLoadingHelper&& rhs);
545 AsyncLoadingHelper& operator=(AsyncLoadingHelper&&rhs) = delete;
549 * @brief Main constructor that used by all other constructors
551 AsyncLoadingHelper(Toolkit::AsyncImageLoader loader,
552 TextureManager& textureManager,
553 AsyncLoadingInfoContainerType&& loadingInfoContainer);
556 * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager.
557 * @param[in] id Loader id
558 * @param[in] pixelBuffer Image data
560 void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer);
563 Toolkit::AsyncImageLoader mLoader;
564 TextureManager& mTextureManager;
565 AsyncLoadingInfoContainerType mLoadingInfoContainer;
568 struct ExternalTextureInfo
571 TextureSet textureSet;
577 * Deleted copy constructor.
579 TextureManager( const TextureManager& ) = delete;
582 * Deleted assignment operator.
584 TextureManager& operator=( const TextureManager& rhs ) = delete;
587 * This is called by the TextureManagerUploadObserver when an observer is destroyed.
588 * We use the callback to know when to remove an observer from our notify list.
589 * @param[in] observer The observer that generated the callback
591 void ObserverDestroyed( TextureUploadObserver* observer );
593 private: // Member Variables:
595 TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures
596 RoundRobinContainerView< AsyncLoadingHelper > mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
597 RoundRobinContainerView< AsyncLoadingHelper > mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
598 std::vector< ExternalTextureInfo > mExternalTextures; ///< Externally provided textures
599 TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation
606 } // namespace Toolkit
610 #endif // DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H