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.
25 #include <dali/public-api/common/dali-vector.h>
26 #include <dali/public-api/object/ref-object.h>
27 #include <dali/public-api/rendering/texture-set.h>
28 #include <dali/devel-api/common/owner-container.h>
29 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
32 #include <dali-toolkit/devel-api/image-loader/async-image-loader-devel.h>
33 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
34 #include <dali-toolkit/public-api/image-loader/async-image-loader.h>
35 #include <dali-toolkit/internal/visuals/texture-upload-observer.h>
36 #include <dali-toolkit/internal/visuals/visual-url.h>
37 #include <dali-toolkit/internal/helpers/round-robin-container-view.h>
38 #include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
49 class ImageAtlasManager;
50 typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
53 * The TextureManager provides a common Image loading API for Visuals.
55 * The TextureManager is responsible for providing sync, async, atlased and non-atlased loads.
56 * Texture caching is provided and performed when possible.
57 * Broken Images are automatically provided on load failure.
59 class TextureManager : public ConnectionTracker
63 typedef int32_t TextureId; ///< The TextureId type. This is used as a handle to refer to a particular Texture.
64 static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error
67 * Whether the texture should be atlased or uploaded into it's own GPU texture
76 * Whether the pixel data should be kept in TextureManager, or uploaded for rendering
85 * Whether the texture should be loaded synchronously or asynchronously.
94 * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle.
98 NOT_STARTED, ///< Default
99 LOADING, ///< Loading has been started, but not finished.
100 LOAD_FINISHED, ///< Loading has finished. (for CPU storage only)
101 WAITING_FOR_MASK,///< Loading has finished, but waiting for mask image
102 UPLOADED, ///< Uploaded and ready. (For GPU upload only)
103 CANCELLED, ///< Removed before loading completed
104 LOAD_FAILED ///< Async loading failed, e.g. connection problem
108 * @breif Types of reloading policies
110 enum class ReloadPolicy
112 CACHED = 0, ///< Loads cached texture if it exists.
113 FORCED ///< Forces reloading of texture.
121 ~MaskingData() = default;
123 VisualUrl mAlphaMaskUrl;
124 TextureManager::TextureId mAlphaMaskId;
125 float mContentScaleFactor;
128 using MaskingDataPointer = std::unique_ptr<MaskingData>;
138 ~TextureManager() = default;
141 // TextureManager Main API:
143 TextureSet LoadTexture(const VisualUrl& url, Dali::ImageDimensions desiredSize,
144 Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode,
145 const MaskingDataPointer& maskInfo, bool synchronousLoading,
146 TextureManager::TextureId& textureId, Vector4& textureRect,
147 bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU,
148 Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver,
149 AtlasUploadObserver* atlasObserver,
150 ImageAtlasManagerPtr imageAtlasManager,
151 bool orientationCorrection,
152 TextureManager::ReloadPolicy reloadPolicy );
155 * @brief Requests an image load of the given URL.
157 * The parameters are used to specify how the image is loaded.
158 * The observer has the UploadComplete method called when the load is ready.
160 * When the client has finished with the Texture, Remove() should be called.
162 * @param[in] url The URL of the image to load
163 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
164 * @param[in] fittingMode The FittingMode to use
165 * @param[in] samplingMode The SamplingMode to use
166 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
167 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
168 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
169 * This is called when an image load completes (or fails).
170 * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
171 * @param[in] reloadPolicy Forces a reload of the texture even if already cached
172 * @return A TextureId to use as a handle to reference this Texture
174 TextureId RequestLoad( const VisualUrl& url,
175 const ImageDimensions desiredSize,
176 FittingMode::Type fittingMode,
177 Dali::SamplingMode::Type samplingMode,
178 const UseAtlas useAtlasing,
179 TextureUploadObserver* observer,
180 bool orientationCorrection,
181 TextureManager::ReloadPolicy reloadPolicy );
184 * @brief Requests an image load of the given URL, when the texture has
185 * have loaded, it will perform a blend with the image mask, and upload
186 * the blended texture.
188 * The parameters are used to specify how the image is loaded.
189 * The observer has the UploadComplete method called when the load is ready.
191 * When the client has finished with the Texture, Remove() should be called.
193 * @param[in] url The URL of the image to load
194 * @param[in] maskTextureId The texture id of an image to mask this with
195 * (can be INVALID if no masking required)
196 * @param[in] contentScale The scale factor to apply to the image before masking
197 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
198 * @param[in] fittingMode The FittingMode to use
199 * @param[in] samplingMode The SamplingMode to use
200 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still
201 * be loaded, and marked successful,
202 * but "useAtlasing" will be set to false in the "UploadCompleted" callback from
203 * the TextureManagerUploadObserver.
204 * @param[in] cropToMask Only used with masking, this will crop the scaled image to the mask size.
205 * If false, then the mask will be scaled to fit the image before being applied.
206 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted"
208 * This is called when an image load completes (or fails).
209 * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
210 * @param[in] reloadPolicy Forces a reload of the texture even if already cached
211 * @return A TextureId to use as a handle to reference this Texture
213 TextureId RequestLoad( const VisualUrl& url,
214 TextureId maskTextureId,
216 const ImageDimensions desiredSize,
217 FittingMode::Type fittingMode,
218 Dali::SamplingMode::Type samplingMode,
219 const UseAtlas useAtlasing,
221 TextureUploadObserver* observer,
222 bool orientationCorrection,
223 TextureManager::ReloadPolicy reloadPolicy );
226 * Requests a masking image to be loaded. This mask is not uploaded to GL,
227 * instead, it is stored in CPU memory, and can be used for CPU blending.
229 TextureId RequestMaskLoad( const VisualUrl& maskUrl );
232 * @brief Remove a Texture from the TextureManager.
234 * Textures are cached and therefore only the removal of the last
235 * occurrence of a Texture will cause its removal internally.
237 * @param[in] textureId The ID of the Texture to remove.
239 void Remove( const TextureManager::TextureId textureId );
242 * Get the visualUrl associated with the texture id
244 const VisualUrl& GetVisualUrl( TextureId textureId );
247 * @brief Get the current state of a texture
248 * @param[in] textureId The texture id to query
249 * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
252 LoadState GetTextureState( TextureId textureId );
255 * @brief Get the associated texture set if the texture id is valid
256 * @param[in] textureId The texture Id to look up
257 * @return the associated texture set, or an empty handle if textureId is not valid
259 TextureSet GetTextureSet( TextureId textureId );
262 * Adds an external texture to the texture manager
263 * @param[in] texture The texture to add
264 * @return string containing the URL for the texture
266 std::string AddExternalTexture( TextureSet& texture );
269 * Removes an external texture from texture manager
270 * @param[in] url The string containing the texture to remove
271 * @return handle to the texture
273 TextureSet RemoveExternalTexture( const std::string& url );
278 * @brief Requests an image load of the given URL, when the texture has
279 * have loaded, if there is a valid maskTextureId, it will perform a
280 * CPU blend with the mask, and upload the blend texture.
282 * The parameters are used to specify how the image is loaded.
283 * The observer has the UploadComplete method called when the load is ready.
285 * When the client has finished with the Texture, Remove() should be called.
287 * @param[in] url The URL of the image to load
288 * @param[in] maskTextureId The texture id of an image to use as a mask. If no mask is required, then set
289 * to INVALID_TEXTURE_ID
290 * @param[in] contentScale The scaling factor to apply to the content when masking
291 * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
292 * @param[in] fittingMode The FittingMode to use
293 * @param[in] samplingMode The SamplingMode to use
294 * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be
295 * loaded, and marked successful, but "useAtlasing" will be set to false in the
296 * "UploadCompleted" callback from the TextureManagerUploadObserver.
297 * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before
299 * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU
300 * @param[in] observer The client object should inherit from this and provide the "UploadCompleted"
302 * This is called when an image load completes (or fails).
303 * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
304 * @param[in] reloadPolicy Forces a reload of the texture even if already cached
305 * @return A TextureId to use as a handle to reference this Texture
307 TextureId RequestLoadInternal(
308 const VisualUrl& url,
309 TextureId maskTextureId,
311 const ImageDimensions desiredSize,
312 FittingMode::Type fittingMode,
313 Dali::SamplingMode::Type samplingMode,
316 StorageType storageType,
317 TextureUploadObserver* observer,
318 bool orientationCorrection,
319 TextureManager::ReloadPolicy reloadPolicy );
322 * @brief Get the current state of a texture
323 * @param[in] textureId The texture id to query
324 * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
327 LoadState GetTextureStateInternal( TextureId textureId );
329 typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
332 * @brief This struct is used to manage the life-cycle of Texture loading and caching.
336 TextureInfo( TextureId textureId,
337 TextureId maskTextureId,
338 const VisualUrl& url,
339 ImageDimensions desiredSize,
341 FittingMode::Type fittingMode,
342 Dali::SamplingMode::Type samplingMode,
343 bool loadSynchronously,
346 TextureManager::TextureHash hash,
347 bool orientationCorrection )
349 desiredSize( desiredSize ),
350 useSize( desiredSize ),
351 atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
352 textureId( textureId ),
353 maskTextureId( maskTextureId ),
355 scaleFactor( scaleFactor ),
356 referenceCount( 1u ),
357 loadState( NOT_STARTED ),
358 fittingMode( fittingMode ),
359 samplingMode( samplingMode ),
360 storageType( UPLOAD_TO_TEXTURE ),
361 loadSynchronously( loadSynchronously ),
362 useAtlas( useAtlas ),
363 cropToMask( cropToMask ),
364 orientationCorrection( true )
369 * Container type used to store all observer clients of this Texture
371 typedef Dali::Vector< TextureUploadObserver* > ObserverListType;
373 ObserverListType observerList; ///< Container used to store all observer clients of this Texture
374 Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any)
375 Devel::PixelBuffer pixelBuffer;///< The PixelBuffer holding the image data (May be empty after upload)
376 TextureSet textureSet; ///< The TextureSet holding the Texture
377 VisualUrl url; ///< The URL of the image
378 ImageDimensions desiredSize; ///< The size requested
379 ImageDimensions useSize; ///< The size used
380 Vector4 atlasRect; ///< The atlas rect used if atlased
381 TextureId textureId; ///< The TextureId associated with this Texture
382 TextureId maskTextureId; ///< The mask TextureId to be applied on load
383 TextureManager::TextureHash hash; ///< The hash used to cache this Texture
384 float scaleFactor; ///< The scale factor to apply to the Texture when masking
385 int16_t referenceCount; ///< The reference count of clients using this Texture
386 LoadState loadState:3; ///< The load state showing the load progress of the Texture
387 FittingMode::Type fittingMode:2; ///< The requested FittingMode
388 Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
389 StorageType storageType:1; ///< CPU storage / GPU upload;
390 bool loadSynchronously:1; ///< True if synchronous loading was requested
391 UseAtlas useAtlas:1; ///< USE_ATLAS if an atlas was requested.
392 ///< This is updated to false if atlas is not used
393 bool cropToMask:1; ///< true if the image should be cropped to the mask size.
394 bool orientationCorrection:1; ///< true if the image should be rotated to match exif orientation data
400 * Struct to hold information about a requested Async load.
401 * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id.
403 struct AsyncLoadingInfo
405 AsyncLoadingInfo( TextureId textureId )
406 : textureId( textureId ),
411 TextureId textureId; ///< The external Texture Id assigned to this load
412 uint32_t loadId; ///< The load Id used by the async loader to reference this load
417 typedef std::deque<AsyncLoadingInfo> AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress
418 typedef std::vector<TextureInfo> TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
421 * @brief Used internally to initiate a load.
422 * @param[in] textureInfo The TextureInfo struct associated with the Texture
423 * @return True if the load was initiated
425 bool LoadTexture( TextureInfo& textureInfo );
428 * Add the observer to the observer list
429 * @param[in] textureInfo The TextureInfo struct associated with the texture
430 * observer The observer wishing to observe the texture upload
432 void ObserveTexture( TextureInfo & textureInfo, TextureUploadObserver* observer );
435 * @brief This signal handler is called when the async local loader finishes loading.
436 * @param[in] id This is the async image loaders Id
437 * @param[in] pixelBuffer The loaded image data
439 void AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
442 * @brief This signal handler is called when the async local loader finishes loading.
443 * @param[in] id This is the async image loaders Id
444 * @param[in] pixelBuffer The loaded image data
446 void AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
449 * Common method to handle loading completion
450 * @param[in] container The Async loading container
451 * @param[in] id This is the async image loaders Id
452 * @param[in] pixelBuffer The loaded image data
454 void AsyncLoadComplete( AsyncLoadingInfoContainerType& container, uint32_t id, Devel::PixelBuffer pixelBuffer );
457 * @brief Performs Post-Load steps including atlasing.
458 * @param[in] textureInfo The struct associated with this Texture
459 * @param[in] pixelBuffer The image pixelBuffer
460 * @return True if successful
462 void PostLoad( TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer );
465 * Check if there is a texture waiting to be masked. If there
466 * is then apply this mask and upload it.
467 * @param[in] maskTextureInfo The texture info of the mask that has just loaded.
469 void CheckForWaitingTexture( TextureInfo& maskTextureInfo );
472 * Apply the mask to the pixelBuffer.
473 * @param[in] pixelBuffer The pixelBuffer to apply the mask to
474 * @param[in] maskTextureId The texture id of the mask.
475 * @param[in] contentScale The factor to scale the content
476 * @param[in] cropToMask Whether to crop the content to the mask size
478 void ApplyMask( Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
479 float contentScale, bool cropToMask );
482 * Upload the texture specified in pixelBuffer to the appropriate location
483 * @param[in] pixelBuffer The image data to upload
484 * @param[in] textureInfo The texture info containing the location to
487 void UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo );
490 * Mark the texture as complete, and inform observers
491 * @param[in] textureInfo The struct associated with this Texture
493 void UploadComplete( TextureInfo& textureInfo );
496 * Notify the current observers that the texture upload is complete,
497 * then remove the observers from the list.
498 * @param[in] textureInfo The struct associated with this Texture
499 * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU
501 void NotifyObservers( TextureInfo& textureInfo, bool success );
504 * @brief Generates a new, unique TextureId
505 * @return A unique TextureId
507 TextureManager::TextureId GenerateUniqueTextureId();
510 * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
511 * @param[in] textureId The TextureId to look up
512 * @return The cache index
514 int GetCacheIndexFromId( TextureId textureId );
518 * @brief Generates a hash for caching based on the input parameters.
519 * Only applies size, fitting mode andsampling mode if the size is specified.
520 * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
521 * Always applies useAtlas.
522 * @param[in] url The URL of the image to load
523 * @param[in] size The image size
524 * @param[in] fittingMode The FittingMode to use
525 * @param[in] samplingMode The SamplingMode to use
526 * @param[in] useAtlas True if atlased
527 * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
528 * @return A hash of the provided data for caching.
530 TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
531 const FittingMode::Type fittingMode,
532 const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
533 TextureId maskTextureId );
535 * @brief Looks up a cached texture by its hash.
536 * If found, the given parameters are used to check there is no hash-collision.
537 * @param[in] hash The hash to look up
538 * @param[in] url The URL of the image to load
539 * @param[in] size The image size
540 * @param[in] fittingMode The FittingMode to use
541 * @param[in] samplingMode The SamplingMode to use
542 * @param[in] useAtlas True if atlased
543 * @param[in] maskTextureId Optional texture ID to use to mask this image
544 * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
546 TextureManager::TextureId FindCachedTexture(
547 const TextureManager::TextureHash hash,
548 const std::string& url,
549 const ImageDimensions size,
550 const FittingMode::Type fittingMode,
551 const Dali::SamplingMode::Type samplingMode,
553 TextureId maskTextureId );
558 * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container
560 class AsyncLoadingHelper : public ConnectionTracker
564 * @brief Create an AsyncLoadingHelper.
565 * @param[in] textureManager Reference to the texture manager
567 AsyncLoadingHelper(TextureManager& textureManager);
570 * @brief Load a new texture.
571 * @param[in] textureId TextureId to reference the texture that will be loaded
572 * @param[in] url The URL of the image to load
573 * @param[in] desiredSize The size the image is likely to appear at.
574 * This can be set to 0,0 for automatic
575 * @param[in] fittingMode The FittingMode to use
576 * @param[in] samplingMode The SamplingMode to use
577 * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image,
578 * e.g., from portrait to landscape
580 void Load(TextureId textureId,
581 const VisualUrl& url,
582 ImageDimensions desiredSize,
583 FittingMode::Type fittingMode,
584 SamplingMode::Type samplingMode,
585 bool orientationCorrection);
588 AsyncLoadingHelper(const AsyncLoadingHelper&) = delete;
589 AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete;
591 AsyncLoadingHelper(AsyncLoadingHelper&& rhs);
592 AsyncLoadingHelper& operator=(AsyncLoadingHelper&&rhs) = delete;
596 * @brief Main constructor that used by all other constructors
598 AsyncLoadingHelper(Toolkit::AsyncImageLoader loader,
599 TextureManager& textureManager,
600 AsyncLoadingInfoContainerType&& loadingInfoContainer);
603 * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager.
604 * @param[in] id Loader id
605 * @param[in] pixelBuffer Image data
607 void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer);
610 Toolkit::AsyncImageLoader mLoader;
611 TextureManager& mTextureManager;
612 AsyncLoadingInfoContainerType mLoadingInfoContainer;
615 struct ExternalTextureInfo
618 TextureSet textureSet;
624 * Deleted copy constructor.
626 TextureManager( const TextureManager& ) = delete;
629 * Deleted assignment operator.
631 TextureManager& operator=( const TextureManager& rhs ) = delete;
634 * This is called by the TextureManagerUploadObserver when an observer is destroyed.
635 * We use the callback to know when to remove an observer from our notify list.
636 * @param[in] observer The observer that generated the callback
638 void ObserverDestroyed( TextureUploadObserver* observer );
640 private: // Member Variables:
642 TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures
643 RoundRobinContainerView< AsyncLoadingHelper > mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
644 RoundRobinContainerView< AsyncLoadingHelper > mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
645 std::vector< ExternalTextureInfo > mExternalTextures; ///< Externally provided textures
646 TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation
653 } // namespace Toolkit
657 #endif // DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H