Merge "Prevent keypad enter key inserting when textfield is in password mode" into... devel/master
authorChihun Jeong <chihun.jeong@samsung.com>
Tue, 24 Sep 2024 10:13:15 +0000 (10:13 +0000)
committerGerrit Code Review <gerrit@review>
Tue, 24 Sep 2024 10:13:15 +0000 (10:13 +0000)
369 files changed:
.ahub/sam/exclude.txt [new file with mode: 0755]
automated-tests/CMakeLists.txt.in
automated-tests/resources/headerOK-invalidArray.ktx [new file with mode: 0644]
automated-tests/resources/headerOnly.ktx [new file with mode: 0644]
automated-tests/src/dali-physics2d/CMakeLists.txt
automated-tests/src/dali-physics3d/CMakeLists.txt
automated-tests/src/dali-scene3d-internal/CMakeLists.txt
automated-tests/src/dali-scene3d-internal/utc-Dali-SceneViewImpl.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/CMakeLists.txt
automated-tests/src/dali-scene3d/utc-Dali-FacialAnimation.cpp
automated-tests/src/dali-scene3d/utc-Dali-KtxLoader.cpp
automated-tests/src/dali-scene3d/utc-Dali-MeshDefinition.cpp
automated-tests/src/dali-scene3d/utc-Dali-NavigationMesh.cpp
automated-tests/src/dali-scene3d/utc-Dali-Panel.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-SceneView.cpp
automated-tests/src/dali-shader-generator/CMakeLists.txt
automated-tests/src/dali-toolkit-internal/CMakeLists.txt
automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dummy-visual.cpp
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dummy-visual.h
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Accessible.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Value.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-ColorConversion.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-DebugRendering.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-ImageVisualShaderFeatureBuilder.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-PropertyHelper.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-RenderEffect-internal.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit-internal/utc-Dali-SvgLoader.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-CharacterSpacing.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Ellipsis.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Layout.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-MultiLanguage.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Segmentation.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-ViewModel.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextField-internal.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextLabel-internal.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextSelectionPopup-internal.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Visuals-internal.cpp
automated-tests/src/dali-toolkit-styling/CMakeLists.txt
automated-tests/src/dali-toolkit-third-party/CMakeLists.txt
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-application.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-application.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-command-buffer.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-platform-abstraction.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-platform-abstraction.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-render-surface.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-render-surface.h [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-adaptor-impl.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-adaptor.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-scene-holder-impl.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-scene-holder.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-abstraction.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-video-player.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-web-engine.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-window.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-window.h
automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-CanvasView.cpp
automated-tests/src/dali-toolkit/utc-Dali-ControlImpl.cpp
automated-tests/src/dali-toolkit/utc-Dali-GaussianBlurView.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.cpp
automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-SvgVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextGeometry.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextLabel-Async.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
automated-tests/src/dali-toolkit/utc-Dali-WebView.cpp
build/tizen/CMakeLists.txt
build/tizen/docs/dali.doxy.in
dali-scene3d/dali-scene3d.h
dali-scene3d/internal/common/image-resource-loader.cpp
dali-scene3d/internal/common/image-resource-loader.h
dali-scene3d/internal/common/model-load-task.cpp
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/controls/panel/panel-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/controls/panel/panel-impl.h [new file with mode: 0644]
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/internal/event/collider-mesh-processor-impl.cpp
dali-scene3d/internal/event/collider-mesh-processor-impl.h
dali-scene3d/internal/event/collider-mesh-processor.cpp
dali-scene3d/internal/event/collider-mesh-processor.h
dali-scene3d/internal/file.list
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.vert
dali-scene3d/internal/graphics/shaders/scene3d-joint-debug.frag
dali-scene3d/internal/graphics/shaders/scene3d-joint-debug.vert
dali-scene3d/internal/model-components/model-node-tree-utility.cpp [new file with mode: 0644]
dali-scene3d/internal/model-components/model-node-tree-utility.h [new file with mode: 0644]
dali-scene3d/internal/model-components/model-primitive-impl.cpp
dali-scene3d/public-api/controls/model/model.h
dali-scene3d/public-api/controls/panel/panel.cpp [new file with mode: 0644]
dali-scene3d/public-api/controls/panel/panel.h [new file with mode: 0644]
dali-scene3d/public-api/controls/scene-view/scene-view.cpp
dali-scene3d/public-api/controls/scene-view/scene-view.h
dali-scene3d/public-api/file.list
dali-scene3d/public-api/loader/environment-definition.cpp
dali-scene3d/public-api/loader/environment-definition.h
dali-scene3d/public-api/loader/facial-animation-loader.cpp
dali-scene3d/public-api/loader/ktx-loader.cpp
dali-scene3d/public-api/loader/material-definition.cpp
dali-scene3d/public-api/loader/mesh-definition.cpp
dali-scene3d/public-api/loader/node-definition.cpp
dali-scene3d/public-api/loader/node-definition.h
dali-scene3d/public-api/loader/shader-definition.cpp
dali-scene3d/public-api/loader/shader-definition.h
dali-scene3d/public-api/loader/shader-manager.cpp
dali-scene3d/public-api/loader/utils.cpp
dali-scene3d/public-api/model-components/model-node.h
dali-toolkit/dali-toolkit.h
dali-toolkit/devel-api/asset-manager/asset-manager.h
dali-toolkit/devel-api/controls/canvas-view/canvas-view.cpp
dali-toolkit/devel-api/controls/canvas-view/canvas-view.h
dali-toolkit/devel-api/controls/control-accessible.cpp
dali-toolkit/devel-api/controls/control-accessible.h
dali-toolkit/devel-api/controls/control-devel.cpp
dali-toolkit/devel-api/controls/control-devel.h
dali-toolkit/devel-api/controls/text-controls/text-label-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-label-devel.h
dali-toolkit/devel-api/controls/web-view/web-view.cpp
dali-toolkit/devel-api/controls/web-view/web-view.h
dali-toolkit/devel-api/file.list
dali-toolkit/devel-api/image-loader/texture-manager.cpp
dali-toolkit/devel-api/text/text-utils-devel.cpp
dali-toolkit/devel-api/utility/npatch-helper.cpp
dali-toolkit/devel-api/visual-factory/precompile-shader-option.cpp [new file with mode: 0644]
dali-toolkit/devel-api/visual-factory/precompile-shader-option.h [new file with mode: 0644]
dali-toolkit/devel-api/visual-factory/visual-base.cpp
dali-toolkit/devel-api/visual-factory/visual-base.h
dali-toolkit/devel-api/visual-factory/visual-factory.cpp
dali-toolkit/devel-api/visual-factory/visual-factory.h
dali-toolkit/devel-api/visuals/color-visual-properties-devel.h
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/devel-api/visuals/visual-properties-devel.h
dali-toolkit/internal/builder/builder-signals.cpp
dali-toolkit/internal/builder/dictionary.h
dali-toolkit/internal/controls/buttons/check-box-button-impl.cpp
dali-toolkit/internal/controls/buttons/push-button-impl.cpp
dali-toolkit/internal/controls/buttons/radio-button-impl.cpp
dali-toolkit/internal/controls/buttons/toggle-button-impl.cpp
dali-toolkit/internal/controls/canvas-view/canvas-view-impl.cpp
dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/control/control-data-impl.h
dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp
dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.h
dali-toolkit/internal/controls/gl-view/gl-view-impl.cpp
dali-toolkit/internal/controls/gl-view/gl-view-impl.h
dali-toolkit/internal/controls/image-view/image-view-impl.cpp
dali-toolkit/internal/controls/image-view/image-view-impl.h
dali-toolkit/internal/controls/popup/popup-impl.cpp
dali-toolkit/internal/controls/render-effects/blur-effect-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/render-effects/blur-effect-impl.h [new file with mode: 0644]
dali-toolkit/internal/controls/render-effects/render-effect-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/render-effects/render-effect-impl.h [new file with mode: 0644]
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.h
dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.cpp
dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.h
dali-toolkit/internal/controls/video-view/video-view-impl.cpp
dali-toolkit/internal/controls/web-view/web-view-impl.cpp
dali-toolkit/internal/controls/web-view/web-view-impl.h
dali-toolkit/internal/file.list
dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp
dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h
dali-toolkit/internal/graphics/shaders/arc-visual-butt-cap-shader.frag
dali-toolkit/internal/graphics/shaders/arc-visual-round-cap-shader.frag
dali-toolkit/internal/graphics/shaders/blur-effect.frag [new file with mode: 0644]
dali-toolkit/internal/graphics/shaders/border-visual-anti-aliasing-shader.frag
dali-toolkit/internal/graphics/shaders/border-visual-shader.frag
dali-toolkit/internal/graphics/shaders/color-visual-shader.frag
dali-toolkit/internal/graphics/shaders/color-visual-shader.vert
dali-toolkit/internal/graphics/shaders/control-renderers.frag
dali-toolkit/internal/graphics/shaders/control-renderers.vert
dali-toolkit/internal/graphics/shaders/gaussian-blur-view.frag
dali-toolkit/internal/graphics/shaders/gradient-visual-shader.frag
dali-toolkit/internal/graphics/shaders/gradient-visual-shader.vert
dali-toolkit/internal/graphics/shaders/image-visual-shader.frag
dali-toolkit/internal/graphics/shaders/image-visual-shader.vert
dali-toolkit/internal/graphics/shaders/mesh-visual-normal-map-shader.frag
dali-toolkit/internal/graphics/shaders/mesh-visual-shader.frag
dali-toolkit/internal/graphics/shaders/mesh-visual-simple-shader.frag
dali-toolkit/internal/graphics/shaders/npatch-visual-mask-shader.frag
dali-toolkit/internal/graphics/shaders/npatch-visual-shader.frag
dali-toolkit/internal/graphics/shaders/primitive-visual-shader.frag
dali-toolkit/internal/graphics/shaders/render-effect.frag [new file with mode: 0644]
dali-toolkit/internal/graphics/shaders/render-effect.vert [new file with mode: 0644]
dali-toolkit/internal/graphics/shaders/text-scroller-shader.frag
dali-toolkit/internal/graphics/shaders/text-visual-shader.frag
dali-toolkit/internal/graphics/shaders/wireframe-visual-shader.frag
dali-toolkit/internal/image-loader/fast-track-loading-task.cpp
dali-toolkit/internal/image-loader/image-url-impl.cpp
dali-toolkit/internal/image-loader/loading-task.cpp
dali-toolkit/internal/text/async-text/async-text-interface.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-loader-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-loader-impl.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-loader.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-loader.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-manager-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-manager-impl.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-manager.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-manager.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-module-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-module-impl.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-module.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/async-text-module.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/text-load-observer.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/text-load-observer.h [new file with mode: 0644]
dali-toolkit/internal/text/async-text/text-loading-task.cpp [new file with mode: 0644]
dali-toolkit/internal/text/async-text/text-loading-task.h [new file with mode: 0644]
dali-toolkit/internal/text/bidirectional-support.cpp
dali-toolkit/internal/text/bidirectional-support.h
dali-toolkit/internal/text/controller/text-controller-impl-model-updater.cpp
dali-toolkit/internal/text/controller/text-controller-impl.cpp
dali-toolkit/internal/text/controller/text-controller-impl.h
dali-toolkit/internal/text/controller/text-controller-relayouter.cpp
dali-toolkit/internal/text/controller/text-controller-text-updater.cpp
dali-toolkit/internal/text/controller/text-controller.cpp
dali-toolkit/internal/text/controller/text-controller.h
dali-toolkit/internal/text/hyphenator.cpp
dali-toolkit/internal/text/hyphenator.h
dali-toolkit/internal/text/layouts/layout-engine.cpp
dali-toolkit/internal/text/layouts/layout-parameters.h
dali-toolkit/internal/text/multi-language-support-impl.cpp
dali-toolkit/internal/text/multi-language-support-impl.h
dali-toolkit/internal/text/multi-language-support.cpp
dali-toolkit/internal/text/multi-language-support.h
dali-toolkit/internal/text/rendering/text-typesetter-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/text/rendering/text-typesetter-impl.h [new file with mode: 0644]
dali-toolkit/internal/text/rendering/text-typesetter.cpp
dali-toolkit/internal/text/rendering/text-typesetter.h
dali-toolkit/internal/text/rendering/view-model.cpp
dali-toolkit/internal/text/rendering/view-model.h
dali-toolkit/internal/text/segmentation.cpp
dali-toolkit/internal/text/segmentation.h
dali-toolkit/internal/text/shaper.cpp
dali-toolkit/internal/text/shaper.h
dali-toolkit/internal/text/text-model-interface.h
dali-toolkit/internal/text/text-model.cpp
dali-toolkit/internal/text/text-model.h
dali-toolkit/internal/text/text-scroller.cpp
dali-toolkit/internal/text/text-view.cpp
dali-toolkit/internal/text/text-view.h
dali-toolkit/internal/text/visual-model-impl.cpp
dali-toolkit/internal/text/visual-model-impl.h
dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.h
dali-toolkit/internal/texture-manager/texture-manager-impl.cpp
dali-toolkit/internal/texture-manager/texture-manager-impl.h
dali-toolkit/internal/transition/transition-set-impl.cpp
dali-toolkit/internal/visuals/animated-gradient/animated-gradient-visual.cpp
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
dali-toolkit/internal/visuals/animated-image/animated-image-visual.h
dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h
dali-toolkit/internal/visuals/arc/arc-visual.cpp
dali-toolkit/internal/visuals/border/border-visual.cpp
dali-toolkit/internal/visuals/color/color-visual-shader-factory.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/color/color-visual-shader-factory.h [new file with mode: 0644]
dali-toolkit/internal/visuals/color/color-visual.cpp
dali-toolkit/internal/visuals/color/color-visual.h
dali-toolkit/internal/visuals/custom-shader-factory.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/custom-shader-factory.h [new file with mode: 0644]
dali-toolkit/internal/visuals/gradient/gradient-visual.cpp
dali-toolkit/internal/visuals/image-visual-shader-feature-builder.cpp [deleted file]
dali-toolkit/internal/visuals/image/image-atlas-manager.cpp [moved from dali-toolkit/internal/visuals/image-atlas-manager.cpp with 97% similarity]
dali-toolkit/internal/visuals/image/image-atlas-manager.h [moved from dali-toolkit/internal/visuals/image-atlas-manager.h with 99% similarity]
dali-toolkit/internal/visuals/image/image-visual-shader-debug.cpp [moved from dali-toolkit/internal/visuals/image-visual-shader-debug.cpp with 99% similarity]
dali-toolkit/internal/visuals/image/image-visual-shader-debug.h [moved from dali-toolkit/internal/visuals/image-visual-shader-debug.h with 100% similarity]
dali-toolkit/internal/visuals/image/image-visual-shader-factory.cpp [moved from dali-toolkit/internal/visuals/image-visual-shader-factory.cpp with 63% similarity]
dali-toolkit/internal/visuals/image/image-visual-shader-factory.h [moved from dali-toolkit/internal/visuals/image-visual-shader-factory.h with 69% similarity]
dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h [moved from dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h with 62% similarity]
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/mesh/mesh-visual.cpp
dali-toolkit/internal/visuals/npatch-shader-factory.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch-shader-factory.h [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch/npatch-data.cpp [moved from dali-toolkit/internal/visuals/npatch-data.cpp with 98% similarity]
dali-toolkit/internal/visuals/npatch/npatch-data.h [moved from dali-toolkit/internal/visuals/npatch-data.h with 99% similarity]
dali-toolkit/internal/visuals/npatch/npatch-loader.cpp [moved from dali-toolkit/internal/visuals/npatch-loader.cpp with 96% similarity]
dali-toolkit/internal/visuals/npatch/npatch-loader.h [moved from dali-toolkit/internal/visuals/npatch-loader.h with 97% similarity]
dali-toolkit/internal/visuals/npatch/npatch-visual.cpp
dali-toolkit/internal/visuals/npatch/npatch-visual.h
dali-toolkit/internal/visuals/primitive/primitive-visual.cpp
dali-toolkit/internal/visuals/svg/svg-loader-observer.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/svg/svg-loader-observer.h [new file with mode: 0644]
dali-toolkit/internal/visuals/svg/svg-loader.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/svg/svg-loader.h [new file with mode: 0644]
dali-toolkit/internal/visuals/svg/svg-task.cpp
dali-toolkit/internal/visuals/svg/svg-task.h
dali-toolkit/internal/visuals/svg/svg-visual.cpp
dali-toolkit/internal/visuals/svg/svg-visual.h
dali-toolkit/internal/visuals/text-visual-shader-factory.cpp [deleted file]
dali-toolkit/internal/visuals/text/text-visual-shader-factory.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/text/text-visual-shader-factory.h [moved from dali-toolkit/internal/visuals/text-visual-shader-factory.h with 76% similarity]
dali-toolkit/internal/visuals/text/text-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.h
dali-toolkit/internal/visuals/visual-base-data-impl.cpp
dali-toolkit/internal/visuals/visual-base-data-impl.h
dali-toolkit/internal/visuals/visual-base-impl.cpp
dali-toolkit/internal/visuals/visual-base-impl.h
dali-toolkit/internal/visuals/visual-factory-cache.cpp
dali-toolkit/internal/visuals/visual-factory-cache.h
dali-toolkit/internal/visuals/visual-factory-impl.cpp
dali-toolkit/internal/visuals/visual-factory-impl.h
dali-toolkit/internal/visuals/visual-shader-factory-interface.h [new file with mode: 0644]
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h
dali-toolkit/internal/visuals/wireframe/wireframe-visual.cpp
dali-toolkit/public-api/controls/control-impl.cpp
dali-toolkit/public-api/controls/control-impl.h
dali-toolkit/public-api/controls/control.cpp
dali-toolkit/public-api/controls/control.h
dali-toolkit/public-api/controls/render-effects/background-blur-effect.cpp [new file with mode: 0644]
dali-toolkit/public-api/controls/render-effects/background-blur-effect.h [new file with mode: 0644]
dali-toolkit/public-api/controls/render-effects/render-effect.cpp [new file with mode: 0644]
dali-toolkit/public-api/controls/render-effects/render-effect.h [new file with mode: 0644]
dali-toolkit/public-api/dali-toolkit-version.cpp
dali-toolkit/public-api/file.list
dali-toolkit/public-api/visuals/visual-properties.h
docs/content/main.md
docs/content/programming-guide/creating-custom-controls.md
docs/content/programming-guide/documentation-guide.md
docs/content/programming-guide/font-selection.md
docs/content/programming-guide/layer.md
docs/content/programming-guide/multi-touch-guide.md
docs/content/programming-guide/resource-image-scaling.md
docs/content/programming-guide/text-auto-scrolling.md
docs/content/programming-guide/visuals.md
packaging/dali-toolkit.spec

diff --git a/.ahub/sam/exclude.txt b/.ahub/sam/exclude.txt
new file mode 100755 (executable)
index 0000000..579989c
--- /dev/null
@@ -0,0 +1,4 @@
+automated-tests
+dali-toolkit/third-party
+dali-scene3d/third-party
+dali-physics/third-party
\ No newline at end of file
index be21183..42940bd 100644 (file)
@@ -6,6 +6,7 @@ INCLUDE(FindPkgConfig)
 SET(BIN_DIR "/opt/usr/bin")
 SET(SCRIPT_DIR ${CMAKE_SOURCE_DIR}/scripts)
 
+
 ADD_DEFINITIONS(-DDALI_STYLE_DIR="@dataReadOnlyDir@/toolkit/styles/")
 
 # Macro checks if the module is available and sets user variable
@@ -24,8 +25,22 @@ CHECK_MODULE_AND_SET(ELDBUS eldbus>=${ELDBUS_REQUIRED} ELDBUS_AVAILABLE)
 
 INCLUDE_DIRECTORIES(
   src/common
+  @PREFIX@/include/dali/integration-api/adaptor-framework
 )
 
+# Ignore some warning options
+INCLUDE(CheckCXXCompilerFlag)
+
+CHECK_CXX_COMPILER_FLAG(-Wno-unused-result HAVE_NO_UNUSED_RESULT)
+IF (HAVE_NO_UNUSED_RESULT)
+  ADD_COMPILE_OPTIONS( -Wno-unused-result )
+ENDIF()
+
+CHECK_CXX_COMPILER_FLAG(-Wno-ignored-attributes HAVE_NO_IGNORED_ATTRIBUTES)
+IF (HAVE_NO_IGNORED_ATTRIBUTES)
+  ADD_COMPILE_OPTIONS( -Wno-ignored-attributes )
+ENDIF()
+
 ADD_SUBDIRECTORY(src)
 
 #Internationalization
diff --git a/automated-tests/resources/headerOK-invalidArray.ktx b/automated-tests/resources/headerOK-invalidArray.ktx
new file mode 100644 (file)
index 0000000..d65cb30
Binary files /dev/null and b/automated-tests/resources/headerOK-invalidArray.ktx differ
diff --git a/automated-tests/resources/headerOnly.ktx b/automated-tests/resources/headerOnly.ktx
new file mode 100644 (file)
index 0000000..6c1e998
Binary files /dev/null and b/automated-tests/resources/headerOnly.ktx differ
index e72db08..b1fd2e9 100644 (file)
@@ -55,6 +55,7 @@ SET(TEST_HARNESS_SOURCES
   ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
   ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
   ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-render-surface.cpp
   ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
 )
 
index 01ecfc0..3b7a469 100644 (file)
@@ -54,6 +54,7 @@ SET(TEST_HARNESS_SOURCES
   ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
   ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
   ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-render-surface.cpp
   ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
 )
 
index b050314..1c4f40a 100755 (executable)
@@ -18,6 +18,7 @@ SET(TC_SOURCES
   utc-Dali-MaterialImpl.cpp
   utc-Dali-ModelCacheManager.cpp
   utc-Dali-ModelPrimitiveImpl.cpp
+  utc-Dali-SceneViewImpl.cpp
 )
 
 # List of test harness files (Won't get parsed for test cases)
@@ -64,6 +65,7 @@ SET(TEST_HARNESS_SOURCES
   ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
   ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
   ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-render-surface.cpp
   ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
 )
 
diff --git a/automated-tests/src/dali-scene3d-internal/utc-Dali-SceneViewImpl.cpp b/automated-tests/src/dali-scene3d-internal/utc-Dali-SceneViewImpl.cpp
new file mode 100644 (file)
index 0000000..0689257
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+#include <toolkit-timer.h>
+
+#include <dali-toolkit/dali-toolkit.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-scene3d/public-api/controls/model/model.h>
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
+
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+namespace
+{
+
+// Functor to test whether a Finish signal is emitted
+struct TransitionFinishCheck
+{
+  TransitionFinishCheck(bool& signalReceived)
+  : mSignalReceived(signalReceived)
+  {
+  }
+
+  void operator()(Scene3D::SceneView& sceneView)
+  {
+    mSignalReceived = true;
+  }
+
+  void Reset()
+  {
+    mSignalReceived = false;
+  }
+
+  void CheckSignalReceived()
+  {
+    if(!mSignalReceived)
+    {
+      tet_printf("Expected Finish signal was not received\n");
+      tet_result(TET_FAIL);
+    }
+    else
+    {
+      tet_result(TET_PASS);
+    }
+  }
+
+  void CheckSignalNotReceived()
+  {
+    if(mSignalReceived)
+    {
+      tet_printf("Unexpected Finish signal was received\n");
+      tet_result(TET_FAIL);
+    }
+    else
+    {
+      tet_result(TET_PASS);
+    }
+  }
+
+  bool& mSignalReceived; // owned by individual tests
+};
+
+}
+
+int UtcDaliSceneViewImplCameraTransitionFail(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  CameraActor camera1 = CameraActor::New3DCamera();
+  camera1.SetProperty(Dali::Actor::Property::NAME, "camera1");
+  view.AddCamera(camera1);
+  DALI_TEST_CHECK(!camera1.GetParent());
+  view.SelectCamera("camera1");
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+
+  camera1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  view.Add(camera1);
+
+  bool                  signalReceived(false);
+  TransitionFinishCheck finishCheck(signalReceived);
+  view.CameraTransitionFinishedSignal().Connect(&application, finishCheck);
+
+  view.StartCameraTransition("camera1", 1.0f);
+
+  application.SendNotification();
+  application.Render(500);
+  application.SendNotification();
+
+  // We didn't expect the animation to finish yet
+  finishCheck.CheckSignalNotReceived();
+
+  application.SendNotification();
+  application.Render(600);
+  application.SendNotification();
+
+  finishCheck.CheckSignalNotReceived();
+
+  CameraActor finalCamera = view.GetSelectedCamera();
+  DALI_TEST_EQUALS(finalCamera, camera1, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewImplCameraChangeDuringTransition(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  CameraActor camera1 = CameraActor::New3DCamera();
+  camera1.SetProperty(Dali::Actor::Property::NAME, "camera1");
+  view.AddCamera(camera1);
+  DALI_TEST_CHECK(!camera1.GetParent());
+  view.SelectCamera("camera1");
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+
+  camera1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  view.Add(camera1);
+
+  CameraActor camera2 = CameraActor::New3DCamera();
+  camera2.SetProperty(Dali::Actor::Property::NAME, "camera2");
+  view.AddCamera(camera2);
+  camera2.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * 100.0f);
+
+  application.SendNotification();
+  application.Render();
+
+  bool                  signalReceived(false);
+  TransitionFinishCheck finishCheck(signalReceived);
+  view.CameraTransitionFinishedSignal().Connect(&application, finishCheck);
+
+  view.StartCameraTransition("camera2", 1.0f);
+
+  CameraActor camera3 = CameraActor::New3DCamera();
+  camera3.SetProperty(Dali::Actor::Property::NAME, "camera3");
+  view.AddCamera(camera3);
+  view.SelectCamera("camera3");
+
+  DALI_TEST_NOT_EQUALS(camera3, view.GetSelectedCamera(), 0.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(camera2, view.GetSelectedCamera(), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(500);
+  application.SendNotification();
+
+  // We didn't expect the animation to finish yet
+  finishCheck.CheckSignalNotReceived();
+
+  Dali::Scene3D::Internal::SceneView& sceneViewImpl = Dali::Scene3D::GetImpl(view);
+  auto renderTask = sceneViewImpl.GetRenderTask();
+  DALI_TEST_CHECK(renderTask);
+
+  CameraActor currentCamera = renderTask.GetCameraActor();
+  DALI_TEST_CHECK(currentCamera);
+  DALI_TEST_NOT_EQUALS(currentCamera, camera1, 0.0f, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(currentCamera, camera2, 0.0f, TEST_LOCATION);
+
+  view.SelectCamera(view.GetCameraCount() - 1);
+  DALI_TEST_NOT_EQUALS(camera3, view.GetSelectedCamera(), 0.0f, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(currentCamera, view.GetSelectedCamera(), 0.0f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(600);
+  application.SendNotification();
+
+  finishCheck.CheckSignalReceived();
+
+  CameraActor finalCamera = view.GetSelectedCamera();
+  DALI_TEST_EQUALS(finalCamera, camera2, TEST_LOCATION);
+
+  view.SelectCamera("camera3");
+  DALI_TEST_EQUALS(camera3, view.GetSelectedCamera(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewImplStartCameraTransitionDuringTransition(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  CameraActor camera1 = CameraActor::New3DCamera();
+  camera1.SetProperty(Dali::Actor::Property::NAME, "camera1");
+  view.AddCamera(camera1);
+  DALI_TEST_CHECK(!camera1.GetParent());
+  view.SelectCamera("camera1");
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+
+  camera1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  view.Add(camera1);
+
+  CameraActor camera2 = CameraActor::New3DCamera();
+  camera2.SetProperty(Dali::Actor::Property::NAME, "camera2");
+  view.AddCamera(camera2);
+  camera2.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * 100.0f);
+
+  application.SendNotification();
+  application.Render();
+
+  bool                  signalReceived(false);
+  TransitionFinishCheck finishCheck(signalReceived);
+  view.CameraTransitionFinishedSignal().Connect(&application, finishCheck);
+
+  view.StartCameraTransition("camera2", 1.0f);
+
+  application.SendNotification();
+  application.Render(500);
+  application.SendNotification();
+
+  // We didn't expect the animation to finish yet
+  finishCheck.CheckSignalNotReceived();
+
+  CameraActor camera3 = CameraActor::New3DCamera();
+  camera3.SetProperty(Dali::Actor::Property::NAME, "camera3");
+  view.AddCamera(camera3);
+
+  view.StartCameraTransition("camera3", 1.0f);
+
+  application.SendNotification();
+  application.Render(600);
+  application.SendNotification();
+
+  finishCheck.CheckSignalReceived();
+
+  CameraActor finalCamera = view.GetSelectedCamera();
+  DALI_TEST_EQUALS(finalCamera, camera2, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewImplCameraTransition1(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  CameraActor camera1 = CameraActor::New3DCamera();
+  camera1.SetProperty(Dali::Actor::Property::NAME, "camera1");
+  view.AddCamera(camera1);
+  DALI_TEST_CHECK(!camera1.GetParent());
+  view.SelectCamera("camera1");
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+
+  Scene3D::Model model1 = Scene3D::Model::New();
+  model1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  view.Add(model1);
+
+  camera1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  camera1.SetNearClippingPlane(10.0f);
+  camera1.SetFarClippingPlane(100.0f);
+  model1.Add(camera1);
+
+  CameraActor camera2 = CameraActor::New3DCamera();
+  camera2.SetProperty(Dali::Actor::Property::NAME, "camera2");
+  view.AddCamera(camera2);
+  camera2.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * 100.0f);
+  camera2.SetNearClippingPlane(5.0f);
+  camera2.SetFarClippingPlane(50.0f);
+
+  Scene3D::Model model2 = Scene3D::Model::New();
+  model2.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * 100.0f);
+  view.Add(model2);
+  model2.Add(camera2);
+
+  application.SendNotification();
+  application.Render();
+
+  camera1.SetProperty(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION, Dali::DevelCameraActor::ProjectionDirection::VERTICAL);
+  camera2.SetProperty(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION, Dali::DevelCameraActor::ProjectionDirection::HORIZONTAL);
+  camera1.SetFieldOfView(1.0f); // Vertical : 1.0f, Horizontal : 0.533293254f
+  camera2.SetFieldOfView(1.0f); // Vertical : 1.65924551f, Horizontal : 1.0f
+  camera1.SetAspectRatio(0.5f);
+  camera2.SetAspectRatio(0.5f);
+  tet_printf("camera1 fov : %f\n", camera1.GetFieldOfView());
+  tet_printf("camera2 fov : %f\n", camera2.GetFieldOfView());
+  tet_printf("camera1 aspect : %f\n", camera1.GetAspectRatio());
+  tet_printf("camera2 aspect : %f\n", camera2.GetAspectRatio());
+  tet_printf("camera1 direction : %d\n", camera1.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION));
+  tet_printf("camera2 direction : %d\n", camera2.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION));
+
+  bool                  signalReceived(false);
+  TransitionFinishCheck finishCheck(signalReceived);
+  view.CameraTransitionFinishedSignal().Connect(&application, finishCheck);
+
+  view.StartCameraTransition("camera2", 1.0f);
+
+  application.SendNotification();
+  application.Render(500);
+  application.SendNotification();
+
+  // We didn't expect the animation to finish yet
+  finishCheck.CheckSignalNotReceived();
+
+
+  Dali::Scene3D::Internal::SceneView& sceneViewImpl = Dali::Scene3D::GetImpl(view);
+  auto renderTask = sceneViewImpl.GetRenderTask();
+  DALI_TEST_CHECK(renderTask);
+
+  CameraActor currentCamera = renderTask.GetCameraActor();
+  DALI_TEST_CHECK(currentCamera);
+  DALI_TEST_NOT_EQUALS(currentCamera, camera1, 0.0f, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(currentCamera, camera2, 0.0f, TEST_LOCATION);
+
+  Vector3 currentPosition = currentCamera.GetCurrentProperty<Vector3>(Dali::Actor::Property::POSITION);
+  DALI_TEST_EQUALS(currentPosition, Vector3::ONE * 50.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(currentCamera.GetNearClippingPlane(), 5.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(currentCamera.GetFarClippingPlane(), 100.0f, TEST_LOCATION);
+  float currentFov =  (0.533293254f + 1.0f) / 2.0f;
+  DALI_TEST_EQUALS(currentCamera.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION), Dali::DevelCameraActor::ProjectionDirection::HORIZONTAL, TEST_LOCATION);
+  DALI_TEST_EQUALS(currentCamera.GetCurrentProperty<float>(Dali::CameraActor::Property::FIELD_OF_VIEW), currentFov, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(600);
+  application.SendNotification();
+
+  finishCheck.CheckSignalReceived();
+
+  CameraActor finalCamera = view.GetSelectedCamera();
+  DALI_TEST_EQUALS(finalCamera, camera2, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewImplCameraTransition2(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  CameraActor camera1 = CameraActor::New3DCamera();
+  camera1.SetProperty(Dali::Actor::Property::NAME, "camera1");
+  view.AddCamera(camera1);
+  DALI_TEST_CHECK(!camera1.GetParent());
+  view.SelectCamera("camera1");
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+
+  Scene3D::Model model1 = Scene3D::Model::New();
+  model1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  view.Add(model1);
+
+  camera1.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * -50.0f);
+  camera1.SetNearClippingPlane(10.0f);
+  camera1.SetFarClippingPlane(100.0f);
+  model1.Add(camera1);
+
+  CameraActor camera2 = CameraActor::New3DCamera();
+  camera2.SetProperty(Dali::Actor::Property::NAME, "camera2");
+  view.AddCamera(camera2);
+  camera2.SetProperty(Dali::Actor::Property::POSITION, Vector3::ONE * 200.0f);
+  camera2.SetNearClippingPlane(5.0f);
+  camera2.SetFarClippingPlane(50.0f);
+  // Camera2 is not added on SceneView, it will added on root layer automatically.
+
+  application.SendNotification();
+  application.Render();
+
+  bool                  signalReceived(false);
+  TransitionFinishCheck finishCheck(signalReceived);
+  view.CameraTransitionFinishedSignal().Connect(&application, finishCheck);
+
+  camera1.SetProjectionMode(Dali::Camera::ProjectionMode::ORTHOGRAPHIC_PROJECTION);
+  camera2.SetProjectionMode(Dali::Camera::ProjectionMode::ORTHOGRAPHIC_PROJECTION);
+  camera1.SetProperty(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION, Dali::DevelCameraActor::ProjectionDirection::VERTICAL);
+  camera2.SetProperty(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION, Dali::DevelCameraActor::ProjectionDirection::HORIZONTAL);
+
+  camera1.SetProperty(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE, 10.0f);  // Vertical : 10.0f, Horizontal : 5.0f
+  camera2.SetProperty(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE, 10.0f);  // Vertical : 20.0f, Horizontal : 10.0f
+  camera1.SetAspectRatio(0.5f);
+  camera2.SetAspectRatio(0.5f);
+
+  tet_printf("camera1 fov : %f\n", camera1.GetProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE));
+  tet_printf("camera2 fov : %f\n", camera2.GetProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE));
+  tet_printf("camera1 aspect : %f\n", camera1.GetAspectRatio());
+  tet_printf("camera2 aspect : %f\n", camera2.GetAspectRatio());
+  tet_printf("camera1 direction : %f\n", camera1.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION));
+  tet_printf("camera2 direction : %f\n", camera2.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION));
+
+  int camera2Index = view.GetCameraCount() - 1;
+  view.StartCameraTransition(camera2Index, 1.0f);
+
+  application.SendNotification();
+  application.Render(500);
+  application.SendNotification();
+
+  // We didn't expect the animation to finish yet
+  finishCheck.CheckSignalNotReceived();
+
+  Dali::Scene3D::Internal::SceneView& sceneViewImpl = Dali::Scene3D::GetImpl(view);
+  auto renderTask = sceneViewImpl.GetRenderTask();
+  DALI_TEST_CHECK(renderTask);
+
+  CameraActor currentCamera = renderTask.GetCameraActor();
+  DALI_TEST_CHECK(currentCamera);
+  DALI_TEST_NOT_EQUALS(currentCamera, camera1, 0.0f, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(currentCamera, camera2, 0.0f, TEST_LOCATION);
+
+  Vector3 currentPosition = currentCamera.GetCurrentProperty<Vector3>(Dali::Actor::Property::POSITION);
+  DALI_TEST_EQUALS(currentPosition, Vector3::ONE * 50.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(currentCamera.GetNearClippingPlane(), 5.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(currentCamera.GetFarClippingPlane(), 100.0f, TEST_LOCATION);
+  float currentOrthographicSize = (5.0f + 10.0f) / 2.0f;
+  DALI_TEST_EQUALS(currentCamera.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION), Dali::DevelCameraActor::ProjectionDirection::HORIZONTAL, TEST_LOCATION);
+  DALI_TEST_EQUALS(currentCamera.GetCurrentProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE), currentOrthographicSize, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(600);
+  application.SendNotification();
+
+  finishCheck.CheckSignalReceived();
+
+  CameraActor finalCamera = view.GetSelectedCamera();
+  DALI_TEST_EQUALS(finalCamera, camera2, TEST_LOCATION);
+
+  END_TEST;
+}
index 1269cb2..95cca45 100755 (executable)
@@ -31,6 +31,7 @@ SET(TC_SOURCES
   utc-Dali-MeshDefinition.cpp
   utc-Dali-NavigationMesh.cpp
   utc-Dali-NodeDefinition.cpp
+  utc-Dali-Panel.cpp
   utc-Dali-PathFinding.cpp
   utc-Dali-RendererState.cpp
   utc-Dali-ResourceBundle.cpp
@@ -88,6 +89,7 @@ SET(TEST_HARNESS_SOURCES
   ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
   ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
   ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-render-surface.cpp
   ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
 )
 
index 3d12208..9038bbb 100644 (file)
@@ -15,6 +15,7 @@
  *
  */
 
+#include <dali/devel-api/animation/key-frames-devel.h>
 #include <dali-scene3d/public-api/loader/animation-definition.h>
 #include <dali-scene3d/public-api/loader/facial-animation-loader.h>
 #include <dali-test-suite-utils.h>
@@ -171,4 +172,60 @@ int UtcDaliLoadFacialAnimationFailed03(void)
     DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   }
   END_TEST;
+}
+
+int UtcDaliLoadFacialAnimationLoadFirstFrameData(void)
+{
+  TestApplication application;
+
+  tet_infoline("parse json which don't define times zero");
+  std::string rawData = R"(
+  {
+    "name": "Facial_Blendshape_Animation",
+    "version": "1.2.3",
+    "blendShapes": [
+      {
+        "name": "GEO_1",
+        "fullName": "Facial_Blendshape_Animation:GEO_1",
+        "blendShapeVersion": "3.0",
+        "morphtarget": 1,
+        "morphname": [
+          "EyeBlink_Left"
+        ],
+        "key": [
+          [
+            0.5
+          ],
+          [
+            1.0
+          ]
+        ]
+      }
+    ],
+    "shapesAmount": 1,
+    "time": [
+      50,
+      100
+    ],
+    "frames": 2
+  }
+  )";
+  AnimationDefinition animDef = LoadFacialAnimationFromBuffer(reinterpret_cast<uint8_t*>(rawData.data()), static_cast<int>(rawData.length()));
+
+  DALI_TEST_EQUALS(1u, animDef.GetPropertyCount(), TEST_LOCATION);
+
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "GEO_1");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "uBlendShapeWeight[0]");
+
+  // Let we ensure 0'th keyframe progress is 0.0f.
+  auto keyFrames = animDef.GetPropertyAt(0).mKeyFrames;
+  DALI_TEST_EQUAL(keyFrames.GetType(), Property::Type::FLOAT);
+  DALI_TEST_EQUALS(Dali::DevelKeyFrames::GetKeyFrameCount(keyFrames), 3, TEST_LOCATION);
+
+  float progress = -1.0f;
+  Property::Value value = Property::Value(10.0f);
+  Dali::DevelKeyFrames::GetKeyFrame(keyFrames, 0u, progress, value);
+  DALI_TEST_EQUALS(progress, 0.0f, TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index 18b9d08..4accb91 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,6 +55,20 @@ int UtcDaliKtxLoaderFailTruncated(void)
   END_TEST;
 }
 
+int UtcDaliKtxLoaderFailValidHeaderInvalidArray(void)
+{
+  EnvironmentMapData environmentMapData;
+  DALI_TEST_CHECK(!LoadKtxData(TEST_RESOURCE_DIR "/headerOK-invalidArray.ktx", environmentMapData));
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderFailValidHeaderOnly(void)
+{
+  EnvironmentMapData environmentMapData;
+  DALI_TEST_CHECK(!LoadKtxData(TEST_RESOURCE_DIR "/headerOnly.ktx", environmentMapData));
+  END_TEST;
+}
+
 int UtcDaliKtxLoaderSuccess(void)
 {
   EnvironmentMapData environmentMapData;
index ba67d42..2754ad3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -173,3 +173,13 @@ int UtcDaliMeshDefinitionShortSkinWeight(void)
   }
   END_TEST;
 }
+
+int UtcDaliMeshDefinitionInvalidUrl(void)
+{
+  MeshDefinition meshDefinition;
+  meshDefinition.mUri = "invalid-uri/";
+  BufferDefinition::Vector buffers;
+  MeshDefinition::RawData rawData = meshDefinition.LoadRaw("invalidModelPath", buffers);
+  DALI_TEST_EQUALS(rawData.mIndices.size(), 0u, TEST_LOCATION);
+  END_TEST;
+}
\ No newline at end of file
index b2ccb7f..a6d4659 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -851,5 +851,25 @@ int UtcDaliColliderMeshModelNodeRemoveModelNode(void)
   model.AddModelNode(node);
   DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
 
+  // Scene off model, and add,remove model node.
+  model.Unparent();
+  DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
+
+  model.RemoveModelNode(node);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
+
+  model.AddModelNode(node);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
+
+  // Reset collider mesh
+  node.SetColliderMesh(nullptr);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+
+  model.RemoveModelNode(node);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+
+  model.AddModelNode(node);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+
   END_TEST;
 }
\ No newline at end of file
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-Panel.cpp b/automated-tests/src/dali-scene3d/utc-Dali-Panel.cpp
new file mode 100644 (file)
index 0000000..197c1e8
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali/devel-api/common/map-wrapper.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
+#include <dali/integration-api/events/touch-event-integ.h>
+#include <toolkit-environment-variable.h>
+#include <toolkit-event-thread-callback.h>
+
+#include <dali-scene3d/public-api/controls/panel/panel.h>
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void panel_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void panel_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+// Negative test case for a method
+int UtcDaliPanelUninitialized(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelUninitialized");
+
+  Scene3D::Panel panel;
+
+  try
+  {
+    // New() must be called to create a Panel or it wont be valid.
+    Actor a = Actor::New();
+    panel.Add(a);
+    DALI_TEST_CHECK(false);
+  }
+  catch(Dali::DaliException& e)
+  {
+    // Tests that a negative test of an assertion succeeds
+    DALI_TEST_PRINT_ASSERT(e);
+    DALI_TEST_CHECK(!panel);
+  }
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliPanelNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelNew");
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  DALI_TEST_CHECK(panel);
+  END_TEST;
+}
+
+int UtcDaliPanelCopy(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelCopy");
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  DALI_TEST_CHECK(panel);
+
+  Scene3D::Panel panelCopy = Scene3D::Panel(panel);
+  DALI_TEST_CHECK(panelCopy);
+
+  END_TEST;
+}
+
+int UtcDaliPanelDestruct(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelDestruct");
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  DALI_TEST_CHECK(panel);
+  panel.Reset();
+  DALI_TEST_CHECK(!panel);
+  END_TEST;
+}
+
+int UtcDaliPanelOnSceneOffScene(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelOnSceneOffScene");
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  application.GetWindow().Add(sceneView);
+
+  int previousTaskCount = application.GetWindow().GetRenderTaskList().GetTaskCount();
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  DALI_TEST_CHECK(panel);
+  sceneView.Add(panel);
+
+  int taskCount = application.GetWindow().GetRenderTaskList().GetTaskCount();
+  DALI_TEST_EQUALS(previousTaskCount + 1, taskCount, TEST_LOCATION);
+
+  panel.Unparent();
+  panel.Reset();
+
+  DALI_TEST_EQUALS(previousTaskCount, application.GetWindow().GetRenderTaskList().GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPanelSetPanelResolution01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelSetPanelResolution01");
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  DALI_TEST_CHECK(panel);
+
+  DALI_TEST_EQUALS(Vector2::ZERO, panel.GetPanelResolution(), TEST_LOCATION);
+
+  panel.SetPanelResolution(Vector2(300, 500));
+
+  DALI_TEST_EQUALS(Vector2(300, 500), panel.GetPanelResolution(), TEST_LOCATION);
+
+  panel.SetPanelResolution(Vector2(500, 300));
+
+  DALI_TEST_EQUALS(Vector2(500, 300), panel.GetPanelResolution(), TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliPanelSetPanelResolution02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelSetPanelResolution02");
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  application.GetWindow().Add(sceneView);
+
+  int previousTaskCount = application.GetWindow().GetRenderTaskList().GetTaskCount();
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  DALI_TEST_CHECK(panel);
+  sceneView.Add(panel);
+
+  int taskCount = application.GetWindow().GetRenderTaskList().GetTaskCount();
+  DALI_TEST_EQUALS(previousTaskCount + 1, taskCount, TEST_LOCATION);
+
+  panel.SetPanelResolution(Vector2(300, 500));
+  DALI_TEST_EQUALS(Vector2(300, 500), panel.GetPanelResolution(), TEST_LOCATION);
+
+  RenderTask task = application.GetWindow().GetRenderTaskList().GetTask(taskCount - 1); // Newly added task.
+  DALI_TEST_CHECK(task);
+
+  Dali::FrameBuffer framebuffer = task.GetFrameBuffer();
+  DALI_TEST_CHECK(framebuffer);
+
+  Dali::Texture texture = framebuffer.GetColorTexture();
+  DALI_TEST_CHECK(texture);
+
+  DALI_TEST_EQUALS(300, texture.GetWidth(), TEST_LOCATION);
+  DALI_TEST_EQUALS(500, texture.GetHeight(), TEST_LOCATION);
+
+  panel.SetPanelResolution(Vector2(500, 300));
+  DALI_TEST_EQUALS(Vector2(500, 300), panel.GetPanelResolution(), TEST_LOCATION);
+
+  framebuffer = task.GetFrameBuffer();
+  DALI_TEST_CHECK(framebuffer);
+
+  texture = framebuffer.GetColorTexture();
+  DALI_TEST_CHECK(texture);
+
+  DALI_TEST_EQUALS(500, texture.GetWidth(), TEST_LOCATION);
+  DALI_TEST_EQUALS(300, texture.GetHeight(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+namespace
+{
+
+/**
+ * For the diffuse and specular cube map texture.
+ * These textures are based off version of Wave engine sample
+ * Take from https://github.com/WaveEngine/Samples
+ *
+ * Copyright (c) 2024 Wave Coorporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+const char* TEST_DIFFUSE_TEXTURE  = TEST_RESOURCE_DIR "/forest_irradiance.ktx";
+const char* TEST_SPECULAR_TEXTURE = TEST_RESOURCE_DIR "/forest_radiance.ktx";
+
+static bool gResourceReadyCalled = false;
+void        OnResourceReady(Control control)
+{
+  gResourceReadyCalled = true;
+}
+
+Dali::Scene3D::ModelNode GetContentPlaneNode(Dali::Scene3D::Panel panel)
+{
+  Dali::Scene3D::ModelNode contentPlaneNode;
+  Dali::Actor panelNode;
+  for(uint32_t i = 0; i < panel.GetChildCount(); ++i)
+  {
+    Dali::Actor actor = panel.GetChildAt(i);
+    Scene3D::ModelNode modelNode = Scene3D::ModelNode::DownCast(actor);
+    if(modelNode)
+    {
+      panelNode = modelNode;
+      break;
+    }
+  }
+
+  if(panelNode)
+  {
+    DALI_TEST_EQUALS(3, panelNode.GetChildCount(), TEST_LOCATION);
+
+    Dali::Actor childPanel = panelNode.GetChildAt(0u);
+    if(childPanel)
+    {
+      contentPlaneNode = Scene3D::ModelNode::DownCast(childPanel);
+    }
+  }
+
+  return contentPlaneNode;
+}
+
+Dali::Scene3D::ModelNode GetBackPlaneNode(Dali::Scene3D::Panel panel)
+{
+  Dali::Scene3D::ModelNode backPlaneNode;
+  Dali::Actor panelNode;
+  for(uint32_t i = 0; i < panel.GetChildCount(); ++i)
+  {
+    Dali::Actor actor = panel.GetChildAt(i);
+    Scene3D::ModelNode modelNode = Scene3D::ModelNode::DownCast(actor);
+    if(modelNode)
+    {
+      panelNode = modelNode;
+      break;
+    }
+  }
+
+  if(panelNode)
+  {
+    DALI_TEST_EQUALS(3, panelNode.GetChildCount(), TEST_LOCATION);
+
+    Dali::Actor childPanel = panelNode.GetChildAt(1u);
+    if(childPanel)
+    {
+      backPlaneNode = Scene3D::ModelNode::DownCast(childPanel);
+    }
+  }
+
+  return backPlaneNode;
+}
+
+Dali::Scene3D::ModelNode GetDoubleSidedPlaneNode(Dali::Scene3D::Panel panel)
+{
+  Dali::Scene3D::ModelNode backPlaneNode;
+  Dali::Actor panelNode;
+  for(uint32_t i = 0; i < panel.GetChildCount(); ++i)
+  {
+    Dali::Actor actor = panel.GetChildAt(i);
+    Scene3D::ModelNode modelNode = Scene3D::ModelNode::DownCast(actor);
+    if(modelNode)
+    {
+      panelNode = modelNode;
+      break;
+    }
+  }
+
+  if(panelNode)
+  {
+    DALI_TEST_EQUALS(3, panelNode.GetChildCount(), TEST_LOCATION);
+
+    Dali::Actor childPanel = panelNode.GetChildAt(2u);
+    if(childPanel)
+    {
+      backPlaneNode = Scene3D::ModelNode::DownCast(childPanel);
+    }
+  }
+
+  return backPlaneNode;
+}
+
+Dali::Texture GetDiffuseTexture(Dali::Scene3D::Panel panel)
+{
+  Dali::Texture texture;
+
+  Dali::Actor childPanel = GetContentPlaneNode(panel);
+  if(childPanel)
+  {
+    Renderer renderer = childPanel.GetRendererAt(0u);
+    if(renderer)
+    {
+      TextureSet textureSet = renderer.GetTextures();
+      uint32_t   textureCnt = textureSet.GetTextureCount();
+      DALI_TEST_CHECK(textureCnt >= 2);
+      texture = textureSet.GetTexture(textureCnt - 2u);
+    }
+  }
+
+  DALI_TEST_CHECK(texture);
+
+  return texture;
+}
+
+Dali::Texture GetSpecularTexture(Dali::Scene3D::Panel panel)
+{
+  Dali::Texture texture;
+
+  Dali::Actor childPanel = GetContentPlaneNode(panel);
+  if(childPanel)
+  {
+    Renderer renderer = childPanel.GetRendererAt(0u);
+    if(renderer)
+    {
+      TextureSet textureSet = renderer.GetTextures();
+      uint32_t   textureCnt = textureSet.GetTextureCount();
+      DALI_TEST_CHECK(textureCnt >= 2);
+      texture = textureSet.GetTexture(textureCnt - 1u);
+    }
+  }
+
+  DALI_TEST_CHECK(texture);
+
+  return texture;
+}
+}
+
+int UtcDaliPanelIBLWithSceneView(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelIBLWithSceneView");
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.ResourceReadySignal().Connect(OnResourceReady);
+  application.GetWindow().Add(sceneView);
+
+  Scene3D::Panel panel1 = Scene3D::Panel::New();
+  panel1.SetPanelResolution(Vector2(300, 500));
+  DALI_TEST_CHECK(panel1);
+  sceneView.Add(panel1);
+
+  Scene3D::Panel panel2 = Scene3D::Panel::New();
+  panel2.SetPanelResolution(Vector2(300, 500));
+  DALI_TEST_CHECK(panel2);
+  sceneView.Add(panel2);
+
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+  sceneView.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(GetDiffuseTexture(panel1), GetDiffuseTexture(panel2), TEST_LOCATION);
+  DALI_TEST_EQUALS(GetSpecularTexture(panel1), GetSpecularTexture(panel2), TEST_LOCATION);
+
+  // For coverage
+  sceneView.SetImageBasedLightScaleFactor(0.4f);
+
+  // Reset
+  sceneView.SetImageBasedLightSource("", "");
+
+  END_TEST;
+}
+
+int UtcDaliPanelSetGetProperty(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelSetGetProperty");
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  application.GetWindow().Add(sceneView);
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  panel.SetPanelResolution(Vector2(300, 500));
+  DALI_TEST_CHECK(panel);
+  sceneView.Add(panel);
+
+  Dali::Scene3D::ModelNode contentPlaneNode = GetContentPlaneNode(panel);
+  DALI_TEST_CHECK(contentPlaneNode);
+  DALI_TEST_CHECK(contentPlaneNode.GetModelPrimitiveCount() > 0);
+  DALI_TEST_CHECK(contentPlaneNode.GetModelPrimitive(0u));
+
+  Dali::Scene3D::Material contentPlaneMaterial = contentPlaneNode.GetModelPrimitive(0u).GetMaterial();
+  DALI_TEST_CHECK(contentPlaneMaterial);
+
+  Dali::Scene3D::ModelNode backPlaneNode = GetBackPlaneNode(panel);
+  DALI_TEST_CHECK(backPlaneNode);
+  DALI_TEST_CHECK(backPlaneNode.GetModelPrimitiveCount() > 0);
+  DALI_TEST_CHECK(backPlaneNode.GetModelPrimitive(0u));
+
+  Dali::Scene3D::Material backPlaneMaterial = backPlaneNode.GetModelPrimitive(0u).GetMaterial();
+  DALI_TEST_CHECK(backPlaneMaterial);
+
+  Dali::Scene3D::ModelNode doubleSidedPlaneNode = GetDoubleSidedPlaneNode(panel);
+  DALI_TEST_CHECK(doubleSidedPlaneNode);
+  DALI_TEST_CHECK(doubleSidedPlaneNode.GetModelPrimitiveCount() > 0);
+  DALI_TEST_CHECK(doubleSidedPlaneNode.GetModelPrimitive(0u));
+
+  Vector4 backPlaneMaterialBaseColorFactor = backPlaneMaterial.GetProperty<Vector4>(Dali::Scene3D::Material::Property::BASE_COLOR_FACTOR);
+  DALI_TEST_EQUALS(Vector3(backPlaneMaterialBaseColorFactor), panel.GetProperty<Vector3>(Dali::Scene3D::Panel::Property::BACK_FACE_PLANE_COLOR), TEST_LOCATION);
+
+  panel.SetProperty(Dali::Scene3D::Panel::Property::BACK_FACE_PLANE_COLOR, Vector3(1.0f, 0.0f, 0.0f));
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), panel.GetProperty<Vector3>(Dali::Scene3D::Panel::Property::BACK_FACE_PLANE_COLOR), TEST_LOCATION);
+
+  backPlaneMaterialBaseColorFactor = backPlaneMaterial.GetProperty<Vector4>(Dali::Scene3D::Material::Property::BASE_COLOR_FACTOR);
+  DALI_TEST_EQUALS(Vector3(backPlaneMaterialBaseColorFactor), panel.GetProperty<Vector3>(Dali::Scene3D::Panel::Property::BACK_FACE_PLANE_COLOR), TEST_LOCATION);
+
+  bool                                   isTransparent;
+  Dali::Scene3D::Material::AlphaModeType contentPlaneAlphaMode;
+  bool                                   isUsingBackFacePlane;
+  bool                                   isBackPlaneVisible;
+  bool                                   isDoubleSided;
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+
+  // Case 1. transparent false, double sided false, useBackFacePlane false;
+  // Front Material Alpha Mode : Opaque, Back Plane Visible : false, Front Material Double Sided : false
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, false);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+
+
+  // Case 2. transparent false, double sided false, useBackFacePlane true;
+  // Front Material Alpha Mode : Opaque, Back Plane Visible : true, Front Material Double Sided : false
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, true);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+
+
+  // Case 2. transparent false, double sided true, useBackFacePlane false;
+  // Front Material Alpha Mode : Opaque, Back Plane Visible : false, Front Material Double Sided : true
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, false);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), true, TEST_LOCATION);
+
+
+  // Case 2. transparent false, double sided true, useBackFacePlane true;
+  // Front Material Alpha Mode : Opaque, Back Plane Visible : true, Front Material Double Sided : false
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, true);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+
+
+  // Case 1. transparent true, double sided false, useBackFacePlane false;
+  // Front Material Alpha Mode : Blend, Back Plane Visible : false, Front Material Double Sided : false
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, false);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::BLEND, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+
+
+  // Case 2. transparent true, double sided false, useBackFacePlane true;
+  // Front Material Alpha Mode : Blend, Back Plane Visible : false, Front Material Double Sided : false
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, false);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, true);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::BLEND, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+
+
+  // Case 2. transparent true, double sided true, useBackFacePlane false;
+  // Front Material Alpha Mode : Blend, Back Plane Visible : false, Front Material Double Sided : true
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, false);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::BLEND, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), true, TEST_LOCATION);
+
+
+  // Case 2. transparent true, double sided true, useBackFacePlane true;
+  // Front Material Alpha Mode : Blend, Back Plane Visible : false, Front Material Double Sided : true
+  panel.SetProperty(Dali::Scene3D::Panel::Property::TRANSPARENT, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::DOUBLE_SIDED, true);
+  panel.SetProperty(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE, true);
+
+  isTransparent       = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::TRANSPARENT);
+  contentPlaneAlphaMode = contentPlaneMaterial.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Dali::Scene3D::Material::Property::ALPHA_MODE);
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, isTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE, TEST_LOCATION);
+  isUsingBackFacePlane = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::USE_BACK_FACE_PLANE);
+  isBackPlaneVisible   = !isTransparent && isUsingBackFacePlane;
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isBackPlaneVisible, TEST_LOCATION);
+  isDoubleSided = panel.GetProperty<bool>(Dali::Scene3D::Panel::Property::DOUBLE_SIDED);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), isDoubleSided && !isBackPlaneVisible, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(contentPlaneAlphaMode, Dali::Scene3D::Material::AlphaModeType::BLEND, TEST_LOCATION);
+  DALI_TEST_EQUALS(backPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(doubleSidedPlaneNode.GetProperty<bool>(Dali::Actor::Property::VISIBLE), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPanelSetGetContent(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelSetPanelResolution");
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  application.GetWindow().Add(sceneView);
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  panel.SetPanelResolution(Vector2(300, 500));
+  DALI_TEST_CHECK(panel);
+  sceneView.Add(panel);
+
+  Dali::Toolkit::Control control = Dali::Toolkit::Control::New();
+  control.SetProperty(Dali::Actor::Property::SIZE, Vector2(300, 300));
+  control.SetBackgroundColor(Color::BLACK);
+  panel.SetContent(control);
+  DALI_TEST_EQUALS(control, panel.GetContent(), TEST_LOCATION);
+
+  Dali::Toolkit::Control retrievedControl;
+  Dali::Layer            panelRootLayer;
+  for(uint32_t i = 0; i < panel.GetChildCount(); ++i)
+  {
+    Dali::Actor child = panel.GetChildAt(i);
+    Dali::Layer layer = Dali::Layer::DownCast(child);
+    if(layer)
+    {
+      panelRootLayer = layer;
+      break;
+    }
+  }
+  DALI_TEST_CHECK(panelRootLayer);
+
+  for(uint32_t i = 0; i < panelRootLayer.GetChildCount(); ++i)
+  {
+    Dali::Actor child = panelRootLayer.GetChildAt(i);
+    Dali::Toolkit::Control childControl = Dali::Toolkit::Control::DownCast(child);
+    if(childControl)
+    {
+      retrievedControl = childControl;
+      break;
+    }
+  }
+  DALI_TEST_CHECK(retrievedControl);
+
+  DALI_TEST_EQUALS(control, retrievedControl, TEST_LOCATION);
+
+  Dali::Toolkit::Control secondControl = Dali::Toolkit::Control::New();
+  secondControl.SetProperty(Dali::Actor::Property::SIZE, Vector2(300, 300));
+  secondControl.SetBackgroundColor(Color::BLACK);
+  panel.SetContent(secondControl);
+  DALI_TEST_EQUALS(secondControl, panel.GetContent(), TEST_LOCATION);
+
+  retrievedControl.Reset();
+  panelRootLayer.Reset();
+  for(uint32_t i = 0; i < panel.GetChildCount(); ++i)
+  {
+    Dali::Actor child = panel.GetChildAt(i);
+    Dali::Layer layer = Dali::Layer::DownCast(child);
+    if(layer)
+    {
+      panelRootLayer = layer;
+      break;
+    }
+  }
+  DALI_TEST_CHECK(panelRootLayer);
+
+  for(uint32_t i = 0; i < panelRootLayer.GetChildCount(); ++i)
+  {
+    Dali::Actor child = panelRootLayer.GetChildAt(i);
+    Dali::Toolkit::Control childControl = Dali::Toolkit::Control::DownCast(child);
+    if(childControl)
+    {
+      retrievedControl = childControl;
+      break;
+    }
+  }
+  DALI_TEST_CHECK(retrievedControl);
+
+  DALI_TEST_EQUALS(secondControl, retrievedControl, TEST_LOCATION);
+  DALI_TEST_CHECK(retrievedControl != control);
+
+  DALI_TEST_CHECK(secondControl.GetParent());
+  panel.ClearPanel();
+  DALI_TEST_CHECK(!secondControl.GetParent());
+
+  END_TEST;
+}
+
+int UtcDaliPanelSetGetShadow(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliPanelSetGetShadow");
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  application.GetWindow().Add(sceneView);
+
+  Scene3D::Panel panel = Scene3D::Panel::New();
+  panel.SetPanelResolution(Vector2(300, 500));
+  DALI_TEST_CHECK(panel);
+  sceneView.Add(panel);
+
+  panel.CastShadow(true);
+  DALI_TEST_EQUALS(panel.IsShadowCasting(), true, TEST_LOCATION);
+
+  panel.CastShadow(false);
+  DALI_TEST_EQUALS(panel.IsShadowCasting(), false, TEST_LOCATION);
+
+  panel.ReceiveShadow(true);
+  DALI_TEST_EQUALS(panel.IsShadowReceiving(), true, TEST_LOCATION);
+
+  panel.ReceiveShadow(false);
+  DALI_TEST_EQUALS(panel.IsShadowReceiving(), false, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index 06ff712..4e77816 100644 (file)
  */
 
 #include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+#include <toolkit-timer.h>
+
 #include <dali-toolkit/dali-toolkit.h>
 #include <stdlib.h>
 #include <iostream>
 
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
-#include <toolkit-event-thread-callback.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
+
 
 using namespace Dali;
 using namespace Dali::Toolkit;
@@ -1195,3 +1199,375 @@ int UtcDaliSceneViewMasking(void)
 
   END_TEST;
 }
+
+namespace
+{
+static bool              gCaptureFinishedCalled{false};
+static int32_t           gCaptureId{-1};
+static Toolkit::ImageUrl gCapturedImageUrl;
+
+void OnCaptureFinished(Scene3D::SceneView sceneView, int32_t captureId, const Toolkit::ImageUrl& capturedImageUrl)
+{
+  gCaptureFinishedCalled = true;
+  gCaptureId             = captureId;
+  gCapturedImageUrl      = capturedImageUrl;
+}
+
+static int32_t                                             gCapturedCount{0};
+static std::vector<int32_t>                                gCaptureIds;
+static std::vector<Toolkit::ImageUrl>                      gCapturedImageUrls;
+
+void OnCaptureMultipleFinished(Scene3D::SceneView sceneView, int32_t captureId, const Toolkit::ImageUrl& capturedImageUrl)
+{
+  gCapturedCount++;
+  gCaptureIds.push_back(captureId);
+  gCapturedImageUrls.push_back(capturedImageUrl);
+}
+} // namespace
+
+int UtcDaliSceneViewCapture01(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCapturedImageUrl.GetUrl().empty(), false, TEST_LOCATION);
+
+  Toolkit::ImageUrl tempImageUrl = gCapturedImageUrl;
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  int32_t captureId2 = view.Capture(camera, Vector2(400, 400));
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(captureId, captureId2, 0.1f, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId2, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCapturedImageUrl.GetUrl().empty(), false, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(gCapturedImageUrl, tempImageUrl, 0.1f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCapture02(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureMultipleFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCapturedCount = 0;
+  gCaptureIds.clear();
+  gCapturedImageUrls.clear();
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+  int32_t captureId2 = view.Capture(camera, Vector2(300, 300));
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCapturedCount, 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureIds.size(), 2, TEST_LOCATION);
+  auto idIter1 = std::find(gCaptureIds.begin(), gCaptureIds.end(), captureId);
+  bool isIter1 = idIter1 != gCaptureIds.end();
+  DALI_TEST_EQUALS(isIter1, true, TEST_LOCATION);
+  auto idIter2 = std::find(gCaptureIds.begin(), gCaptureIds.end(), captureId2);
+  bool isIter2 = idIter2 != gCaptureIds.end();
+  DALI_TEST_EQUALS(isIter2, true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(gCapturedImageUrls.size(), 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrls[0], true, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrls[1], true, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(gCapturedImageUrls[0], gCapturedImageUrls[1], 0.1f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCaptureCancel(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  view.Unparent();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, false, TEST_LOCATION);
+
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCaptureFailed(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  Test::EmitGlobalTimerSignal();
+  Test::EmitGlobalTimerSignal();
+  Test::EmitGlobalTimerSignal();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, false, TEST_LOCATION);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCaptureFailed2(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  // not add on Scene.
+
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  application.RunIdles();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewSelectCamera(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  CameraActor camera1 = CameraActor::New3DCamera();
+  camera1.SetProperty(Dali::Actor::Property::NAME, "camera1");
+  view.AddCamera(camera1);
+  DALI_TEST_CHECK(!camera1.GetParent());
+  view.SelectCamera("camera1");
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+
+  CameraActor camera2 = CameraActor::New3DCamera();
+  camera2.SetProperty(Dali::Actor::Property::NAME, "camera2");
+  view.AddCamera(camera2);
+  DALI_TEST_CHECK(!camera2.GetParent());
+  view.SelectCamera("camera2");
+  DALI_TEST_EQUALS(camera2, view.GetSelectedCamera(), TEST_LOCATION);
+  DALI_TEST_CHECK(camera2.GetParent());
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_EQUALS(camera1.GetParent(), camera2.GetParent(), TEST_LOCATION);
+
+  view.SelectCamera("camera1");
+  Scene3D::Model model = Scene3D::Model::New();
+  view.Add(model);
+  model.Add(camera1);
+  DALI_TEST_EQUALS(camera1.GetParent(), model, TEST_LOCATION);
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_CHECK(camera2.GetParent());
+  DALI_TEST_EQUALS(camera1.GetParent(), model, TEST_LOCATION);
+
+  model.Unparent();
+  view.SelectCamera("camera1");
+  DALI_TEST_EQUALS(camera1, view.GetSelectedCamera(), TEST_LOCATION);
+  DALI_TEST_CHECK(camera1.GetParent());
+  DALI_TEST_CHECK(camera2.GetParent());
+  DALI_TEST_EQUALS(camera1.GetParent(), camera2.GetParent(), TEST_LOCATION);
+
+  camera1.Unparent();
+
+  application.SendNotification();
+  application.Render(0);
+  application.SendNotification();
+  application.Render(0);
+
+  DALI_TEST_NOT_EQUALS(camera1, view.GetSelectedCamera(), 0.01f, TEST_LOCATION);
+
+  END_TEST;
+}
index e3b52cd..9cbeedc 100644 (file)
@@ -59,6 +59,7 @@ SET(TEST_HARNESS_SOURCES
   ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
   ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
   ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-render-surface.cpp
   ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
 )
 
index 05354c0..c453a17 100755 (executable)
@@ -20,6 +20,8 @@ SET(TC_SOURCES
  utc-Dali-LineHelperFunctions.cpp
  utc-Dali-LogicalModel.cpp
  utc-Dali-PropertyHelper.cpp
+ utc-Dali-RenderEffect-internal.cpp
+ utc-Dali-SvgLoader.cpp
  utc-Dali-Text-AbstractStyleCharacterRun.cpp
  utc-Dali-Text-Characters.cpp
  utc-Dali-Text-CharacterSetConversion.cpp
@@ -105,6 +107,7 @@ SET(TEST_HARNESS_SOURCES
    ../dali-toolkit/dali-toolkit-test-utils/test-graphics-shader.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-platform-abstraction.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-render-controller.cpp
+   ../dali-toolkit/dali-toolkit-test-utils/test-render-surface.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-trace-call-stack.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-native-image.cpp
    dali-toolkit-test-utils/toolkit-text-utils.cpp
index a94f92b..d78e740 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-
 // EXTERNAL HEADERS
-#include <dali/devel-api/addons/addon-base.h>
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/devel-api/addons/addon-base.h>
 #include <dali/public-api/rendering/geometry.h>
 #include <dali/public-api/rendering/renderer.h>
 
 // INTERNAL HEADERS
 // Needed to access the private class members
 #define private public
+#include <dali-toolkit/internal/visuals/npatch/npatch-loader.h>
 #include <dali-toolkit/internal/visuals/npatch/npatch-visual.h>
-#include <dali-toolkit/internal/visuals/npatch-loader.h>
 #undef private
 
-
 using Dali::Toolkit::Internal::TextureManager;
 
 namespace Dali
 {
 namespace AddOns
 {
-
 struct DummyTiler
 {
 };
 
-void* CreateInstance( TextureManager* textureManager )
+void* CreateInstance(TextureManager* textureManager)
 {
   fprintf(stderr, "AddOn::CreateInstance( %p )\n", textureManager);
   return new DummyTiler;
@@ -50,53 +47,53 @@ namespace GeometryTiler
 {
 std::vector<std::string> gCallStack;
 
-static Geometry GetGeometryInternal(TextureManager::TextureId textureId, uint32_t& o0, uint32_t& o1 )
+static Geometry GetGeometryInternal(TextureManager::TextureId textureId, uint32_t& o0, uint32_t& o1)
 {
-  gCallStack.emplace_back( "GetGeometry" );
+  gCallStack.emplace_back("GetGeometry");
   o0 = 10;
   o1 = 5;
   fprintf(stderr, "AddOn::GetGeometryInternal()\n");
   return Dali::Geometry::New();
 }
 
-static Geometry CreateGeometryInternal(TextureManager::TextureId textureId, const Devel::PixelBuffer& pixelBuffer )
+static Geometry CreateGeometryInternal(TextureManager::TextureId textureId, const Devel::PixelBuffer& pixelBuffer)
 {
-  gCallStack.emplace_back( "CreateGeometry" );
+  gCallStack.emplace_back("CreateGeometry");
   fprintf(stderr, "AddOn::CreateGeometryInternal()\n");
   return Dali::Geometry::New();
 }
 
-static Geometry CreateGeometryMapInternal(const void* opacityMap,
+static Geometry CreateGeometryMapInternal(const void*       opacityMap,
                                           const Uint16Pair& gridSize,
-                                          uint32_t *outElements)
+                                          uint32_t*         outElements)
 {
-  gCallStack.emplace_back( "CreateGeometryGrid" );
+  gCallStack.emplace_back("CreateGeometryGrid");
   outElements[0] = 2;
   outElements[1] = 3;
   return Dali::Geometry::New();
 }
 
-static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchData* data )
+static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchData* data)
 {
-  gCallStack.emplace_back( "BuildNPatch" );
+  gCallStack.emplace_back("BuildNPatch");
   fprintf(stderr, "AddOn::NPatchBuild()\n");
   static char dummyData;
   return &dummyData;
 }
 
-static void NPatchDestroyInternal(void* object )
+static void NPatchDestroyInternal(void* object)
 {
-  gCallStack.emplace_back( "DestroyNPatch" );
+  gCallStack.emplace_back("DestroyNPatch");
   fprintf(stderr, "AddOn::NPatchDestroy()\n");
 }
 
-static void SubmitInternal(Renderer& renderer, const void* object  )
+static void SubmitInternal(Renderer& renderer, const void* object)
 {
-  gCallStack.emplace_back( "SubmitRenderTask" );
+  gCallStack.emplace_back("SubmitRenderTask");
   fprintf(stderr, "AddOn::SubmitInternal()\n");
 }
 
-static std::vector<std::string> GetCallStack( bool clear )
+static std::vector<std::string> GetCallStack(bool clear)
 {
   auto retval = gCallStack;
   if(clear)
@@ -106,10 +103,9 @@ static std::vector<std::string> GetCallStack( bool clear )
   return retval;
 }
 
-
-}
-}
-}
+} // namespace GeometryTiler
+} // namespace AddOns
+} // namespace Dali
 
 /**
  * OverdrawingAddOn implementation
@@ -117,13 +113,12 @@ static std::vector<std::string> GetCallStack( bool clear )
 class TestRenderingAddOn : public Dali::AddOns::AddOnBase
 {
 public:
-
-  void GetAddOnInfo( Dali::AddOnInfo& info ) override
+  void GetAddOnInfo(Dali::AddOnInfo& info) override
   {
-    info.type = Dali::AddOnType::GENERIC;
-    info.name = "oo-rendering";
-    info.version = Dali::DALI_ADDON_VERSION( 1, 0, 0 );
-    info.next = nullptr;
+    info.type    = Dali::AddOnType::GENERIC;
+    info.name    = "oo-rendering";
+    info.version = Dali::DALI_ADDON_VERSION(1, 0, 0);
+    info.next    = nullptr;
   }
 
   /**
@@ -133,7 +128,7 @@ public:
   Dali::AddOns::DispatchTable* GetGlobalDispatchTable() override
   {
     static Dali::AddOns::DispatchTable dispatchTable{};
-    if( dispatchTable.Empty() )
+    if(dispatchTable.Empty())
     {
       dispatchTable["Initialize"]         = Dali::AddOns::CreateInstance;
       dispatchTable["CreateGeometry"]     = Dali::AddOns::GeometryTiler::CreateGeometryInternal;
@@ -177,4 +172,4 @@ public:
   }
 };
 
-REGISTER_ADDON_CLASS( TestRenderingAddOn );
+REGISTER_ADDON_CLASS(TestRenderingAddOn);
index c6499b0..739f3fe 100644 (file)
 #include <automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h>
 #include <dali-toolkit-test-suite-utils.h>
-#include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/adaptor-framework/accessibility-bridge.h>
 #include <dali/devel-api/atspi-interfaces/accessible.h>
+#include <dali/devel-api/common/stage.h>
 #include "dbus-wrapper.h"
 
 namespace Dali
 {
 namespace Accessibility
 {
-  using MethodType = TestDBusWrapper::MethodType;
-  using MessagePtr = DBusWrapper::MessagePtr;
+using MethodType = TestDBusWrapper::MethodType;
+using MessagePtr = DBusWrapper::MessagePtr;
 
-  static bool gMoveOutedCalled = false;
+static bool gMoveOutedCalled      = false;
+static bool gPropertyChangeCalled = false;
 
-  void TestEnableSC(bool b)
-  {
-    static bool firstTime = true;
-    if (b && firstTime)
+struct StateChangedResult
+{
+  std::string state{};
+  int         value{-1};
+};
+static StateChangedResult gStateChangedResult{};
+
+void TestEnableSC(bool b)
+{
+  static bool firstTime = true;
+  if(b && firstTime)
+  {
+    gPropertyChangeCalled  = false;
+    gStateChangedResult    = {};
+    firstTime              = false;
+    auto        bridge     = Accessibility::Bridge::GetCurrentBridge();
+    Dali::Stage stage      = Dali::Stage::GetCurrent();
+    auto        accessible = Accessibility::Accessible::Get(stage.GetRootLayer());
+    bridge->ApplicationResumed();
+    bridge->AddTopLevelWindow(accessible);
+    bridge->SetApplicationName("TestApp");
+    bridge->Initialize();
+
+    static bool ScreenReaderEnabled = false;
+    static bool IsEnabled           = false;
+
+    auto* dbusWrapper = DBusWrapper::Installed();
+    auto* wr          = dynamic_cast<TestDBusWrapper*>(dbusWrapper);
+    if(wr == nullptr)
     {
-      firstTime = false;
-      auto bridge = Accessibility::Bridge::GetCurrentBridge();
-      Dali::Stage stage = Dali::Stage::GetCurrent();
-      auto accessible = Accessibility::Accessible::Get( stage.GetRootLayer() );
-      bridge->AddTopLevelWindow( accessible );
-      bridge->SetApplicationName( "TestApp" );
-      bridge->Initialize();
-
-      static bool ScreenReaderEnabled = false;
-      static bool IsEnabled = false;
-
-      auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", MethodType::Getter}] = [wr](const MessagePtr &m) -> MessagePtr {
-          auto reply = wr->newReplyMessage(m);
-          wr->Encode(reply, std::tuple<TestDBusWrapper::Variant<bool>>{ ScreenReaderEnabled });
-          return reply;
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/bus", "org.a11y.Status", "IsEnabled", MethodType::Getter}] = [wr](const MessagePtr &m) -> MessagePtr {
-          auto reply = wr->newReplyMessage(m);
-          wr->Encode(reply, std::tuple<TestDBusWrapper::Variant<bool>>{ IsEnabled });
-          return reply;
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/bus", "org.a11y.Bus", "GetAddress", MethodType::Method}] = [wr](const MessagePtr &m) -> MessagePtr {
-          auto reply = wr->newReplyMessage(m);
-          wr->Encode(reply, std::tuple<const char*>{ "bus" });
-          return reply;
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible/root", "org.a11y.atspi.Socket", "Embed", MethodType::Method}] = [wr](const MessagePtr &m) -> MessagePtr {
-          auto reply = wr->newReplyMessage(m);
-          wr->Encode(reply, std::tuple<Address>{ {"bus", "root"} });
-          return reply;
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible/root", "org.a11y.atspi.Socket", "Unembed", MethodType::Method}] = [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible/root", "org.a11y.atspi.Socket", "Embedded", MethodType::Method}] = [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "PropertyChange", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "StateChanged", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "BoundsChanged", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "ActiveDescendantChanged", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "TextChanged", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "TextCaretMoved", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "MoveOuted", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          gMoveOutedCalled = true;
-          return wr->newReplyMessage(m);
-      };
-
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Window", "Activate", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
-      wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Window", "Deactivate", MethodType::Method}] =
-      [wr](const MessagePtr &m) -> MessagePtr {
-          return wr->newReplyMessage(m);
-      };
+      fprintf(stderr, "Wrong case! TestDBusWrapper was not installed! have you forget to call DBusWrapper::Install(std::unique_ptr<DBusWrapper>(new TestDBusWrapper)); at startup?\n");
+      std::abort();
     }
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    wr->fromTestChangeProperty("/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", b);
-    wr->fromTestChangeProperty("/org/a11y/bus", "org.a11y.Status", "IsEnabled", b);
-  }
 
-  std::vector<Address> TestGetChildren(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall<std::vector<Address>>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetChildren", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", MethodType::Getter}] = [wr](const MessagePtr& m) -> MessagePtr {
+      auto reply = wr->newReplyMessage(m);
+      wr->Encode(reply, std::tuple<TestDBusWrapper::Variant<bool>>{ScreenReaderEnabled});
+      return reply;
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/bus", "org.a11y.Status", "IsEnabled", MethodType::Getter}] = [wr](const MessagePtr& m) -> MessagePtr {
+      auto reply = wr->newReplyMessage(m);
+      wr->Encode(reply, std::tuple<TestDBusWrapper::Variant<bool>>{IsEnabled});
+      return reply;
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/bus", "org.a11y.Bus", "GetAddress", MethodType::Method}] = [wr](const MessagePtr& m) -> MessagePtr {
+      auto reply = wr->newReplyMessage(m);
+      wr->Encode(reply, std::tuple<const char*>{"bus"});
+      return reply;
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible/root", "org.a11y.atspi.Socket", "Embed", MethodType::Method}] = [wr](const MessagePtr& m) -> MessagePtr {
+      auto reply = wr->newReplyMessage(m);
+      wr->Encode(reply, std::tuple<Address>{{"bus", "root"}});
+      return reply;
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible/root", "org.a11y.atspi.Socket", "Unembed", MethodType::Method}] = [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "PropertyChange", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      gPropertyChangeCalled = true;
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "StateChanged", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      std::tuple<std::string, int> decoded;
+      wr->Decode(m, decoded);
+      gStateChangedResult.state = std::get<0>(decoded);
+      gStateChangedResult.value = std::get<1>(decoded);
+
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "BoundsChanged", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "ActiveDescendantChanged", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "TextChanged", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "TextCaretMoved", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Object", "MoveOuted", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      gMoveOutedCalled = true;
+      return wr->newReplyMessage(m);
+    };
+
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Window", "Activate", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+    wr->testMethods[std::tuple<std::string, std::string, std::string, MethodType>{"/org/a11y/atspi/accessible", "org.a11y.atspi.Event.Window", "Deactivate", MethodType::Method}] =
+      [wr](const MessagePtr& m) -> MessagePtr {
+      return wr->newReplyMessage(m);
+    };
+  }
+  auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  wr->fromTestChangeProperty("/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", b);
+  wr->fromTestChangeProperty("/org/a11y/bus", "org.a11y.Status", "IsEnabled", b);
+}
+
+std::vector<Address> TestGetChildren(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::vector<Address>>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetChildren", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  std::string TestGetName(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto name = wr->fromTestGet<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "Name");
-    return name;
-  }
+std::string TestGetName(const Address& adr)
+{
+  auto wr   = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto name = wr->fromTestGet<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "Name");
+  return name;
+}
 
-  std::string TestGetDescription(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto description = wr->fromTestGet<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "Description");
-    return description;
-  }
+std::string TestGetDescription(const Address& adr)
+{
+  auto wr          = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto description = wr->fromTestGet<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "Description");
+  return description;
+}
 
-  uint32_t TestGetRole(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall<uint32_t>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetRole", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+uint32_t TestGetRole(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<uint32_t>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetRole", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  std::string TestGetRoleName(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetRoleName", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+std::string TestGetRoleName(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetRoleName", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  Address TestGetParent(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestGet< Address >(adr.GetPath(), "org.a11y.atspi.Accessible", "Parent");
-    return chs;
-  }
+Address TestGetParent(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestGet<Address>(adr.GetPath(), "org.a11y.atspi.Accessible", "Parent");
+  return chs;
+}
 
-  std::string TestGetLocalizedRoleName(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetLocalizedRoleName", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+std::string TestGetLocalizedRoleName(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetLocalizedRoleName", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  std::array< uint32_t, 2 > TestGetStates(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall<std::array< uint32_t, 2 >>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetState", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+std::array<uint32_t, 2> TestGetStates(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::array<uint32_t, 2>>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetState", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  std::unordered_map< std::string, std::string > TestGetAttributes(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall<std::unordered_map< std::string, std::string >>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetAttributes", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+std::unordered_map<std::string, std::string> TestGetAttributes(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::unordered_map<std::string, std::string>>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetAttributes", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  bool TestDoGesture(const Address &adr, Dali::Accessibility::Gesture type, int32_t xBeg, int32_t xEnd, int32_t yBeg, int32_t yEnd, Dali::Accessibility::GestureState state, uint32_t eventTime)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< bool >(adr.GetPath(), "org.a11y.atspi.Accessible", "DoGesture",
-        std::tuple< Dali::Accessibility::Gesture, int32_t, int32_t, int32_t, int32_t, Dali::Accessibility::GestureState, uint32_t >(type, xBeg, xEnd, yBeg, yEnd, state, eventTime ));
-    return std::move(std::get<0>(chs));
-  }
+bool TestDoGesture(const Address& adr, Dali::Accessibility::Gesture type, int32_t xBeg, int32_t xEnd, int32_t yBeg, int32_t yEnd, Dali::Accessibility::GestureState state, uint32_t eventTime)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Accessible", "DoGesture", std::tuple<Dali::Accessibility::Gesture, int32_t, int32_t, int32_t, int32_t, Dali::Accessibility::GestureState, uint32_t>(type, xBeg, xEnd, yBeg, yEnd, state, eventTime));
+  return std::move(std::get<0>(chs));
+}
 
-  std::vector< std::tuple< uint32_t, std::vector< Dali::Accessibility::Address > > > TestGetRelationSet(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< std::vector< std::tuple< uint32_t, std::vector< Dali::Accessibility::Address > > > >(adr.GetPath(), "org.a11y.atspi.Accessible", "GetRelationSet", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+std::vector<std::tuple<uint32_t, std::vector<Dali::Accessibility::Address>>> TestGetRelationSet(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::vector<std::tuple<uint32_t, std::vector<Dali::Accessibility::Address>>>>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetRelationSet", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  Address TestGetChildAtIndex(const Address &adr, int index)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< Address >(adr.GetPath(), "org.a11y.atspi.Accessible", "GetChildAtIndex", std::tuple< int >( index ));
-    return std::move(std::get<0>(chs));
-  }
+Address TestGetChildAtIndex(const Address& adr, int index)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<Address>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetChildAtIndex", std::tuple<int>(index));
+  return std::move(std::get<0>(chs));
+}
 
-  ComponentLayer TestGetLayer(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< Dali::Accessibility::ComponentLayer >(adr.GetPath(), "org.a11y.atspi.Component", "GetLayer", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+ComponentLayer TestGetLayer(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<Dali::Accessibility::ComponentLayer>(adr.GetPath(), "org.a11y.atspi.Component", "GetLayer", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  int TestGetIndexInParent(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< int >(adr.GetPath(), "org.a11y.atspi.Accessible", "GetIndexInParent", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+int TestGetIndexInParent(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<int>(adr.GetPath(), "org.a11y.atspi.Accessible", "GetIndexInParent", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  bool TestGrabFocus(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< bool >(adr.GetPath(), "org.a11y.atspi.Component", "GrabFocus", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+bool TestGrabFocus(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Component", "GrabFocus", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  bool TestGrabHighlight(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< bool >(adr.GetPath(), "org.a11y.atspi.Component", "GrabHighlight", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+bool TestGrabHighlight(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Component", "GrabHighlight", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  bool TestClearHighlight(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< bool >(adr.GetPath(), "org.a11y.atspi.Component", "ClearHighlight", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+bool TestClearHighlight(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Component", "ClearHighlight", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  std::tuple< int32_t, int32_t, int32_t, int32_t > TestGetExtents(const Address &adr, Dali::Accessibility::CoordinateType coordinateType)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< std::tuple< int32_t, int32_t, int32_t, int32_t > >(adr.GetPath(), "org.a11y.atspi.Component", "GetExtents", std::make_tuple(static_cast<uint32_t>(coordinateType)));
-    return std::move(std::get<0>(chs));
-  }
+std::tuple<int32_t, int32_t, int32_t, int32_t> TestGetExtents(const Address& adr, Dali::Accessibility::CoordinateType coordinateType)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::tuple<int32_t, int32_t, int32_t, int32_t>>(adr.GetPath(), "org.a11y.atspi.Component", "GetExtents", std::make_tuple(static_cast<uint32_t>(coordinateType)));
+  return std::move(std::get<0>(chs));
+}
 
-  int TestGetMdiZOrder(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< int16_t >(adr.GetPath(), "org.a11y.atspi.Component", "GetMDIZOrder", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+int TestGetMdiZOrder(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<int16_t>(adr.GetPath(), "org.a11y.atspi.Component", "GetMDIZOrder", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  double TestGetAlpha(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< double >(adr.GetPath(), "org.a11y.atspi.Component", "GetAlpha", std::tuple<>());
-    return std::move(std::get<0>(chs));
-  }
+double TestGetAlpha(const Address& adr)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<double>(adr.GetPath(), "org.a11y.atspi.Component", "GetAlpha", std::tuple<>());
+  return std::move(std::get<0>(chs));
+}
 
-  std::string TestGetActionName(const Address &adr, size_t index)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< std::string >(adr.GetPath(), "org.a11y.atspi.Action", "GetName", std::tuple< int32_t >( index ));
-    return std::move(std::get<0>(chs));
-  }
+std::string TestGetActionName(const Address& adr, size_t index)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Action", "GetName", std::tuple<int32_t>(index));
+  return std::move(std::get<0>(chs));
+}
 
-  std::string TestGetLocalizedActionName(const Address &adr, size_t index)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< std::string >(adr.GetPath(), "org.a11y.atspi.Action", "GetLocalizedName", std::tuple< int32_t >( index ));
-    return std::move(std::get<0>(chs));
-  }
+std::string TestGetLocalizedActionName(const Address& adr, size_t index)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Action", "GetLocalizedName", std::tuple<int32_t>(index));
+  return std::move(std::get<0>(chs));
+}
 
-  size_t TestGetActionCount(const Address &adr)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto count = wr->fromTestGet< int32_t >(adr.GetPath(), "org.a11y.atspi.Action", "NActions");
-    return count;
-  }
+size_t TestGetActionCount(const Address& adr)
+{
+  auto wr    = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto count = wr->fromTestGet<int32_t>(adr.GetPath(), "org.a11y.atspi.Action", "NActions");
+  return count;
+}
 
-  bool TestDoAction(const Address &adr, size_t index)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< bool >(adr.GetPath(), "org.a11y.atspi.Action", "DoAction", std::tuple< int32_t >( index ));
-    return std::move(std::get<0>(chs));
-  }
+bool TestDoAction(const Address& adr, size_t index)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Action", "DoAction", std::tuple<int32_t>(index));
+  return std::move(std::get<0>(chs));
+}
 
-  bool TestDoAction(const Address &adr, const std::string& name)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< bool >(adr.GetPath(), "org.a11y.atspi.Action", "DoActionName", std::tuple< std::string >( name ));
-    return std::move(std::get<0>(chs));
-  }
+bool TestDoAction(const Address& adr, const std::string& name)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Action", "DoActionName", std::tuple<std::string>(name));
+  return std::move(std::get<0>(chs));
+}
 
-  std::string TestGetActionKeyBinding(const Address &adr, size_t index)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< std::string >(adr.GetPath(), "org.a11y.atspi.Action", "GetKeyBinding", std::tuple< int32_t >( index ));
-    return std::move(std::get<0>(chs));
-  }
+std::string TestGetActionKeyBinding(const Address& adr, size_t index)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Action", "GetKeyBinding", std::tuple<int32_t>(index));
+  return std::move(std::get<0>(chs));
+}
 
-  std::string TestGetActionDescription(const Address &adr, size_t index)
-  {
-    auto wr = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
-    auto chs = wr->fromTestCall< std::string >(adr.GetPath(), "org.a11y.atspi.Action", "GetDescription", std::tuple< int32_t >( index ));
-    return std::move(std::get<0>(chs));
-  }
+std::string TestGetActionDescription(const Address& adr, size_t index)
+{
+  auto wr  = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto chs = wr->fromTestCall<std::string>(adr.GetPath(), "org.a11y.atspi.Action", "GetDescription", std::tuple<int32_t>(index));
+  return std::move(std::get<0>(chs));
+}
 
-  void TestResetMoveOutedCalled ()
-  {
-    gMoveOutedCalled = false;
-  }
+bool TestGetIncludeHidden(const Address& adr)
+{
+  auto dbus   = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  auto result = dbus->fromTestCall<bool>(adr.GetPath(), "org.a11y.atspi.Application", "GetIncludeHidden", std::tuple<>());
+  return std::move(std::get<0>(result));
+}
 
-  bool TestGetMoveOutedCalled ()
-  {
-    return gMoveOutedCalled;
-  }
+void TestSetIncludeHidden(const Address& adr, bool includeHidden)
+{
+  auto dbus = static_cast<TestDBusWrapper*>(DBusWrapper::Installed());
+  dbus->voidTestCall(adr.GetPath(), "org.a11y.atspi.Application", "SetIncludeHidden", std::tuple<bool>(includeHidden));
+}
 
-  void PrintTree(const Address &root, size_t depth)
+void TestResetMoveOutedCalled()
+{
+  gMoveOutedCalled = false;
+}
+
+bool TestGetMoveOutedCalled()
+{
+  return gMoveOutedCalled;
+}
+
+bool TestPropertyChangeCalled()
+{
+  return gPropertyChangeCalled;
+}
+
+bool TestStateChangedCalled()
+{
+  return !gStateChangedResult.state.empty();
+}
+
+bool TestStateChangedResult(const std::string_view& expectedState, int expectedValue)
+{
+  return expectedState == gStateChangedResult.state && expectedValue == gStateChangedResult.value;
+}
+
+void TestResetStateChangedResult()
+{
+  gStateChangedResult = {};
+}
+
+void PrintTree(const Address& root, size_t depth)
+{
+  auto name = TestGetName(root);
+  printf("%10s", root.GetPath().c_str());
+  for(unsigned int i = 0; i < depth; ++i) printf("  ");
+  printf("%s\n", name.c_str());
+  auto chs = TestGetChildren(root);
+  for(auto& c : chs)
   {
-    auto name = TestGetName(root);
-    printf("%10s", root.GetPath().c_str());
-    for(unsigned int i = 0; i < depth; ++i) printf("  ");
-    printf("%s\n", name.c_str());
-    auto chs = TestGetChildren(root);
-    for(auto &c : chs)
-    {
-      PrintTree(c, depth + 1);
-    }
+    PrintTree(c, depth + 1);
   }
+}
 
-  bool Find(const std::vector< std::string > &collection, const std::string &key)
+bool Find(const std::vector<std::string>& collection, const std::string& key)
+{
+  for(auto& it : collection)
   {
-    for(auto& it : collection)
+    if(it == key)
     {
-      if(it == key)
-      {
-        return true;
-      }
+      return true;
     }
-    return false;
   }
+  return false;
+}
 
 } // namespace Accessibility
 } // namespace Dali
index 92388e2..ebbcbeb 100644 (file)
@@ -7,38 +7,44 @@ namespace Dali
 {
 namespace Accessibility
 {
-  void TestEnableSC(bool b);
-  std::vector<Address> TestGetChildren(const Address &adr);
-  std::string TestGetName(const Address &adr);
-  std::string TestGetDescription(const Address &adr);
-  uint32_t TestGetRole(const Address &adr);
-  std::string TestGetRoleName(const Address &adr);
-  Address TestGetParent(const Address &adr);
-  std::string TestGetLocalizedRoleName(const Address &adr);
-  std::array< uint32_t, 2 > TestGetStates(const Address &adr);
-  std::unordered_map< std::string, std::string > TestGetAttributes(const Address &adr);
-  bool TestDoGesture(const Address &adr, Dali::Accessibility::Gesture type, int32_t xBeg, int32_t xEnd, int32_t yBeg, int32_t yEnd, Dali::Accessibility::GestureState state, uint32_t eventTime);
-  std::vector< std::tuple< uint32_t, std::vector< Dali::Accessibility::Address > > > TestGetRelationSet(const Address &adr);
-  Address TestGetChildAtIndex(const Address &adr, int index);
-  ComponentLayer TestGetLayer(const Address &adr);
-  int TestGetIndexInParent(const Address &adr);
-  bool TestGrabFocus(const Address &adr);
-  bool TestGrabHighlight(const Address &adr);
-  bool TestClearHighlight(const Address &adr);
-  std::tuple< int32_t, int32_t, int32_t, int32_t > TestGetExtents(const Address &adr, Dali::Accessibility::CoordinateType coordinateType );
-  int TestGetMdiZOrder(const Address &adr);
-  double TestGetAlpha(const Address &adr);
-  void PrintTree(const Address &root, size_t depth = 0);
-  bool Find( const std::vector< std::string > &collection, const std::string &key);
-  std::string TestGetActionName( const Address &adr, size_t index );
-  std::string TestGetLocalizedActionName( const Address &adr, size_t index );
-  size_t TestGetActionCount( const Address &adr );
-  bool TestDoAction ( const Address &adr, size_t index );
-  bool TestDoAction ( const Address &adr, const std::string& name );
-  std::string TestGetActionKeyBinding ( const Address &adr, size_t index );
-  std::string TestGetActionDescription ( const Address &adr, size_t index );
-  void TestResetMoveOutedCalled ();
-  bool TestGetMoveOutedCalled ();
+void                                                                           TestEnableSC(bool b);
+std::vector<Address>                                                           TestGetChildren(const Address& adr);
+std::string                                                                    TestGetName(const Address& adr);
+std::string                                                                    TestGetDescription(const Address& adr);
+uint32_t                                                                       TestGetRole(const Address& adr);
+std::string                                                                    TestGetRoleName(const Address& adr);
+Address                                                                        TestGetParent(const Address& adr);
+std::string                                                                    TestGetLocalizedRoleName(const Address& adr);
+std::array<uint32_t, 2>                                                        TestGetStates(const Address& adr);
+std::unordered_map<std::string, std::string>                                   TestGetAttributes(const Address& adr);
+bool                                                                           TestDoGesture(const Address& adr, Dali::Accessibility::Gesture type, int32_t xBeg, int32_t xEnd, int32_t yBeg, int32_t yEnd, Dali::Accessibility::GestureState state, uint32_t eventTime);
+std::vector<std::tuple<uint32_t, std::vector<Dali::Accessibility::Address> > > TestGetRelationSet(const Address& adr);
+Address                                                                        TestGetChildAtIndex(const Address& adr, int index);
+ComponentLayer                                                                 TestGetLayer(const Address& adr);
+int                                                                            TestGetIndexInParent(const Address& adr);
+bool                                                                           TestGrabFocus(const Address& adr);
+bool                                                                           TestGrabHighlight(const Address& adr);
+bool                                                                           TestClearHighlight(const Address& adr);
+std::tuple<int32_t, int32_t, int32_t, int32_t>                                 TestGetExtents(const Address& adr, Dali::Accessibility::CoordinateType coordinateType);
+int                                                                            TestGetMdiZOrder(const Address& adr);
+double                                                                         TestGetAlpha(const Address& adr);
+void                                                                           PrintTree(const Address& root, size_t depth = 0);
+bool                                                                           Find(const std::vector<std::string>& collection, const std::string& key);
+std::string                                                                    TestGetActionName(const Address& adr, size_t index);
+std::string                                                                    TestGetLocalizedActionName(const Address& adr, size_t index);
+size_t                                                                         TestGetActionCount(const Address& adr);
+bool                                                                           TestDoAction(const Address& adr, size_t index);
+bool                                                                           TestDoAction(const Address& adr, const std::string& name);
+std::string                                                                    TestGetActionKeyBinding(const Address& adr, size_t index);
+std::string                                                                    TestGetActionDescription(const Address& adr, size_t index);
+void                                                                           TestResetMoveOutedCalled();
+bool                                                                           TestGetMoveOutedCalled();
+bool                                                                           TestPropertyChangeCalled();
+bool                                                                           TestStateChangedCalled();
+bool                                                                           TestStateChangedResult(const std::string_view& expectedState, int expectedValue);
+void                                                                           TestResetStateChangedResult();
+bool                                                                           TestGetIncludeHidden(const Address& adr);
+void                                                                           TestSetIncludeHidden(const Address& adr, bool includeHidden);
 } // namespace Accessibility
 } // namespace Dali
 
index d3f4e25..0349dcd 100644 (file)
  */
 
 // EXTERNAL INCLUDES
-#include <memory>
 #include <array>
 #include <atomic>
 #include <cstdint>
 #include <cstring>
-#include <map>
+#include <functional>
 #include <list>
+#include <map>
+#include <memory>
 #include <sstream>
 #include <string>
 #include <thread>
@@ -32,7 +33,6 @@
 #include <type_traits>
 #include <unordered_map>
 #include <vector>
-#include <functional>
 
 // INTERNAL INCLUDES
 //#include <dali/public-api/adaptor-framework/accessibility.h>
 #define ATSPI_PREFIX_PATH "/org/a11y/atspi/accessible/"
 #define ATSPI_NULL_PATH "/org/a11y/atspi/null"
 
-
 //// BART QUICK HACK
-#define bart_assert(x) do{}while(0)
+#define bart_assert(x) \
+  do                   \
+  {                    \
+  } while(0)
 
 struct _Eina_Value;
 
@@ -66,569 +68,864 @@ struct DBusWrapper
   };
 
 #define DEFINE_TYPE(name, eldbus_name, unref_call) \
-    struct name { virtual ~name() = default; }; \
-    using name ## Ptr = std::shared_ptr<name>; \
-    using name ## WeakPtr = std::weak_ptr<name>; \
+  struct name                                      \
+  {                                                \
+    virtual ~name() = default;                     \
+  };                                               \
+  using name##Ptr     = std::shared_ptr<name>;     \
+  using name##WeakPtr = std::weak_ptr<name>;
 
   DEFINE_TYPE(Connection, Eldbus_Connection, )
-  DEFINE_TYPE(MessageIter, Eldbus_Message_Iter, eldbus_message_iter_container_close( Value ))
-  DEFINE_TYPE(Message, Eldbus_Message, eldbus_message_unref( Value ))
-  DEFINE_TYPE(Proxy, Eldbus_Proxy, eldbus_proxy_unref( Value ))
-  DEFINE_TYPE(Object, Eldbus_Object, eldbus_object_unref( Value ))
+  DEFINE_TYPE(MessageIter, Eldbus_Message_Iter, eldbus_message_iter_container_close(Value))
+  DEFINE_TYPE(Message, Eldbus_Message, eldbus_message_unref(Value))
+  DEFINE_TYPE(Proxy, Eldbus_Proxy, eldbus_proxy_unref(Value))
+  DEFINE_TYPE(Object, Eldbus_Object, eldbus_object_unref(Value))
   DEFINE_TYPE(Pending, Eldbus_Pending, )
   DEFINE_TYPE(EventPropertyChanged, Eldbus_Proxy_Event_Property_Changed, )
 
 #undef DEFINE_TYPE
-  virtual ConnectionPtr eldbus_address_connection_get_impl(const std::string &addr) = 0;
+  virtual ConnectionPtr eldbus_address_connection_get_impl(const std::stringaddr) = 0;
 
-#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig) \
-  virtual void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, type_set src) = 0; \
-  virtual bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, type_get &dst) = 0;
+#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig)                 \
+  virtual void eldbus_message_iter_arguments_append_impl(const MessageIterPtrit, type_set src) = 0; \
+  virtual bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr& it, type_get& dst)    = 0;
 #define eldbus_message_iter_arguments_append_impl_basic(type, sig) \
   eldbus_message_iter_arguments_append_impl_basic_impl(type, type, sig)
 
   eldbus_message_iter_arguments_append_impl_basic(uint8_t, y)
-  eldbus_message_iter_arguments_append_impl_basic(uint16_t, q)
-  eldbus_message_iter_arguments_append_impl_basic(uint32_t, u)
-  eldbus_message_iter_arguments_append_impl_basic(uint64_t, t)
-  eldbus_message_iter_arguments_append_impl_basic(int16_t, n)
-  eldbus_message_iter_arguments_append_impl_basic(int32_t, i)
-  eldbus_message_iter_arguments_append_impl_basic(int64_t, x)
-  eldbus_message_iter_arguments_append_impl_basic(double, d)
-  eldbus_message_iter_arguments_append_impl_basic(bool, b)
-  eldbus_message_iter_arguments_append_impl_basic_impl(const std::string &, std::string, s)
-  eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath &, ObjectPath, o)
+    eldbus_message_iter_arguments_append_impl_basic(uint16_t, q)
+      eldbus_message_iter_arguments_append_impl_basic(uint32_t, u)
+        eldbus_message_iter_arguments_append_impl_basic(uint64_t, t)
+          eldbus_message_iter_arguments_append_impl_basic(int16_t, n)
+            eldbus_message_iter_arguments_append_impl_basic(int32_t, i)
+              eldbus_message_iter_arguments_append_impl_basic(int64_t, x)
+                eldbus_message_iter_arguments_append_impl_basic(double, d)
+                  eldbus_message_iter_arguments_append_impl_basic(bool, b)
+                    eldbus_message_iter_arguments_append_impl_basic_impl(const std::string&, std::string, s)
+                      eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath&, ObjectPath, o)
 
 #undef eldbus_message_iter_arguments_append_impl_basic
 #undef eldbus_message_iter_arguments_append_impl_basic_impl
 
-  virtual MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr &it, int type, const std::string &sig) = 0;
-  virtual MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr &it, int type) = 0;
-  virtual MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr &it, bool write) = 0;
-  virtual MessagePtr eldbus_proxy_method_call_new_impl(const ProxyPtr &proxy, const std::string &funcName) = 0;
-  virtual MessagePtr eldbus_proxy_send_and_block_impl(const ProxyPtr &proxy, const MessagePtr &msg) = 0;
-  virtual bool eldbus_message_error_get_impl(const MessagePtr &msg, std::string &name, std::string &text) = 0;
-  virtual std::string eldbus_message_signature_get_impl(const MessagePtr &msg) = 0;
-
-  using SendCallback = std::function<void(const MessagePtr &msg)>;
-  virtual PendingPtr eldbus_proxy_send_impl(const ProxyPtr &proxy, const MessagePtr &msg, const SendCallback &callback) = 0;
-  virtual std::string eldbus_proxy_interface_get_impl(const ProxyPtr &) = 0;
-  virtual void eldbus_proxy_signal_handler_add_impl(const ProxyPtr &proxy, const std::string &member, const std::function<void(const MessagePtr &)> &cb) = 0;
-  virtual std::string eldbus_message_iter_signature_get_impl(const MessageIterPtr &iter) = 0;
-  virtual MessagePtr eldbus_message_method_return_new_impl( const MessagePtr &msg) = 0;
-  virtual MessagePtr eldbus_message_error_new_impl( const MessagePtr &msg, const std::string &err, const std::string &txt ) = 0;
-  virtual PendingPtr eldbus_connection_send_impl(const ConnectionPtr &conn, const MessagePtr &msg) = 0;
-  virtual MessagePtr eldbus_message_signal_new_impl(const std::string &path, const std::string &iface, const std::string &name) = 0;
-  virtual MessagePtr eldbus_message_ref_impl(const MessagePtr &msg) = 0;
-  virtual ConnectionPtr eldbus_connection_get_impl(ConnectionType type) = 0;
-  virtual std::string eldbus_connection_unique_name_get_impl(const ConnectionPtr &conn) = 0;
-  virtual ObjectPtr eldbus_object_get_impl( const ConnectionPtr &conn, const std::string &bus, const std::string &path ) = 0;
-  virtual ProxyPtr eldbus_proxy_get_impl( const ObjectPtr &obj, const std::string &interface ) = 0;
-  virtual ProxyPtr eldbus_proxy_copy_impl( const ProxyPtr &ptr) = 0;
-  virtual void eldbus_name_request_impl(const ConnectionPtr&, const std::string&) {} // no-op
-  virtual void eldbus_name_release_impl(const ConnectionPtr&, const std::string&) {} // no-op
+                        virtual MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr& it, int type, const std::string& sig) = 0;
+  virtual MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr& it, int type)                                        = 0;
+  virtual MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr& it, bool write)                                                           = 0;
+  virtual MessagePtr     eldbus_proxy_method_call_new_impl(const ProxyPtr& proxy, const std::string& funcName)                                    = 0;
+  virtual MessagePtr     eldbus_proxy_send_and_block_impl(const ProxyPtr& proxy, const MessagePtr& msg)                                           = 0;
+  virtual bool           eldbus_message_error_get_impl(const MessagePtr& msg, std::string& name, std::string& text)                               = 0;
+  virtual std::string    eldbus_message_signature_get_impl(const MessagePtr& msg)                                                                 = 0;
+
+  using SendCallback                                                                                                                                             = std::function<void(const MessagePtr& msg)>;
+  virtual PendingPtr    eldbus_proxy_send_impl(const ProxyPtr& proxy, const MessagePtr& msg, const SendCallback& callback)                                       = 0;
+  virtual std::string   eldbus_proxy_interface_get_impl(const ProxyPtr&)                                                                                         = 0;
+  virtual void          eldbus_proxy_signal_handler_add_impl(const ProxyPtr& proxy, const std::string& member, const std::function<void(const MessagePtr&)>& cb) = 0;
+  virtual std::string   eldbus_message_iter_signature_get_impl(const MessageIterPtr& iter)                                                                       = 0;
+  virtual MessagePtr    eldbus_message_method_return_new_impl(const MessagePtr& msg)                                                                             = 0;
+  virtual MessagePtr    eldbus_message_error_new_impl(const MessagePtr& msg, const std::string& err, const std::string& txt)                                     = 0;
+  virtual PendingPtr    eldbus_connection_send_impl(const ConnectionPtr& conn, const MessagePtr& msg)                                                            = 0;
+  virtual MessagePtr    eldbus_message_signal_new_impl(const std::string& path, const std::string& iface, const std::string& name)                               = 0;
+  virtual MessagePtr    eldbus_message_ref_impl(const MessagePtr& msg)                                                                                           = 0;
+  virtual ConnectionPtr eldbus_connection_get_impl(ConnectionType type)                                                                                          = 0;
+  virtual std::string   eldbus_connection_unique_name_get_impl(const ConnectionPtr& conn)                                                                        = 0;
+  virtual ObjectPtr     eldbus_object_get_impl(const ConnectionPtr& conn, const std::string& bus, const std::string& path)                                       = 0;
+  virtual ProxyPtr      eldbus_proxy_get_impl(const ObjectPtr& obj, const std::string& interface)                                                                = 0;
+  virtual ProxyPtr      eldbus_proxy_copy_impl(const ProxyPtr& ptr)                                                                                              = 0;
+  virtual void          eldbus_name_request_impl(const ConnectionPtr&, const std::string&)
+  {
+  } // no-op
+  virtual void eldbus_name_release_impl(const ConnectionPtr&, const std::string&)
+  {
+  } // no-op
 
   class StringStorage
   {
   public:
     struct char_ptr_deleter
     {
-      void operator()( char* p )
+      void operator()(char* p)
       {
-        free( p );
+        free(p);
       }
     };
-    std::vector< std::unique_ptr< char, char_ptr_deleter > > storage;
+    std::vector<std::unique_ptr<char, char_ptr_deleter>> storage;
 
-    const char* add( const char* txt )
+    const char* add(const char* txt)
     {
-      auto ptr = strdup( txt );
-      storage.push_back( std::unique_ptr< char, char_ptr_deleter >( ptr ) );
+      auto ptr = strdup(txt);
+      storage.push_back(std::unique_ptr<char, char_ptr_deleter>(ptr));
       return storage.back().get();
     }
-    const char* add( const std::string& txt )
+    const char* add(const std::string& txt)
     {
-      return add( txt.c_str() );
+      return add(txt.c_str());
     }
   };
 
   struct CallId
   {
-    static std::atomic< unsigned int > LastId;
-    unsigned int id = ++LastId;
+    static std::atomic<unsigned int> LastId;
+    unsigned int                     id = ++LastId;
   };
   struct MethodInfo
   {
-    CallId id;
-    std::string memberName;
-    std::vector< std::pair<std::string, std::string> > in, out; // _Eldbus_Arg_Info
-    std::function< DBusWrapper::MessagePtr( const DBusWrapper::MessagePtr &msg ) > callback;
+    CallId                                                                     id;
+    std::string                                                                memberName;
+    std::vector<std::pair<std::string, std::string>>                           in, out; // _Eldbus_Arg_Info
+    std::function<DBusWrapper::MessagePtr(const DBusWrapper::MessagePtr& msg)> callback;
   };
   struct SignalInfo
   {
-    CallId id;
-    std::string memberName;
-    std::vector< std::pair<std::string, std::string> > args;
-    unsigned int uniqueId;
+    CallId                                           id;
+    std::string                                      memberName;
+    std::vector<std::pair<std::string, std::string>> args;
+    unsigned int                                     uniqueId;
   };
   struct PropertyInfo
   {
-    CallId setterId, getterId;
-    std::string memberName, typeSignature;
-    std::function< std::string( const DBusWrapper::MessagePtr &src, const DBusWrapper::MessageIterPtr &dst ) > getCallback, setCallback;
+    CallId                                                                                               setterId, getterId;
+    std::string                                                                                          memberName, typeSignature;
+    std::function<std::string(const DBusWrapper::MessagePtr&src, const DBusWrapper::MessageIterPtr&dst)> getCallback, setCallback;
   };
   struct SignalId
   {
     CallId id;
 
     SignalId() = default;
-    SignalId( CallId id ) : id( id ) {}
+    SignalId(CallId id)
+    : id(id)
+    {
+    }
   };
-  virtual void add_interface_impl( bool fallback, const std::string& pathName,
-                              const ConnectionPtr &connection,
-                              std::vector<std::function<void()>> &destructors,
-                              const std::string& interfaceName,
-                              std::vector< MethodInfo >& dscrMethods,
-                              std::vector< PropertyInfo >& dscrProperties,
-                              std::vector< SignalInfo >& dscrSignals ) = 0;
-  virtual void add_property_changed_event_listener_impl( const ProxyPtr &proxy, const std::string &interface, const std::string &name, std::function< void( const _Eina_Value * ) > cb) = 0;
-  static DBusWrapper *Installed();
-  static void Install(std::unique_ptr<DBusWrapper>);
+  virtual void        add_interface_impl(bool fallback, const std::string& pathName, const ConnectionPtr& connection, std::vector<std::function<void()>>& destructors, const std::string& interfaceName, std::vector<MethodInfo>& dscrMethods, std::vector<PropertyInfo>& dscrProperties, std::vector<SignalInfo>& dscrSignals) = 0;
+  virtual void        add_property_changed_event_listener_impl(const ProxyPtr& proxy, const std::string& interface, const std::string& name, std::function<void(const _Eina_Value*)> cb)                                                                                                                                        = 0;
+  static DBusWrapper* Installed();
+  static void         Install(std::unique_ptr<DBusWrapper>);
 
   StringStorage Strings;
 };
 
-namespace detail {
-  enum class MethodType {
-    Method, Getter, Setter
-  };
+namespace detail
+{
+enum class MethodType
+{
+  Method,
+  Getter,
+  Setter
+};
 }
 
-namespace std {
-  template <> struct hash<std::tuple<std::string, std::string, std::string>> {
-    size_t operator () (const std::tuple<std::string, std::string, std::string> &a) const {
-      auto a1 = std::hash<std::string>()(std::get<0>(a));
-      auto a2 = std::hash<std::string>()(std::get<1>(a));
-      auto a3 = std::hash<std::string>()(std::get<2>(a));
-      size_t v = a1;
-      v = (v * 11400714819323198485llu) + a2;
-      v = (v * 11400714819323198485llu) + a3;
-      return v;
-    }
+namespace std
+{
+template<>
+struct hash<std::tuple<std::string, std::string, std::string>>
+{
+  size_t operator()(const std::tuple<std::string, std::string, std::string>& a) const
+  {
+    auto   a1 = std::hash<std::string>()(std::get<0>(a));
+    auto   a2 = std::hash<std::string>()(std::get<1>(a));
+    auto   a3 = std::hash<std::string>()(std::get<2>(a));
+    size_t v  = a1;
+    v         = (v * 11400714819323198485llu) + a2;
+    v         = (v * 11400714819323198485llu) + a3;
+    return v;
+  }
+};
+template<>
+struct hash<std::tuple<std::string, std::string, std::string, detail::MethodType>>
+{
+  size_t operator()(const std::tuple<std::string, std::string, std::string, detail::MethodType>& a) const
+  {
+    auto   a1 = std::hash<std::string>()(std::get<0>(a));
+    auto   a2 = std::hash<std::string>()(std::get<1>(a));
+    auto   a3 = std::hash<std::string>()(std::get<2>(a));
+    auto   a4 = static_cast<size_t>(std::get<3>(a));
+    size_t v  = a1;
+    v         = (v * 11400714819323198485llu) + a2;
+    v         = (v * 11400714819323198485llu) + a3;
+    v         = (v * 11400714819323198485llu) + a4;
+    return v;
+  }
+};
+template<>
+struct hash<std::tuple<std::string, std::string, unsigned int>>
+{
+  size_t operator()(const std::tuple<std::string, std::string, unsigned int>& a) const
+  {
+    auto   a1 = std::hash<std::string>()(std::get<0>(a));
+    auto   a2 = std::hash<std::string>()(std::get<1>(a));
+    auto   a3 = std::get<2>(a);
+    size_t v  = a1;
+    v         = (v * 11400714819323198485llu) + a2;
+    v         = (v * 11400714819323198485llu) + a3;
+    return v;
+  }
+};
+} // namespace std
+namespace detail
+{
+template<typename T>
+struct DBusSigImpl
+{
+  enum
+  {
+    value = 0,
+    end   = 0
   };
-  template <> struct hash<std::tuple<std::string, std::string, std::string, detail::MethodType>> {
-    size_t operator () (const std::tuple<std::string, std::string, std::string, detail::MethodType> &a) const {
-      auto a1 = std::hash<std::string>()(std::get<0>(a));
-      auto a2 = std::hash<std::string>()(std::get<1>(a));
-      auto a3 = std::hash<std::string>()(std::get<2>(a));
-      auto a4 = static_cast<size_t>(std::get<3>(a));
-      size_t v = a1;
-      v = (v * 11400714819323198485llu) + a2;
-      v = (v * 11400714819323198485llu) + a3;
-      v = (v * 11400714819323198485llu) + a4;
-      return v;
-    }
+};
+template<typename T>
+struct DBusSig
+{
+  enum
+  {
+    value = DBusSigImpl<typename std::decay<T>::type>::value,
+    end   = DBusSigImpl<typename std::decay<T>::type>::end
   };
-  template <> struct hash<std::tuple<std::string, std::string, unsigned int>> {
-    size_t operator () (const std::tuple<std::string, std::string, unsigned int> &a) const {
-      auto a1 = std::hash<std::string>()(std::get<0>(a));
-      auto a2 = std::hash<std::string>()(std::get<1>(a));
-      auto a3 = std::get<2>(a);
-      size_t v = a1;
-      v = (v * 11400714819323198485llu) + a2;
-      v = (v * 11400714819323198485llu) + a3;
-      return v;
-    }
+};
+template<typename T, typename Q, size_t I, size_t S>
+struct IndexFromTypeTupleImpl
+{
+  enum
+  {
+    value = std::is_same<typename std::decay<typename std::tuple_element<I, T>::type>::type, Q>::value ? I : IndexFromTypeTupleImpl<T, Q, I + 1, S>::value
   };
-}
-namespace detail {
-  template <typename T> struct DBusSigImpl { enum { value = 0, end = 0 }; };
-  template <typename T> struct DBusSig { enum { value = DBusSigImpl<typename std::decay<T>::type>::value, end = DBusSigImpl<typename std::decay<T>::type>::end }; };
-  template <typename T, typename Q, size_t I, size_t S> struct IndexFromTypeTupleImpl {
-    enum { value = std::is_same<typename std::decay<typename std::tuple_element<I, T>::type>::type, Q>::value ? I : IndexFromTypeTupleImpl<T, Q, I + 1, S>::value };
+};
+template<typename T, typename Q, size_t S>
+struct IndexFromTypeTupleImpl<T, Q, S, S>
+{
+  enum
+  {
+    value = S
   };
-  template <typename T, typename Q, size_t S> struct IndexFromTypeTupleImpl<T, Q, S, S> { enum { value = S }; };
-  template <typename T, typename Q> struct IndexFromTypeTuple {
-    enum { value = IndexFromTypeTupleImpl<T, typename std::decay<Q>::type, 0, std::tuple_size<T>::value>::value };
+};
+template<typename T, typename Q>
+struct IndexFromTypeTuple
+{
+  enum
+  {
+    value = IndexFromTypeTupleImpl<T, typename std::decay<Q>::type, 0, std::tuple_size<T>::value>::value
   };
-  template <typename T, typename = void> struct Encoder;
-  template <size_t I, size_t S, typename ... ARGS> struct EncoderTuple;
-}
+};
+template<typename T, typename = void>
+struct Encoder;
+template<size_t I, size_t S, typename... ARGS>
+struct EncoderTuple;
+} // namespace detail
 struct TestDBusWrapper : public DBusWrapper
 {
   using MethodType = detail::MethodType;
-  ConnectionPtr connection;
+  ConnectionPtr                                                                                                                   connection;
   std::unordered_map<std::tuple<std::string, std::string, std::string, MethodType>, std::function<MessagePtr(const MessagePtr&)>> testMethods;
   std::unordered_map<std::tuple<std::string, std::string, std::string, MethodType>, std::function<MessagePtr(const MessagePtr&)>> daliMethods;
-  std::unordered_map<std::tuple<std::string, std::string, unsigned int>, std::string> daliSignalsMap;
-  std::unordered_map<std::tuple<std::string, std::string, std::string>, std::function<void(const MessagePtr&)>> daliSignals;
-  std::unordered_map<std::tuple<std::string, std::string, std::string>, std::function<void(const _Eina_Value *)>> propertyChangeListeners;
+  std::unordered_map<std::tuple<std::string, std::string, unsigned int>, std::string>                                             daliSignalsMap;
+  std::unordered_map<std::tuple<std::string, std::string, std::string>, std::function<void(const MessagePtr&)>>                   daliSignals;
+  std::unordered_map<std::tuple<std::string, std::string, std::string>, std::function<void(const _Eina_Value*)>>                  propertyChangeListeners;
 
   std::vector<std::function<void()>> asyncCalls;
 
-  template <typename T> struct Variant {
+  template<typename T>
+  struct Variant
+  {
     T value;
     Variant() = default;
-    Variant(T t) : value(std::move(t)) { }
+    Variant(T t)
+    : value(std::move(t))
+    {
+    }
   };
-  template <typename ... ARGS> void Encode(const MessagePtr &m, const std::tuple<ARGS...> &args) {
+  template<typename... ARGS>
+  void Encode(const MessagePtr& m, const std::tuple<ARGS...>& args)
+  {
     auto iter = eldbus_message_iter_get_impl(m, true);
     detail::EncoderTuple<0, sizeof...(ARGS), ARGS...>::encode(*this, iter, args);
   }
-  template <typename ... ARGS> void Decode(const MessagePtr &m, std::tuple<ARGS...> &args) {
+  template<typename... ARGS>
+  void Decode(const MessagePtr& m, std::tuple<ARGS...>& args)
+  {
     auto iter = eldbus_message_iter_get_impl(m, false);
     detail::EncoderTuple<0, sizeof...(ARGS), ARGS...>::decode(*this, args, iter);
   }
-  MessagePtr newMessage(const std::string &path, const std::string &interface, const std::string &name, bool reply);
-  MessagePtr newReplyMessage(const MessagePtr &m);
+  MessagePtr newMessage(const std::string& path, const std::string& interface, const std::string& name, bool reply);
+  MessagePtr newReplyMessage(const MessagePtrm);
 
-  template <typename ... ARGS> void fromTestEmitSignal(std::string path, const std::string &interface, const std::string &name, const std::tuple<ARGS...> &args) {
-    if (path.empty()) throw error{};
-    if (path[0] != '/') path = "/org/a11y/atspi/accessible/" + path;
+  template<typename... ARGS>
+  void fromTestEmitSignal(std::string path, const std::string& interface, const std::string& name, const std::tuple<ARGS...>& args)
+  {
+    if(path.empty()) throw error{};
+    if(path[0] != '/') path = "/org/a11y/atspi/accessible/" + path;
     auto msg = newMessage(path, interface, name, false);
     Encode(msg, args);
-    auto it = daliSignals.find(std::tuple<std::string, std::string, std::string>{ path, interface, name });
-    if (it == daliSignals.end()) throw error{};
+    auto it = daliSignals.find(std::tuple<std::string, std::string, std::string>{path, interface, name});
+    if(it == daliSignals.end()) throw error{};
     it->second(msg);
   }
   static std::shared_ptr<_Eina_Value> createEinaValue(bool);
-  template <typename T> void fromTestChangeProperty(std::string path, const std::string &interface, const std::string &name, T new_value) {
+  template<typename T>
+  void fromTestChangeProperty(std::string path, const std::string& interface, const std::string& name, T new_value)
+  {
     auto v = createEinaValue(new_value);
-    if (path.empty()) throw error{};
-    if (path[0] != '/') path = "/org/a11y/atspi/accessible/" + path;
-    auto it = propertyChangeListeners.find(std::tuple<std::string, std::string, std::string>{ path, interface, name });
-    if (it == propertyChangeListeners.end()) throw error{};
+    if(path.empty()) throw error{};
+    if(path[0] != '/') path = "/org/a11y/atspi/accessible/" + path;
+    auto it = propertyChangeListeners.find(std::tuple<std::string, std::string, std::string>{path, interface, name});
+    if(it == propertyChangeListeners.end()) throw error{};
     it->second(v.get());
   }
-  template <typename ... ARGS1, typename ... ARGS2> std::tuple<ARGS1...> fromTestCall(const std::string &path, const std::string &interface, const std::string &name, const std::tuple<ARGS2...> &args) {
+  template<typename... ARGS1, typename... ARGS2>
+  std::tuple<ARGS1...> fromTestCall(const std::string& path, const std::string& interface, const std::string& name, const std::tuple<ARGS2...>& args)
+  {
     auto msg = newMessage(path, interface, name, false);
     Encode(msg, args);
-    auto res = call(daliMethods, "daliMethods", msg, MethodType::Method);
+    auto        res = call(daliMethods, "daliMethods", msg, MethodType::Method);
     std::string a, b;
-    if (eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
+    if(eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
     std::tuple<ARGS1...> tmp;
     Decode(res, tmp);
     return tmp;
   }
-  template <typename T> T fromTestGet(const std::string &path, const std::string &interface, const std::string &name) {
-    auto msg = newMessage(path, interface, name, false);
-    auto res = call(daliMethods, "daliMethods", msg, MethodType::Getter);
+  template<typename T>
+  T fromTestGet(const std::string& path, const std::string& interface, const std::string& name)
+  {
+    auto        msg = newMessage(path, interface, name, false);
+    auto        res = call(daliMethods, "daliMethods", msg, MethodType::Getter);
     std::string a, b;
-    if (eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
+    if(eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
     std::tuple<T> tmp;
     Decode(res, tmp);
     return std::move(std::get<0>(tmp));
   }
-  template <typename ... ARGS1, typename T> std::tuple<ARGS1...> fromTestSet(const std::string &path, const std::string &interface, const std::string &name, const T &arg) {
+  template<typename... ARGS1, typename T>
+  std::tuple<ARGS1...> fromTestSet(const std::string& path, const std::string& interface, const std::string& name, const T& arg)
+  {
     auto msg = newMessage(path, interface, name, false);
-    Encode(msg, TestDBusWrapper::Variant<T>{ arg });
-    auto res = call(daliMethods, "daliMethods", msg, MethodType::Setter);
+    Encode(msg, TestDBusWrapper::Variant<T>{arg});
+    auto        res = call(daliMethods, "daliMethods", msg, MethodType::Setter);
     std::string a, b;
-    if (eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
+    if(eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
     std::tuple<ARGS1...> tmp;
     Decode(res, tmp);
     return tmp;
   }
+  template<typename... ARGS1>
+  void voidTestCall(const std::string& path, const std::string& interface, const std::string& name, const std::tuple<ARGS1...>& args)
+  {
+    auto msg = newMessage(path, interface, name, false);
+    Encode(msg, args);
+    auto        res = call(daliMethods, "daliMethods", msg, MethodType::Method);
+    std::string a, b;
+    if(eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b;
+  }
 
   TestDBusWrapper();
   ~TestDBusWrapper();
 
-  class error : public std::exception {
+  class error : public std::exception
+  {
     std::shared_ptr<std::ostringstream> temp = std::make_shared<std::ostringstream>();
-    mutable std::string text;
+    mutable std::string                 text;
+
   public:
     error() = default;
 
-    template <typename T> error &operator << (T &&t) {
+    template<typename T>
+    error& operator<<(T&& t)
+    {
       *temp << std::forward<T>(t);
       return *this;
     }
-    const char *what() const noexcept override {
+    const char* what() const noexcept override
+    {
       text = temp->str();
       return text.c_str();
     }
   };
-  #define DEFINE_TYPE(name) struct name ## Impl; static name ## Impl *get(const name ## Ptr &);
+#define DEFINE_TYPE(name) \
+  struct name##Impl;      \
+  static name##Impl* get(const name##Ptr&);
   DEFINE_TYPE(Connection)
   DEFINE_TYPE(Proxy)
   DEFINE_TYPE(Object)
   DEFINE_TYPE(Message)
   DEFINE_TYPE(MessageIter)
-  #undef DEFINE_TYPE
+#undef DEFINE_TYPE
 
   class Element;
-  using ElementList = std::list<Element>;
-  using ElementMap = std::map<std::string, std::string>;
+  using ElementList  = std::list<Element>;
+  using ElementMap   = std::map<std::string, std::string>;
   using ElementTuple = std::tuple<
-    uint8_t, uint16_t, uint32_t, uint64_t, int16_t, int32_t, int64_t, double, bool, std::string, ObjectPath,
-    ElementList
-  >;
-
-  class Element {
+    uint8_t,
+    uint16_t,
+    uint32_t,
+    uint64_t,
+    int16_t,
+    int32_t,
+    int64_t,
+    double,
+    bool,
+    std::string,
+    ObjectPath,
+    ElementList>;
+
+  class Element
+  {
     ElementTuple data;
-    char signature_ = 0, end_ = 0, index_ = 0;
-    template <typename T> void set(T &&t, char signature = detail::DBusSig<T>::value) {
+    char         signature_ = 0, end_ = 0, index_ = 0;
+    template<typename T>
+    void set(T&& t, char signature = detail::DBusSig<T>::value)
+    {
       signature_ = signature;
-      end_ = detail::DBusSig<T>::end;
-      index_ = static_cast<char>(detail::IndexFromTypeTuple<ElementTuple, T>::value);
-      get<T>() = std::forward<T>(t);
+      end_       = detail::DBusSig<T>::end;
+      index_     = static_cast<char>(detail::IndexFromTypeTuple<ElementTuple, T>::value);
+      get<T>()   = std::forward<T>(t);
     }
+
   public:
-    template <typename T> Element(T &&t, typename std::enable_if<detail::DBusSig<T>::value != 0>::type* = nullptr) { set(std::forward<T>(t)); }
-    Element(ElementList t, int signature) { set(std::move(t), static_cast<char>(signature)); }
-
-    char signature() const { return signature_; }
-    char end() const { return end_; }
-    char index() const { return index_; }
-    bool isContainer() const { return index_ == detail::IndexFromTypeTuple<ElementTuple, ElementList>::value; }
-    template <typename T, typename = typename std::enable_if<detail::DBusSig<T>::value != 0>::type>
-    bool is() const { return index_ == detail::IndexFromTypeTuple<ElementTuple, T>::value; }
-    template <typename T, typename = typename std::enable_if<detail::DBusSig<T>::value != 0>::type>
-    const T &get() const { if (!is<T>()) throw error{}; return std::get<detail::IndexFromTypeTuple<ElementTuple, T>::value>(data); }
-    template <typename T, typename = typename std::enable_if<detail::DBusSig<T>::value != 0>::type>
-    T &get() { if (!is<T>()) throw error{}; return std::get<detail::IndexFromTypeTuple<ElementTuple, T>::value>(data); }
+    template<typename T>
+    Element(T&& t, typename std::enable_if<detail::DBusSig<T>::value != 0>::type* = nullptr)
+    {
+      set(std::forward<T>(t));
+    }
+    Element(ElementList t, int signature)
+    {
+      set(std::move(t), static_cast<char>(signature));
+    }
+
+    char signature() const
+    {
+      return signature_;
+    }
+    char end() const
+    {
+      return end_;
+    }
+    char index() const
+    {
+      return index_;
+    }
+    bool isContainer() const
+    {
+      return index_ == detail::IndexFromTypeTuple<ElementTuple, ElementList>::value;
+    }
+    template<typename T, typename = typename std::enable_if<detail::DBusSig<T>::value != 0>::type>
+    bool is() const
+    {
+      return index_ == detail::IndexFromTypeTuple<ElementTuple, T>::value;
+    }
+    template<typename T, typename = typename std::enable_if<detail::DBusSig<T>::value != 0>::type>
+    const T& get() const
+    {
+      if(!is<T>()) throw error{};
+      return std::get<detail::IndexFromTypeTuple<ElementTuple, T>::value>(data);
+    }
+    template<typename T, typename = typename std::enable_if<detail::DBusSig<T>::value != 0>::type>
+    T& get()
+    {
+      if(!is<T>()) throw error{};
+      return std::get<detail::IndexFromTypeTuple<ElementTuple, T>::value>(data);
+    }
   };
 
-  ConnectionPtr eldbus_address_connection_get_impl(const std::string &addr) override;
+  ConnectionPtr eldbus_address_connection_get_impl(const std::stringaddr) override;
 
-#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig) \
-  void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, type_set src) override; \
-  bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, type_get &dst) override;
+#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig)              \
+  void eldbus_message_iter_arguments_append_impl(const MessageIterPtrit, type_set src) override; \
+  bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr& it, type_get& dst) override;
 #define eldbus_message_iter_arguments_append_impl_basic(type, sig) \
   eldbus_message_iter_arguments_append_impl_basic_impl(type, type, sig)
 
   eldbus_message_iter_arguments_append_impl_basic(uint8_t, y)
-  eldbus_message_iter_arguments_append_impl_basic(uint16_t, q)
-  eldbus_message_iter_arguments_append_impl_basic(uint32_t, u)
-  eldbus_message_iter_arguments_append_impl_basic(uint64_t, t)
-  eldbus_message_iter_arguments_append_impl_basic(int16_t, n)
-  eldbus_message_iter_arguments_append_impl_basic(int32_t, i)
-  eldbus_message_iter_arguments_append_impl_basic(int64_t, x)
-  eldbus_message_iter_arguments_append_impl_basic(double, d)
-  eldbus_message_iter_arguments_append_impl_basic(bool, b)
-  eldbus_message_iter_arguments_append_impl_basic_impl(const std::string &, std::string, s)
-  eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath &, ObjectPath, o)
+    eldbus_message_iter_arguments_append_impl_basic(uint16_t, q)
+      eldbus_message_iter_arguments_append_impl_basic(uint32_t, u)
+        eldbus_message_iter_arguments_append_impl_basic(uint64_t, t)
+          eldbus_message_iter_arguments_append_impl_basic(int16_t, n)
+            eldbus_message_iter_arguments_append_impl_basic(int32_t, i)
+              eldbus_message_iter_arguments_append_impl_basic(int64_t, x)
+                eldbus_message_iter_arguments_append_impl_basic(double, d)
+                  eldbus_message_iter_arguments_append_impl_basic(bool, b)
+                    eldbus_message_iter_arguments_append_impl_basic_impl(const std::string&, std::string, s)
+                      eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath&, ObjectPath, o)
 
 #undef eldbus_message_iter_arguments_append_impl_basic
 #undef eldbus_message_iter_arguments_append_impl_basic_impl
 
-  MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr &it, int type, const std::string &sig) override;
-  MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr &it, int type) override;
-  MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr &it, bool write) override;
-  MessagePtr eldbus_proxy_method_call_new_impl(const ProxyPtr &proxy, const std::string &funcName) override;
-  MessagePtr eldbus_proxy_send_and_block_impl(const ProxyPtr &proxy, const MessagePtr &msg) override;
-  bool eldbus_message_error_get_impl(const MessagePtr &msg, std::string &name, std::string &text) override;
-  std::string eldbus_message_signature_get_impl(const MessagePtr &msg) override;
-  PendingPtr eldbus_proxy_send_impl(const ProxyPtr &proxy, const MessagePtr &msg, const SendCallback &callback) override;
-  std::string eldbus_proxy_interface_get_impl(const ProxyPtr &) override;
-  void eldbus_proxy_signal_handler_add_impl(const ProxyPtr &proxy, const std::string &member, const std::function<void(const MessagePtr &)> &cb) override;
-  std::string eldbus_message_iter_signature_get_impl(const MessageIterPtr &iter) override;
-  MessagePtr eldbus_message_method_return_new_impl( const MessagePtr &msg) override;
-  MessagePtr eldbus_message_error_new_impl( const MessagePtr &msg, const std::string &err, const std::string &txt ) override;
-  PendingPtr eldbus_connection_send_impl(const ConnectionPtr &conn, const MessagePtr &msg) override;
-  MessagePtr eldbus_message_signal_new_impl(const std::string &path, const std::string &iface, const std::string &name) override;
-  MessagePtr eldbus_message_ref_impl(const MessagePtr &msg) override;
-  ConnectionPtr eldbus_connection_get_impl(ConnectionType type) override;
-  std::string eldbus_connection_unique_name_get_impl(const ConnectionPtr &conn) override;
-  ObjectPtr eldbus_object_get_impl( const ConnectionPtr &conn, const std::string &bus, const std::string &path ) override;
-  ProxyPtr eldbus_proxy_get_impl( const ObjectPtr &obj, const std::string &interface ) override;
-  ProxyPtr eldbus_proxy_copy_impl( const ProxyPtr &ptr) override;
-  void add_property_changed_event_listener_impl( const ProxyPtr &proxy, const std::string &interface, const std::string &name, std::function< void( const _Eina_Value * ) > cb) override;
-  void add_interface_impl( bool fallback, const std::string& pathName,
-                              const ConnectionPtr &connection,
-                              std::vector<std::function<void()>> &destructors,
-                              const std::string& interfaceName,
-                              std::vector< MethodInfo >& dscrMethods,
-                              std::vector< PropertyInfo >& dscrProperties,
-                              std::vector< SignalInfo >& dscrSignals ) override;
-  static bool completed(const MessageIterPtr &iter);
+                        MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr& it, int type, const std::string& sig) override;
+  MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr& it, int type) override;
+  MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr& it, bool write) override;
+  MessagePtr     eldbus_proxy_method_call_new_impl(const ProxyPtr& proxy, const std::string& funcName) override;
+  MessagePtr     eldbus_proxy_send_and_block_impl(const ProxyPtr& proxy, const MessagePtr& msg) override;
+  bool           eldbus_message_error_get_impl(const MessagePtr& msg, std::string& name, std::string& text) override;
+  std::string    eldbus_message_signature_get_impl(const MessagePtr& msg) override;
+  PendingPtr     eldbus_proxy_send_impl(const ProxyPtr& proxy, const MessagePtr& msg, const SendCallback& callback) override;
+  std::string    eldbus_proxy_interface_get_impl(const ProxyPtr&) override;
+  void           eldbus_proxy_signal_handler_add_impl(const ProxyPtr& proxy, const std::string& member, const std::function<void(const MessagePtr&)>& cb) override;
+  std::string    eldbus_message_iter_signature_get_impl(const MessageIterPtr& iter) override;
+  MessagePtr     eldbus_message_method_return_new_impl(const MessagePtr& msg) override;
+  MessagePtr     eldbus_message_error_new_impl(const MessagePtr& msg, const std::string& err, const std::string& txt) override;
+  PendingPtr     eldbus_connection_send_impl(const ConnectionPtr& conn, const MessagePtr& msg) override;
+  MessagePtr     eldbus_message_signal_new_impl(const std::string& path, const std::string& iface, const std::string& name) override;
+  MessagePtr     eldbus_message_ref_impl(const MessagePtr& msg) override;
+  ConnectionPtr  eldbus_connection_get_impl(ConnectionType type) override;
+  std::string    eldbus_connection_unique_name_get_impl(const ConnectionPtr& conn) override;
+  ObjectPtr      eldbus_object_get_impl(const ConnectionPtr& conn, const std::string& bus, const std::string& path) override;
+  ProxyPtr       eldbus_proxy_get_impl(const ObjectPtr& obj, const std::string& interface) override;
+  ProxyPtr       eldbus_proxy_copy_impl(const ProxyPtr& ptr) override;
+  void           add_property_changed_event_listener_impl(const ProxyPtr& proxy, const std::string& interface, const std::string& name, std::function<void(const _Eina_Value*)> cb) override;
+  void           add_interface_impl(bool fallback, const std::string& pathName, const ConnectionPtr& connection, std::vector<std::function<void()>>& destructors, const std::string& interfaceName, std::vector<MethodInfo>& dscrMethods, std::vector<PropertyInfo>& dscrProperties, std::vector<SignalInfo>& dscrSignals) override;
+  static bool    completed(const MessageIterPtr& iter);
+
 private:
-  MessagePtr call(std::unordered_map<std::tuple<std::string, std::string, std::string, MethodType>, std::function<MessagePtr(const MessagePtr&)>> &mp, const std::string &name, const MessagePtr &msg, MethodType type);
-};
-
-namespace detail {
-  template <> struct DBusSigImpl<uint8_t> { enum { value = 'y', end = 0 }; };
-  template <> struct DBusSigImpl<uint16_t> { enum { value = 'q', end = 0 }; };
-  template <> struct DBusSigImpl<uint32_t> { enum { value = 'u', end = 0 }; };
-  template <> struct DBusSigImpl<uint64_t> { enum { value = 't', end = 0 }; };
-  template <> struct DBusSigImpl<int16_t> { enum { value = 'n', end = 0 }; };
-  template <> struct DBusSigImpl<int32_t> { enum { value = 'i', end = 0 }; };
-  template <> struct DBusSigImpl<int64_t> { enum { value = 'x', end = 0 }; };
-  template <> struct DBusSigImpl<double> { enum { value = 'd', end = 0 }; };
-  template <> struct DBusSigImpl<bool> { enum { value = 'b', end = 0 }; };
-  template <> struct DBusSigImpl<std::string> { enum { value = 's', end = 0 }; };
-  template <> struct DBusSigImpl<ObjectPath> { enum { value = 'o', end = 0 }; };
-  template <> struct DBusSigImpl<TestDBusWrapper::ElementList> { enum { value = '(', end = ')' }; };
-  template <> struct DBusSigImpl<TestDBusWrapper::ElementMap> { enum { value = '{', end = '}' }; };
-
-  template <typename T> struct Encoder<T, decltype(TestDBusWrapper().eldbus_message_iter_arguments_append_impl(TestDBusWrapper::MessageIterPtr(), T()))> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) {
-      w.eldbus_message_iter_arguments_append_impl(tgt, src);
-    }
-    static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      if (!w.eldbus_message_iter_get_and_next_impl(src, tgt)) throw TestDBusWrapper::error{};
-    }
+  MessagePtr call(std::unordered_map<std::tuple<std::string, std::string, std::string, MethodType>, std::function<MessagePtr(const MessagePtr&)>>& mp, const std::string& name, const MessagePtr& msg, MethodType type);
+};
+
+namespace detail
+{
+template<>
+struct DBusSigImpl<uint8_t>
+{
+  enum
+  {
+    value = 'y',
+    end   = 0
   };
-  template <typename T> struct Encoder<T, typename std::enable_if<std::is_enum<T>::value>::type> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) {
-      Encoder<typename std::underlying_type<T>::type>::encode(w, tgt, static_cast<typename std::underlying_type<T>::type>(src));
-    }
-    static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      typename std::underlying_type<T>::type v = 0;
-      Encoder<typename std::underlying_type<T>::type>::decode(w, v, src);
-      tgt = static_cast<T>(v);
-    }
+};
+template<>
+struct DBusSigImpl<uint16_t>
+{
+  enum
+  {
+    value = 'q',
+    end   = 0
   };
-  template <typename T> struct Encoder<const T> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) {
-      Encoder<T>::encode(w, tgt, src);
-    }
-    static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      Encoder<T>::decode(w, tgt, src);
-    }
+};
+template<>
+struct DBusSigImpl<uint32_t>
+{
+  enum
+  {
+    value = 'u',
+    end   = 0
   };
-  template <typename T> struct Encoder<const T &> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) {
-      Encoder<T>::encode(w, tgt, src);
-    }
-    static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      Encoder<T>::decode(w, tgt, src);
-    }
+};
+template<>
+struct DBusSigImpl<uint64_t>
+{
+  enum
+  {
+    value = 't',
+    end   = 0
   };
-  template <typename T> struct Encoder<T &> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) {
-      Encoder<T>::encode(w, tgt, src);
-    }
-    static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      Encoder<T>::decode(w, tgt, src);
-    }
+};
+template<>
+struct DBusSigImpl<int16_t>
+{
+  enum
+  {
+    value = 'n',
+    end   = 0
   };
-  template <size_t I, size_t S, typename ... ARGS> struct EncoderTuple {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::tuple<ARGS...> &src) {
-      Encoder<typename std::tuple_element<I, std::tuple<ARGS...>>::type>::encode(w, tgt, std::get<I>(src));
-      EncoderTuple<I + 1, S, ARGS...>::encode(w, tgt, src);
-    }
-    static void decode(TestDBusWrapper &w, std::tuple<ARGS...> &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      Encoder<typename std::tuple_element<I, std::tuple<ARGS...>>::type>::decode(w, std::get<I>(tgt), src);
-      EncoderTuple<I + 1, S, ARGS...>::decode(w, tgt, src);
-    }
+};
+template<>
+struct DBusSigImpl<int32_t>
+{
+  enum
+  {
+    value = 'i',
+    end   = 0
   };
-  template <size_t S, typename ... ARGS> struct EncoderTuple<S, S, ARGS...> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::tuple<ARGS...> &src) { }
-    static void decode(TestDBusWrapper &w, std::tuple<ARGS...> &tgt, const TestDBusWrapper::MessageIterPtr &src) { }
+};
+template<>
+struct DBusSigImpl<int64_t>
+{
+  enum
+  {
+    value = 'x',
+    end   = 0
   };
-  template <typename ...ARGS> struct Encoder<std::tuple<ARGS...>> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::tuple<ARGS...> &src) {
-      auto var = w.eldbus_message_iter_container_new_impl( tgt, 'r', "");
-      EncoderTuple<0, sizeof...(ARGS), ARGS...>::encode(w, var, src);
-    }
-    static void decode(TestDBusWrapper &w, std::tuple<ARGS...> &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'r' );
-      if( !s ) throw TestDBusWrapper::error{};
-      EncoderTuple<0, sizeof...(ARGS), ARGS...>::decode(w, tgt, s);
-    }
+};
+template<>
+struct DBusSigImpl<double>
+{
+  enum
+  {
+    value = 'd',
+    end   = 0
   };
-  template <typename A, typename B> struct Encoder<std::pair<A, B>> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::pair<A, B> &src, char type = 'r') {
-      auto var = w.eldbus_message_iter_container_new_impl( tgt, type, "");
-      Encoder<A>::encode(w, var, src.first);
-      Encoder<B>::encode(w, var, src.second);
-    }
-    static void decode(TestDBusWrapper &w, std::pair<A, B> &tgt, const TestDBusWrapper::MessageIterPtr &src, char type = 'r') {
-      auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, type );
-      if( !s ) throw TestDBusWrapper::error{};
-      Encoder<A>::decode(w, tgt.first, s);
-      Encoder<B>::decode(w, tgt.second, s);
-    }
+};
+template<>
+struct DBusSigImpl<bool>
+{
+  enum
+  {
+    value = 'b',
+    end   = 0
   };
-  template <typename T> struct Encoder<TestDBusWrapper::Variant<T>> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const TestDBusWrapper::Variant<T> &src) {
-      //w.eldbus_message_iter_arguments_append_impl(tgt, src);
-      auto var = w.eldbus_message_iter_container_new_impl( tgt, 'v', "");
-      Encoder<T>::encode(w, var, src.value);
-    }
-    static void decode(TestDBusWrapper &w, TestDBusWrapper::Variant<T> &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'v' );
-      if( !s ) throw TestDBusWrapper::error{};
-      Encoder<T>::decode(w, tgt.value, s);
-    }
+};
+template<>
+struct DBusSigImpl<std::string>
+{
+  enum
+  {
+    value = 's',
+    end   = 0
   };
-  template <typename T> struct Encoder<std::vector<T>> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::vector<T> &src) {
-      auto var = w.eldbus_message_iter_container_new_impl( tgt, 'a', "");
-      for(auto &q : src)
-        Encoder<T>::encode(w, var, q);
-    }
-    static void decode(TestDBusWrapper &w, std::vector<T> &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'a' );
-      if( !s ) throw TestDBusWrapper::error{};
-      while(!TestDBusWrapper::completed(s)) {
-        tgt.push_back({});
-        Encoder<T>::decode(w, tgt.back(), s);
-      }
-    }
+};
+template<>
+struct DBusSigImpl<ObjectPath>
+{
+  enum
+  {
+    value = 'o',
+    end   = 0
   };
-  template <typename K, typename V> struct Encoder<std::unordered_map<K,V>> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::unordered_map<K,V> &src) {
-      auto var = w.eldbus_message_iter_container_new_impl( tgt, 'a', "");
-      for(auto &q : src){
-        Encoder<typename std::unordered_map<K,V>::value_type>::encode(w, var, q, 'e');
-      }
-    }
-    static void decode(TestDBusWrapper &w, std::unordered_map<K,V> &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'a' );
-      if( !s ) throw TestDBusWrapper::error{};
-      while(!TestDBusWrapper::completed(s)) {
-        std::pair<K, V> tmp;
-        Encoder<std::pair<K,V>>::decode(w, tmp, s, 'e');
-        tgt.insert(std::move(tmp));
-      }
-    }
+};
+template<>
+struct DBusSigImpl<TestDBusWrapper::ElementList>
+{
+  enum
+  {
+    value = '(',
+    end   = ')'
   };
-  template <typename T, size_t I> struct Encoder<std::array<T, I>> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::array<T, I> &src) {
-      auto var = w.eldbus_message_iter_container_new_impl( tgt, 'a', "");
-      for(auto &q : src)
-        Encoder<T>::encode(w, var, q);
-    }
-    static void decode(TestDBusWrapper &w, std::array<T, I> &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'a' );
-      if( !s ) throw TestDBusWrapper::error{};
-      size_t i = 0;
-      while(!TestDBusWrapper::completed(s)) {
-        if(i >= tgt.size()) throw TestDBusWrapper::error{};
-        Encoder<T>::decode(w, tgt[i], s);
-        ++i;
-      }
-      if( i!=tgt.size()) throw TestDBusWrapper::error{};
-    }
+};
+template<>
+struct DBusSigImpl<TestDBusWrapper::ElementMap>
+{
+  enum
+  {
+    value = '{',
+    end   = '}'
   };
-  template <> struct Encoder<Dali::Accessibility::Address> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const Dali::Accessibility::Address &src) {
-      Encoder<std::tuple<std::string, ObjectPath>>::encode(w, tgt,std::tuple<std::string, ObjectPath> {
-        src.GetBus(), ObjectPath{ "/org/a11y/atspi/accessible/" + src.GetPath() } }
-      );
+};
+
+template<typename T>
+struct Encoder<T, decltype(TestDBusWrapper().eldbus_message_iter_arguments_append_impl(TestDBusWrapper::MessageIterPtr(), T()))>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const T& src)
+  {
+    w.eldbus_message_iter_arguments_append_impl(tgt, src);
+  }
+  static void decode(TestDBusWrapper& w, T& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    if(!w.eldbus_message_iter_get_and_next_impl(src, tgt)) throw TestDBusWrapper::error{};
+  }
+};
+template<typename T>
+struct Encoder<T, typename std::enable_if<std::is_enum<T>::value>::type>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const T& src)
+  {
+    Encoder<typename std::underlying_type<T>::type>::encode(w, tgt, static_cast<typename std::underlying_type<T>::type>(src));
+  }
+  static void decode(TestDBusWrapper& w, T& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    typename std::underlying_type<T>::type v = 0;
+    Encoder<typename std::underlying_type<T>::type>::decode(w, v, src);
+    tgt = static_cast<T>(v);
+  }
+};
+template<typename T>
+struct Encoder<const T>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const T& src)
+  {
+    Encoder<T>::encode(w, tgt, src);
+  }
+  static void decode(TestDBusWrapper& w, T& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    Encoder<T>::decode(w, tgt, src);
+  }
+};
+template<typename T>
+struct Encoder<const T&>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const T& src)
+  {
+    Encoder<T>::encode(w, tgt, src);
+  }
+  static void decode(TestDBusWrapper& w, T& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    Encoder<T>::decode(w, tgt, src);
+  }
+};
+template<typename T>
+struct Encoder<T&>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const T& src)
+  {
+    Encoder<T>::encode(w, tgt, src);
+  }
+  static void decode(TestDBusWrapper& w, T& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    Encoder<T>::decode(w, tgt, src);
+  }
+};
+template<size_t I, size_t S, typename... ARGS>
+struct EncoderTuple
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::tuple<ARGS...>& src)
+  {
+    Encoder<typename std::tuple_element<I, std::tuple<ARGS...>>::type>::encode(w, tgt, std::get<I>(src));
+    EncoderTuple<I + 1, S, ARGS...>::encode(w, tgt, src);
+  }
+  static void decode(TestDBusWrapper& w, std::tuple<ARGS...>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    Encoder<typename std::tuple_element<I, std::tuple<ARGS...>>::type>::decode(w, std::get<I>(tgt), src);
+    EncoderTuple<I + 1, S, ARGS...>::decode(w, tgt, src);
+  }
+};
+template<size_t S, typename... ARGS>
+struct EncoderTuple<S, S, ARGS...>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::tuple<ARGS...>& src)
+  {
+  }
+  static void decode(TestDBusWrapper& w, std::tuple<ARGS...>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+  }
+};
+template<typename... ARGS>
+struct Encoder<std::tuple<ARGS...>>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::tuple<ARGS...>& src)
+  {
+    auto var = w.eldbus_message_iter_container_new_impl(tgt, 'r', "");
+    EncoderTuple<0, sizeof...(ARGS), ARGS...>::encode(w, var, src);
+  }
+  static void decode(TestDBusWrapper& w, std::tuple<ARGS...>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    auto s = w.eldbus_message_iter_get_and_next_by_type_impl(src, 'r');
+    if(!s) throw TestDBusWrapper::error{};
+    EncoderTuple<0, sizeof...(ARGS), ARGS...>::decode(w, tgt, s);
+  }
+};
+template<typename A, typename B>
+struct Encoder<std::pair<A, B>>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::pair<A, B>& src, char type = 'r')
+  {
+    auto var = w.eldbus_message_iter_container_new_impl(tgt, type, "");
+    Encoder<A>::encode(w, var, src.first);
+    Encoder<B>::encode(w, var, src.second);
+  }
+  static void decode(TestDBusWrapper& w, std::pair<A, B>& tgt, const TestDBusWrapper::MessageIterPtr& src, char type = 'r')
+  {
+    auto s = w.eldbus_message_iter_get_and_next_by_type_impl(src, type);
+    if(!s) throw TestDBusWrapper::error{};
+    Encoder<A>::decode(w, tgt.first, s);
+    Encoder<B>::decode(w, tgt.second, s);
+  }
+};
+template<typename T>
+struct Encoder<TestDBusWrapper::Variant<T>>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const TestDBusWrapper::Variant<T>& src)
+  {
+    //w.eldbus_message_iter_arguments_append_impl(tgt, src);
+    auto var = w.eldbus_message_iter_container_new_impl(tgt, 'v', "");
+    Encoder<T>::encode(w, var, src.value);
+  }
+  static void decode(TestDBusWrapper& w, TestDBusWrapper::Variant<T>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    auto s = w.eldbus_message_iter_get_and_next_by_type_impl(src, 'v');
+    if(!s) throw TestDBusWrapper::error{};
+    Encoder<T>::decode(w, tgt.value, s);
+  }
+};
+template<typename T>
+struct Encoder<std::vector<T>>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::vector<T>& src)
+  {
+    auto var = w.eldbus_message_iter_container_new_impl(tgt, 'a', "");
+    for(auto& q : src)
+      Encoder<T>::encode(w, var, q);
+  }
+  static void decode(TestDBusWrapper& w, std::vector<T>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    auto s = w.eldbus_message_iter_get_and_next_by_type_impl(src, 'a');
+    if(!s) throw TestDBusWrapper::error{};
+    while(!TestDBusWrapper::completed(s))
+    {
+      tgt.push_back({});
+      Encoder<T>::decode(w, tgt.back(), s);
     }
-    static void decode(TestDBusWrapper &w, Dali::Accessibility::Address &tgt, const TestDBusWrapper::MessageIterPtr &src) {
-      std::tuple<std::string, ObjectPath> tmp;
-      Encoder<std::tuple<std::string, ObjectPath>>::decode(w, tmp, src);
-      static const size_t prefixSize = std::string{ "/org/a11y/atspi/accessible/" }.size();
-      tgt = { std::move(std::get<0>(tmp)), std::get<1>(tmp).value.substr(prefixSize) };
+  }
+};
+template<typename K, typename V>
+struct Encoder<std::unordered_map<K, V>>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::unordered_map<K, V>& src)
+  {
+    auto var = w.eldbus_message_iter_container_new_impl(tgt, 'a', "");
+    for(auto& q : src)
+    {
+      Encoder<typename std::unordered_map<K, V>::value_type>::encode(w, var, q, 'e');
     }
-  };
-  template <> struct Encoder<const char*> {
-    static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const char *src) {
-      Encoder<std::string>::encode(w, tgt, src);
+  }
+  static void decode(TestDBusWrapper& w, std::unordered_map<K, V>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    auto s = w.eldbus_message_iter_get_and_next_by_type_impl(src, 'a');
+    if(!s) throw TestDBusWrapper::error{};
+    while(!TestDBusWrapper::completed(s))
+    {
+      std::pair<K, V> tmp;
+      Encoder<std::pair<K, V>>::decode(w, tmp, s, 'e');
+      tgt.insert(std::move(tmp));
     }
-  };
-}
+  }
+};
+template<typename T, size_t I>
+struct Encoder<std::array<T, I>>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const std::array<T, I>& src)
+  {
+    auto var = w.eldbus_message_iter_container_new_impl(tgt, 'a', "");
+    for(auto& q : src)
+      Encoder<T>::encode(w, var, q);
+  }
+  static void decode(TestDBusWrapper& w, std::array<T, I>& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    auto s = w.eldbus_message_iter_get_and_next_by_type_impl(src, 'a');
+    if(!s) throw TestDBusWrapper::error{};
+    size_t i = 0;
+    while(!TestDBusWrapper::completed(s))
+    {
+      if(i >= tgt.size()) throw TestDBusWrapper::error{};
+      Encoder<T>::decode(w, tgt[i], s);
+      ++i;
+    }
+    if(i != tgt.size()) throw TestDBusWrapper::error{};
+  }
+};
+template<>
+struct Encoder<Dali::Accessibility::Address>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const Dali::Accessibility::Address& src)
+  {
+    Encoder<std::tuple<std::string, ObjectPath>>::encode(w, tgt, std::tuple<std::string, ObjectPath>{src.GetBus(), ObjectPath{"/org/a11y/atspi/accessible/" + src.GetPath()}});
+  }
+  static void decode(TestDBusWrapper& w, Dali::Accessibility::Address& tgt, const TestDBusWrapper::MessageIterPtr& src)
+  {
+    std::tuple<std::string, ObjectPath> tmp;
+    Encoder<std::tuple<std::string, ObjectPath>>::decode(w, tmp, src);
+    static const size_t prefixSize = std::string{"/org/a11y/atspi/accessible/"}.size();
+    tgt                            = {std::move(std::get<0>(tmp)), std::get<1>(tmp).value.substr(prefixSize)};
+  }
+};
+template<>
+struct Encoder<const char*>
+{
+  static void encode(TestDBusWrapper& w, const TestDBusWrapper::MessageIterPtr& tgt, const char* src)
+  {
+    Encoder<std::string>::encode(w, tgt, src);
+  }
+};
+} // namespace detail
 /// \endcond
 
-#define DBUS_DEBUG( ... )                                \
-  do                                                     \
-  {                                                      \
-    DBus::debugPrint( __FILE__, __LINE__, __VA_ARGS__ ); \
-  } while( 0 )
+#define DBUS_DEBUG(...)                                \
+  do                                                   \
+  {                                                    \
+    DBus::debugPrint(__FILE__, __LINE__, __VA_ARGS__); \
+  } while(0)
 
 #define DBUS_W DBusWrapper::Installed()
 
@@ -681,7 +978,7 @@ class DBusInterfaceDescription;
 /**
  * @brief Formats debug message and calls debug printer (if any) with it
  */
-void debugPrint( const char* file, size_t line, const char* format, ... );
+void debugPrint(const char* file, size_t line, const char* format, ...);
 
 /**
  * @brief Sets debug printer callback, which will be called with debug messages
@@ -690,16 +987,17 @@ void debugPrint( const char* file, size_t line, const char* format, ... );
  * is pointer to text, second it's length. Text is ended with 0 (not counted towards it's size),
  * user can safely printf it.
  */
-void setDebugPrinter( std::function< void( const char*, size_t ) > );
+void setDebugPrinter(std::function<void(const char*, size_t)>);
 
 struct Error
 {
   std::string message;
 
   Error() = default;
-  Error( std::string msg ) : message( std::move( msg ) )
+  Error(std::string msg)
+  : message(std::move(msg))
   {
-    bart_assert( !message.empty() );
+    bart_assert(!message.empty());
   }
 };
 
@@ -722,7 +1020,7 @@ struct Success
  * Both mean the same - ValueOrError containing no real data and being a marker,
  * wherever operation successed or failed and containing possible error message.
  */
-template < typename... ARGS >
+template<typename... ARGS>
 class ValueOrError
 {
 public:
@@ -736,21 +1034,28 @@ public:
    *
    * This will be initialized as success with passed in values.
    */
-  ValueOrError( ARGS... t ) : value( std::move( t )... ) {}
+  ValueOrError(ARGS... t)
+  : value(std::move(t)...)
+  {
+  }
 
   /**
    * @brief Alternative Value constructor.
    *
    * This will be initialized as success with passed in values.
    */
-  ValueOrError( std::tuple< ARGS... > t ) : value( std::move( t ) ) {}
+  ValueOrError(std::tuple<ARGS...> t)
+  : value(std::move(t))
+  {
+  }
 
   /**
    * @brief Error constructor. This will be initialized as failure with given error message.
    */
-  ValueOrError( Error e ) : error( std::move( e ) )
+  ValueOrError(Error e)
+  : error(std::move(e))
   {
-    bart_assert( !error.message.empty() );
+    bart_assert(!error.message.empty());
   }
 
   /**
@@ -782,9 +1087,9 @@ public:
    * User can modify (or move) data safely.
    * Only callable, if operation actually successed, otherwise will bart_assert.
    */
-  std::tuple< ARGS... >& getValues()
+  std::tuple<ARGS...>& getValues()
   {
-    bart_assert( *this );
+    bart_assert(*this);
     return value;
   }
 
@@ -794,29 +1099,32 @@ public:
    * Returns const reference to the internal tuple containing held data.
    * Only callable, if operation actually successed, otherwise will bart_assert.
    */
-  const std::tuple< ARGS... >& getValues() const
+  const std::tuple<ARGS...>& getValues() const
   {
-    bart_assert( *this );
+    bart_assert(*this);
     return value;
   }
 
 protected:
   /// \cond
-  std::tuple< ARGS... > value;
-  Error error;
+  std::tuple<ARGS...> value;
+  Error               error;
   /// \endcond
 };
 
 /// \cond
-template <>
+template<>
 class ValueOrError<>
 {
 public:
   ValueOrError() = default;
-  ValueOrError( std::tuple<> t ) {}
-  ValueOrError( Error e ) : error( std::move( e ) )
+  ValueOrError(std::tuple<> t)
+  {
+  }
+  ValueOrError(Error e)
+  : error(std::move(e))
   {
-    bart_assert( !error.message.empty() );
+    bart_assert(!error.message.empty());
   }
 
   explicit operator bool() const
@@ -829,13 +1137,13 @@ public:
   }
   std::tuple<>& getValues()
   {
-    bart_assert( *this );
+    bart_assert(*this);
     static std::tuple<> t;
     return t;
   }
   std::tuple<> getValues() const
   {
-    bart_assert( *this );
+    bart_assert(*this);
     return {};
   }
 
@@ -843,15 +1151,18 @@ protected:
   Error error;
 };
 
-template <>
-class ValueOrError< void >
+template<>
+class ValueOrError<void>
 {
 public:
   ValueOrError() = default;
-  ValueOrError( Success ) {}
-  ValueOrError( Error e ) : error( std::move( e ) )
+  ValueOrError(Success)
+  {
+  }
+  ValueOrError(Error e)
+  : error(std::move(e))
   {
-    bart_assert( !error.message.empty() );
+    bart_assert(!error.message.empty());
   }
 
   explicit operator bool() const
@@ -864,13 +1175,13 @@ public:
   }
   std::tuple<>& getValues()
   {
-    bart_assert( *this );
+    bart_assert(*this);
     static std::tuple<> t;
     return t;
   }
   std::tuple<> getValues() const
   {
-    bart_assert( *this );
+    bart_assert(*this);
     return {};
   }
 
@@ -891,7 +1202,7 @@ using ObjectPath = ObjectPath;
  * as return data in variant. So for example user can't specify method call, which on return
  * expects DBUS variant holding either string or int.
  */
-template < typename A >
+template<typename A>
 struct EldbusVariant
 {
   A value;
@@ -903,27 +1214,27 @@ struct EldbusVariant
 namespace detail
 {
 /// \cond
-template < typename T, typename = void >
+template<typename T, typename = void>
 struct signature;
-template < typename... ARGS >
-struct signature< std::tuple< ARGS... > >;
-template < typename A, typename B >
-struct signature< std::pair< A, B > >;
-template < typename A >
-struct signature< std::vector< A > >;
-template < typename A, size_t N >
-struct signature< std::array< A, N > >;
-template < typename A, typename B >
-struct signature< std::unordered_map< A, B > >;
-template < typename A, typename B >
-struct signature< std::map< A, B > >;
+template<typename... ARGS>
+struct signature<std::tuple<ARGS...>>;
+template<typename A, typename B>
+struct signature<std::pair<A, B>>;
+template<typename A>
+struct signature<std::vector<A>>;
+template<typename A, size_t N>
+struct signature<std::array<A, N>>;
+template<typename A, typename B>
+struct signature<std::unordered_map<A, B>>;
+template<typename A, typename B>
+struct signature<std::map<A, B>>;
 /// \endcond
 
 /**
  * @brief Signature class for marshalling uint8 type.
  */
-template <>
-struct signature< uint8_t >
+template<>
+struct signature<uint8_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -944,7 +1255,7 @@ struct signature< uint8_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, uint8_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, uint8_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -952,7 +1263,7 @@ struct signature< uint8_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, uint8_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, uint8_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -961,8 +1272,8 @@ struct signature< uint8_t >
 /**
  * @brief Signature class for marshalling uint16 type.
  */
-template <>
-struct signature< uint16_t >
+template<>
+struct signature<uint16_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -983,7 +1294,7 @@ struct signature< uint16_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, uint16_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, uint16_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -991,7 +1302,7 @@ struct signature< uint16_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, uint16_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, uint16_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1000,8 +1311,8 @@ struct signature< uint16_t >
 /**
  * @brief Signature class for marshalling uint32 type.
  */
-template <>
-struct signature< uint32_t >
+template<>
+struct signature<uint32_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1022,7 +1333,7 @@ struct signature< uint32_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, uint32_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, uint32_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1030,7 +1341,7 @@ struct signature< uint32_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, uint32_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, uint32_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1039,8 +1350,8 @@ struct signature< uint32_t >
 /**
  * @brief Signature class for marshalling uint64 type.
  */
-template <>
-struct signature< uint64_t >
+template<>
+struct signature<uint64_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1061,7 +1372,7 @@ struct signature< uint64_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, uint64_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, uint64_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1069,7 +1380,7 @@ struct signature< uint64_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, uint64_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, uint64_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1078,8 +1389,8 @@ struct signature< uint64_t >
 /**
  * @brief Signature class for marshalling int16 type.
  */
-template <>
-struct signature< int16_t >
+template<>
+struct signature<int16_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1100,7 +1411,7 @@ struct signature< int16_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, int16_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, int16_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1108,7 +1419,7 @@ struct signature< int16_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, int16_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, int16_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1117,8 +1428,8 @@ struct signature< int16_t >
 /**
  * @brief Signature class for marshalling int32 type.
  */
-template <>
-struct signature< int32_t >
+template<>
+struct signature<int32_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1139,7 +1450,7 @@ struct signature< int32_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, int32_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, int32_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1147,7 +1458,7 @@ struct signature< int32_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, int32_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, int32_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1156,8 +1467,8 @@ struct signature< int32_t >
 /**
  * @brief Signature class for marshalling int64 type.
  */
-template <>
-struct signature< int64_t >
+template<>
+struct signature<int64_t>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1178,7 +1489,7 @@ struct signature< int64_t >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, int64_t v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, int64_t v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1186,7 +1497,7 @@ struct signature< int64_t >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, int64_t& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, int64_t& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1195,8 +1506,8 @@ struct signature< int64_t >
 /**
  * @brief Signature class for marshalling double type.
  */
-template <>
-struct signature< double >
+template<>
+struct signature<double>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1217,7 +1528,7 @@ struct signature< double >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, double v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, double v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1225,7 +1536,7 @@ struct signature< double >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, double& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, double& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1233,11 +1544,11 @@ struct signature< double >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, float& v2 )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, float& v2)
   {
     double v = 0;
-    auto r = DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
-    v2 = static_cast< float >( v );
+    auto   r = DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
+    v2       = static_cast<float>(v);
     return r;
   }
 };
@@ -1245,8 +1556,8 @@ struct signature< double >
 /**
  * @brief Signature class for marshalling float type.
  */
-template <>
-struct signature< float >
+template<>
+struct signature<float>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1267,7 +1578,7 @@ struct signature< float >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, float v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, float v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1275,7 +1586,7 @@ struct signature< float >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, double& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, double& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1283,11 +1594,11 @@ struct signature< float >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, float& v2 )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, float& v2)
   {
     double v = 0;
-    auto r = DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
-    v2 = static_cast< float >( v );
+    auto   r = DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
+    v2       = static_cast<float>(v);
     return r;
   }
 };
@@ -1295,8 +1606,8 @@ struct signature< float >
 /**
  * @brief Signature class for marshalling boolean type.
  */
-template <>
-struct signature< bool >
+template<>
+struct signature<bool>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1317,7 +1628,7 @@ struct signature< bool >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, bool v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, bool v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1325,14 +1636,14 @@ struct signature< bool >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, bool& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, bool& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
 };
 
-template < typename T >
-struct signature< T, typename std::enable_if< std::is_enum< T >::value, void >::type >
+template<typename T>
+struct signature<T, typename std::enable_if<std::is_enum<T>::value, void>::type>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1353,21 +1664,21 @@ struct signature< T, typename std::enable_if< std::is_enum< T >::value, void >::
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, T v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, T v)
   {
-    signature<typename std::underlying_type<T>::type>::set(iter, static_cast< int64_t >( v ));
+    signature<typename std::underlying_type<T>::type>::set(iter, static_cast<int64_t>(v));
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, T& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, T& v)
   {
     typename std::underlying_type<T>::type q = 0;
-    if (!signature<typename std::underlying_type<T>::type>::get(iter, q))
+    if(!signature<typename std::underlying_type<T>::type>::get(iter, q))
       return false;
 
-    v = static_cast< T >( q );
+    v = static_cast<T>(q);
     return true;
   }
 };
@@ -1378,8 +1689,8 @@ struct signature< T, typename std::enable_if< std::is_enum< T >::value, void >::
  * Both (const) char * and std::string types are accepted as value to send.
  * Only std::string is accepted as value to receive.
  */
-template <>
-struct signature< std::string >
+template<>
+struct signature<std::string>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1400,30 +1711,30 @@ struct signature< std::string >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::string& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::string& v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
 
-// /**
-//  * @brief Marshals value v as marshalled type into message
-//  */
-//  static void set( const DBusWrapper::MessageIterPtr &iter, const char* v )
-//  {
-//    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
-//  }
+  // /**
+  //  * @brief Marshals value v as marshalled type into message
+  //  */
+  //  static void set( const DBusWrapper::MessageIterPtr &iter, const char* v )
+  //  {
+  //    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
+  //  }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::string& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::string& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
 };
 
-template <>
-struct signature< ObjectPath >
+template<>
+struct signature<ObjectPath>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1444,15 +1755,15 @@ struct signature< ObjectPath >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::string& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::string& v)
   {
-    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, ObjectPath{ v });
+    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, ObjectPath{v});
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const ObjectPath& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const ObjectPath& v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1460,15 +1771,15 @@ struct signature< ObjectPath >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const char* v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const char* v)
   {
-    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, ObjectPath{ v });
+    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, ObjectPath{v});
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, ObjectPath& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, ObjectPath& v)
   {
     return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v);
   }
@@ -1476,10 +1787,10 @@ struct signature< ObjectPath >
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::string& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::string& v)
   {
     ObjectPath q;
-    if (!DBUS_W->eldbus_message_iter_get_and_next_impl(iter, q)) return false;
+    if(!DBUS_W->eldbus_message_iter_get_and_next_impl(iter, q)) return false;
     v = std::move(q.value);
     return true;
   }
@@ -1491,8 +1802,8 @@ struct signature< ObjectPath >
  * Both (const) char * and std::string types are accepted as value to send.
  * You can't use (const) char * variable type to receive value.
  */
-template <>
-struct signature< char* >
+template<>
+struct signature<char*>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1513,7 +1824,7 @@ struct signature< char* >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::string& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::string& v)
   {
     DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v);
   }
@@ -1521,21 +1832,21 @@ struct signature< char* >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const char* v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const char* v)
   {
-    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, std::string{ v });
+    DBUS_W->eldbus_message_iter_arguments_append_impl(iter, std::string{v});
   }
 };
 /// \cond
 
-template < size_t INDEX, typename A, typename... ARGS >
+template<size_t INDEX, typename A, typename... ARGS>
 struct signature_tuple_element_type_helper
 {
-  using type = typename signature_tuple_element_type_helper< INDEX - 1, ARGS... >::type;
+  using type = typename signature_tuple_element_type_helper<INDEX - 1, ARGS...>::type;
 };
 
-template < typename A, typename... ARGS >
-struct signature_tuple_element_type_helper< 0, A, ARGS... >
+template<typename A, typename... ARGS>
+struct signature_tuple_element_type_helper<0, A, ARGS...>
 {
   using type = A;
 };
@@ -1548,19 +1859,19 @@ struct signature_tuple_element_type_helper< 0, A, ARGS... >
  * and incrementing. This class recursively calls itself with increasing INDEX value
  * until INDEX is equal to SIZE, where recursive calling ends.
  */
-template < size_t INDEX, size_t SIZE, typename... ARGS >
+template<size_t INDEX, size_t SIZE, typename... ARGS>
 struct signature_tuple_helper
 {
-  using current_type = typename signature_tuple_element_type_helper< INDEX, ARGS... >::type;
+  using current_type = typename signature_tuple_element_type_helper<INDEX, ARGS...>::type;
 
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    if( INDEX + 1 >= SIZE )
-      return signature< current_type >::name();
-    return signature< current_type >::name() + ", " + signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::name();
+    if(INDEX + 1 >= SIZE)
+      return signature<current_type>::name();
+    return signature<current_type>::name() + ", " + signature_tuple_helper<INDEX + 1, SIZE, ARGS...>::name();
   }
 
   /**
@@ -1568,25 +1879,25 @@ struct signature_tuple_helper
    */
   static std::string sig()
   {
-    return signature< current_type >::sig() + signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::sig();
+    return signature<current_type>::sig() + signature_tuple_helper<INDEX + 1, SIZE, ARGS...>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::tuple< ARGS... >& args )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::tuple<ARGS...>& args)
   {
-    signature< current_type >::set( iter, std::get< INDEX >( args ) );
-    signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::set( iter, args );
+    signature<current_type>::set(iter, std::get<INDEX>(args));
+    signature_tuple_helper<INDEX + 1, SIZE, ARGS...>::set(iter, args);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::tuple< ARGS... >& args )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::tuple<ARGS...>& args)
   {
-    return signature< current_type >::get( iter, std::get< INDEX >( args ) ) &&
-           signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::get( iter, args );
+    return signature<current_type>::get(iter, std::get<INDEX>(args)) &&
+           signature_tuple_helper<INDEX + 1, SIZE, ARGS...>::get(iter, args);
   }
 };
 
@@ -1596,8 +1907,8 @@ struct signature_tuple_helper
  * This class marks end of the tuple marshalling. Members of this class are called
  * when INDEX value is equal to SIZE.
  */
-template < size_t SIZE, typename... ARGS >
-struct signature_tuple_helper< SIZE, SIZE, ARGS... >
+template<size_t SIZE, typename... ARGS>
+struct signature_tuple_helper<SIZE, SIZE, ARGS...>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1618,14 +1929,14 @@ struct signature_tuple_helper< SIZE, SIZE, ARGS... >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::tuple< ARGS... >& args )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::tuple<ARGS...>& args)
   {
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::tuple< ARGS... >& args )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::tuple<ARGS...>& args)
   {
     return true;
   }
@@ -1637,15 +1948,15 @@ struct signature_tuple_helper< SIZE, SIZE, ARGS... >
  * This class marshalls tuple of values. This represents
  * DBUS struct typle, encoded with character 'r'
  */
-template < typename... ARGS >
-struct signature< std::tuple< ARGS... > >
+template<typename... ARGS>
+struct signature<std::tuple<ARGS...>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "tuple<" + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::name() + ">";
+    return "tuple<" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name() + ">";
   }
 
   /**
@@ -1653,26 +1964,26 @@ struct signature< std::tuple< ARGS... > >
    */
   static std::string sig()
   {
-    return "(" + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::sig() + ")";
+    return "(" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig() + ")";
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::tuple< ARGS... >& args )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::tuple<ARGS...>& args)
   {
-    auto entry = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'r', "");
-    signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::set( entry, args );
+    auto entry = DBUS_W->eldbus_message_iter_container_new_impl(iter, 'r', "");
+    signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(entry, args);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::tuple< ARGS... >& args )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::tuple<ARGS...>& args)
   {
-    auto entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'r' );
-    if (!entry) return false;
-    return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::get( entry, args );
+    auto entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'r');
+    if(!entry) return false;
+    return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(entry, args);
   }
 };
 
@@ -1687,15 +1998,15 @@ struct signature< std::tuple< ARGS... > >
  * or
  * \code{.cpp} ValueOrError<std::tuple<std::string, std::string, std::tuple<std::string>>> \endcode
  */
-template < typename... ARGS >
-struct signature< ValueOrError< ARGS... > >
+template<typename... ARGS>
+struct signature<ValueOrError<ARGS...>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "ValueOrError<" + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::name() + ">";
+    return "ValueOrError<" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name() + ">";
   }
 
   /**
@@ -1703,31 +2014,31 @@ struct signature< ValueOrError< ARGS... > >
    */
   static std::string sig()
   {
-    return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::sig();
+    return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const ValueOrError< ARGS... >& args )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const ValueOrError<ARGS...>& args)
   {
-    signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::set( iter, args.getValues() );
+    signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(iter, args.getValues());
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, ValueOrError< ARGS... >& args )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, ValueOrError<ARGS...>& args)
   {
-    return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::get( iter, args.getValues() );
+    return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(iter, args.getValues());
   }
 };
 
 /**
  * @brief Signature class for marshalling ValueOrError<void> type
  */
-template <>
-struct signature< ValueOrError< void > >
+template<>
+struct signature<ValueOrError<void>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1748,14 +2059,14 @@ struct signature< ValueOrError< void > >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const ValueOrError< void >& args )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const ValueOrError<void>& args)
   {
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, ValueOrError< void >& args )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, ValueOrError<void>& args)
   {
     return true;
   }
@@ -1764,8 +2075,8 @@ struct signature< ValueOrError< void > >
 /**
  * @brief Signature class for marshalling ValueOrError<> type
  */
-template <>
-struct signature< ValueOrError<> >
+template<>
+struct signature<ValueOrError<>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
@@ -1786,14 +2097,14 @@ struct signature< ValueOrError<> >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const ValueOrError<>& args )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const ValueOrError<>& args)
   {
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, ValueOrError<>& args )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, ValueOrError<>& args)
   {
     return true;
   }
@@ -1802,15 +2113,15 @@ struct signature< ValueOrError<> >
 /**
  * @brief Signature class for marshalling pair of types
  */
-template < typename A, typename B >
-struct signature< std::pair< A, B > >
+template<typename A, typename B>
+struct signature<std::pair<A, B>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "pair<" + signature_tuple_helper< 0, 2, A, B >::name() + ">";
+    return "pair<" + signature_tuple_helper<0, 2, A, B>::name() + ">";
   }
 
   /**
@@ -1818,35 +2129,36 @@ struct signature< std::pair< A, B > >
    */
   static std::string sig()
   {
-    return "(" + signature_tuple_helper< 0, 2, A, B >::sig() + ")";
+    return "(" + signature_tuple_helper<0, 2, A, B>::sig() + ")";
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::pair< A, B >& ab, bool dictionary = false )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::pair<A, B>& ab, bool dictionary = false)
   {
-    auto entry = DBUS_W->eldbus_message_iter_container_new_impl( iter, dictionary ? 'e' : 'r', "");
-    signature_tuple_helper< 0, 2, A, B >::set( entry, ab );
+    auto entry = DBUS_W->eldbus_message_iter_container_new_impl(iter, dictionary ? 'e' : 'r', "");
+    signature_tuple_helper<0, 2, A, B>::set(entry, ab);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::pair< A, B >& ab )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::pair<A, B>& ab)
   {
-    auto entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'r' );
-    if (!entry) {
-      entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, '{' );
-      if (!entry) return false;
+    auto entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'r');
+    if(!entry)
+    {
+      entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, '{');
+      if(!entry) return false;
     }
 
-    std::tuple< A, B > ab_tmp;
-    auto z = signature_tuple_helper< 0, 2, A, B >::get( entry, ab_tmp );
-    if( z )
+    std::tuple<A, B> ab_tmp;
+    auto             z = signature_tuple_helper<0, 2, A, B>::get(entry, ab_tmp);
+    if(z)
     {
-      ab.first = std::move( std::get< 0 >( ab_tmp ) );
-      ab.second = std::move( std::get< 1 >( ab_tmp ) );
+      ab.first  = std::move(std::get<0>(ab_tmp));
+      ab.second = std::move(std::get<1>(ab_tmp));
     }
     return z;
   }
@@ -1857,15 +2169,15 @@ struct signature< std::pair< A, B > >
  *
  * This marshals container's content as DBUS a ascii character type code.
  */
-template < typename A >
-struct signature< std::vector< A > >
+template<typename A>
+struct signature<std::vector<A>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "vector<" + signature< A >::name() + ">";
+    return "vector<" + signature<A>::name() + ">";
   }
 
   /**
@@ -1873,33 +2185,33 @@ struct signature< std::vector< A > >
    */
   static std::string sig()
   {
-    return "a" + signature< A >::sig();
+    return "a" + signature<A>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::vector< A >& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::vector<A>& v)
   {
-    auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', signature< A >::sig());
-    bart_assert( lst );
-    for( auto& a : v )
+    auto lst = DBUS_W->eldbus_message_iter_container_new_impl(iter, 'a', signature<A>::sig());
+    bart_assert(lst);
+    for(auto& a : v)
     {
-      signature< A >::set( lst, a );
+      signature<A>::set(lst, a);
     }
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::vector< A >& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::vector<A>& v)
   {
-    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' );
-    if (!s) return false;
+    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'a');
+    if(!s) return false;
     v.clear();
     A a;
-    while( signature< A >::get( s, a ) )
-      v.push_back( std::move( a ) );
+    while(signature<A>::get(s, a))
+      v.push_back(std::move(a));
 
     return true;
   }
@@ -1910,15 +2222,15 @@ struct signature< std::vector< A > >
  *
  * This marshals container's content as DBUS a ascii character type code.
  */
-template < typename A, size_t N >
-struct signature< std::array< A, N > >
+template<typename A, size_t N>
+struct signature<std::array<A, N>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "array<" + signature< A >::name() + ", " + std::to_string( N ) + ">";
+    return "array<" + signature<A>::name() + ", " + std::to_string(N) + ">";
   }
 
   /**
@@ -1926,33 +2238,33 @@ struct signature< std::array< A, N > >
    */
   static std::string sig()
   {
-    return "a" + signature< A >::sig();
+    return "a" + signature<A>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::array< A, N >& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::array<A, N>& v)
   {
-    auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', signature< A >::sig());
-    bart_assert( lst );
-    for( auto& a : v )
+    auto lst = DBUS_W->eldbus_message_iter_container_new_impl(iter, 'a', signature<A>::sig());
+    bart_assert(lst);
+    for(auto& a : v)
     {
-      signature< A >::set( lst, a );
+      signature<A>::set(lst, a);
     }
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::array< A, N >& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::array<A, N>& v)
   {
-    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' );
-    if ( !s )
+    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'a');
+    if(!s)
       return false;
-    for( auto& a : v )
+    for(auto& a : v)
     {
-      if( !signature< A >::get( s, a ) )
+      if(!signature<A>::get(s, a))
         return false;
     }
     return true;
@@ -1964,15 +2276,15 @@ struct signature< std::array< A, N > >
  *
  * This marshals variant's content as DBUS v ascii character type code.
  */
-template < typename A >
-struct signature< EldbusVariant< A > >
+template<typename A>
+struct signature<EldbusVariant<A>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "variant<" + signature< A >::name() + ">";
+    return "variant<" + signature<A>::name() + ">";
   }
 
   /**
@@ -1986,29 +2298,29 @@ struct signature< EldbusVariant< A > >
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const EldbusVariant< A >& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const EldbusVariant<A>& v)
   {
-    set( iter, v.value );
+    set(iter, v.value);
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const A& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const A& v)
   {
-    auto var = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'v', signature< A >::sig());
-    signature< A >::set( var, v );
+    auto var = DBUS_W->eldbus_message_iter_container_new_impl(iter, 'v', signature<A>::sig());
+    signature<A>::set(var, v);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, EldbusVariant< A >& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, EldbusVariant<A>& v)
   {
-    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'v' );
-    if( !s )
+    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'v');
+    if(!s)
       return false;
-    return signature< A >::get( s, v.value );
+    return signature<A>::get(s, v.value);
   }
 };
 
@@ -2022,15 +2334,15 @@ struct signature< EldbusVariant< A > >
  * User can receive such values as std::vector of std::pair<key, value> values.
  * Order of such values is unspecified.
  */
-template < typename A, typename B >
-struct signature< std::unordered_map< A, B > >
+template<typename A, typename B>
+struct signature<std::unordered_map<A, B>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "unordered_map<" + signature< A >::name() + ", " + signature< B >::name() + ">";
+    return "unordered_map<" + signature<A>::name() + ", " + signature<B>::name() + ">";
   }
 
   /**
@@ -2038,35 +2350,35 @@ struct signature< std::unordered_map< A, B > >
    */
   static std::string sig()
   {
-    return "a{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}";
+    return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::unordered_map< A, B >& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::unordered_map<A, B>& v)
   {
-    auto sig = "{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}";
-    auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', sig);
-    bart_assert( lst );
-    for( auto& a : v )
+    auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+    auto lst = DBUS_W->eldbus_message_iter_container_new_impl(iter, 'a', sig);
+    bart_assert(lst);
+    for(auto& a : v)
     {
-      signature< std::pair< A, B > >::set( lst, a, true );
+      signature<std::pair<A, B>>::set(lst, a, true);
     }
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::unordered_map< A, B >& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::unordered_map<A, B>& v)
   {
-    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' );
+    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'a');
     v.clear();
-    if( !s )
+    if(!s)
       return false;
-    std::pair< A, B > a;
-    while( signature< std::pair< A, B > >::get( s, a ) )
-      v.insert( std::move( a ) );
+    std::pair<A, B> a;
+    while(signature<std::pair<A, B>>::get(s, a))
+      v.insert(std::move(a));
     return true;
   }
 };
@@ -2081,15 +2393,15 @@ struct signature< std::unordered_map< A, B > >
  * User can receive such values as std::vector of std::pair<key, value> values.
  * Order of such values is unspecified.
  */
-template < typename A, typename B >
-struct signature< std::map< A, B > >
+template<typename A, typename B>
+struct signature<std::map<A, B>>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "map<" + signature< A >::name() + ", " + signature< B >::name() + ">";
+    return "map<" + signature<A>::name() + ", " + signature<B>::name() + ">";
   }
 
   /**
@@ -2097,34 +2409,34 @@ struct signature< std::map< A, B > >
    */
   static std::string sig()
   {
-    return "a{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}";
+    return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const std::map< A, B >& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const std::map<A, B>& v)
   {
-    auto sig = "{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}";
-    auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', sig);
-    bart_assert( lst );
-    for( auto& a : v )
+    auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+    auto lst = DBUS_W->eldbus_message_iter_container_new_impl(iter, 'a', sig);
+    bart_assert(lst);
+    for(auto& a : v)
     {
-      signature< std::pair< A, B > >::set( lst, a, true );
+      signature<std::pair<A, B>>::set(lst, a, true);
     }
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static bool get( const DBusWrapper::MessageIterPtr &iter, std::map< A, B >& v )
+  static bool get(const DBusWrapper::MessageIterPtr& iter, std::map<A, B>& v)
   {
-    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' );
-    if( !s )
+    auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl(iter, 'a');
+    if(!s)
       return false;
-    std::pair< A, B > a;
-    while( signature< std::pair< A, B > >::get( s, a ) )
-      v.insert( std::move( a ) );
+    std::pair<A, B> a;
+    while(signature<std::pair<A, B>>::get(s, a))
+      v.insert(std::move(a));
     return true;
   }
 };
@@ -2132,15 +2444,15 @@ struct signature< std::map< A, B > >
 /**
  * @brief Signature helper class for marshalling const reference types
  */
-template < typename A >
-struct signature< const A& >
+template<typename A>
+struct signature<const A&>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "const " + signature< A >::name() + "&";
+    return "const " + signature<A>::name() + "&";
   }
 
   /**
@@ -2148,38 +2460,38 @@ struct signature< const A& >
    */
   static std::string sig()
   {
-    return signature< A >::sig();
+    return signature<A>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const A& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const A& v)
   {
-    signature< A >::set( iter, v );
+    signature<A>::set(iter, v);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static void get( const DBusWrapper::MessageIterPtr &iter, A& v )
+  static void get(const DBusWrapper::MessageIterPtr& iter, A& v)
   {
-    signature< A >::get( iter, v );
+    signature<A>::get(iter, v);
   }
 };
 
 /**
  * @brief Signature helper class for marshalling reference types
  */
-template < typename A >
-struct signature< A& >
+template<typename A>
+struct signature<A&>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return signature< A >::name() + "&";
+    return signature<A>::name() + "&";
   }
 
   /**
@@ -2187,38 +2499,38 @@ struct signature< A& >
    */
   static std::string sig()
   {
-    return signature< A >::sig();
+    return signature<A>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const A& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const A& v)
   {
-    signature< A >::set( iter, v );
+    signature<A>::set(iter, v);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static void get( const DBusWrapper::MessageIterPtr &iter, A& v )
+  static void get(const DBusWrapper::MessageIterPtr& iter, A& v)
   {
-    signature< A >::get( iter, v );
+    signature<A>::get(iter, v);
   }
 };
 
 /**
  * @brief Signature helper class for marshalling const types
  */
-template < typename A >
-struct signature< const A >
+template<typename A>
+struct signature<const A>
 {
   /**
    * @brief Returns name of type marshalled, for informative purposes
    */
   static std::string name()
   {
-    return "const " + signature< A >::name();
+    return "const " + signature<A>::name();
   }
 
   /**
@@ -2226,384 +2538,380 @@ struct signature< const A >
    */
   static std::string sig()
   {
-    return signature< A >::sig();
+    return signature<A>::sig();
   }
 
   /**
    * @brief Marshals value v as marshalled type into message
    */
-  static void set( const DBusWrapper::MessageIterPtr &iter, const A& v )
+  static void set(const DBusWrapper::MessageIterPtr& iter, const A& v)
   {
-    signature< A >::set( iter, v );
+    signature<A>::set(iter, v);
   }
 
   /**
    * @brief Marshals value from marshalled type into variable v
    */
-  static void get( const DBusWrapper::MessageIterPtr &iter, A& v )
+  static void get(const DBusWrapper::MessageIterPtr& iter, A& v)
   {
-    signature< A >::get( iter, v );
+    signature<A>::get(iter, v);
   }
 };
 
 /// \cond
 using CallId = DBusWrapper::CallId;
 
-template < typename ValueType >
-ValueType unpackValues( CallId callId, const DBusWrapper::MessagePtr &msg )
+template<typename ValueType>
+ValueType unpackValues(CallId callId, const DBusWrapper::MessagePtr& msg)
 {
-  auto iter = DBUS_W->eldbus_message_iter_get_impl( msg, false );
+  auto      iter = DBUS_W->eldbus_message_iter_get_impl(msg, false);
   ValueType r;
 
-  if( iter )
+  if(iter)
   {
-    if( !signature< ValueType >::get( iter, r ) )
+    if(!signature<ValueType>::get(iter, r))
     {
-      DBUS_DEBUG( "ValueType is %s", signature< ValueType >::name().c_str() );
-      r = Error{"call " + std::to_string( callId.id ) + ": failed to unpack values, got signature '" +
-                DBUS_W->eldbus_message_signature_get_impl( msg ) + "', expected '" + signature< ValueType >::sig() + "'"};
+      DBUS_DEBUG("ValueType is %s", signature<ValueType>::name().c_str());
+      r = Error{"call " + std::to_string(callId.id) + ": failed to unpack values, got signature '" +
+                DBUS_W->eldbus_message_signature_get_impl(msg) + "', expected '" + signature<ValueType>::sig() + "'"};
     }
   }
   else
   {
-    r = Error{"call " + std::to_string( callId.id ) + ": failed to get iterator"};
+    r = Error{"call " + std::to_string(callId.id) + ": failed to get iterator"};
   }
   return r;
 }
 
-inline void packValues_helper( const DBusWrapper::MessageIterPtr& ) {}
+inline void packValues_helper(const DBusWrapper::MessageIterPtr&)
+{
+}
 
-template < typename A, typename... ARGS >
-void packValues_helper( const DBusWrapper::MessageIterPtr &iter, A&& a, ARGS&&... r )
+template<typename A, typename... ARGS>
+void packValues_helper(const DBusWrapper::MessageIterPtr& iter, A&& a, ARGS&&... r)
 {
-  signature< A >::set( iter, std::forward< A >( a ) );
-  packValues_helper( iter, std::forward< ARGS >( r )... );
+  signature<A>::set(iter, std::forward<A>(a));
+  packValues_helper(iter, std::forward<ARGS>(r)...);
 }
 
-template < typename... ARGS >
-void packValues( CallId callId, const DBusWrapper::MessagePtr &msg, ARGS&&... r )
+template<typename... ARGS>
+void packValues(CallId callId, const DBusWrapper::MessagePtr& msg, ARGS&&... r)
 {
-  auto iter = DBUS_W->eldbus_message_iter_get_impl( msg, true );
-  packValues_helper( iter, std::forward< ARGS >( r )... );
+  auto iter = DBUS_W->eldbus_message_iter_get_impl(msg, true);
+  packValues_helper(iter, std::forward<ARGS>(r)...);
 }
 
-template < typename >
+template<typename>
 struct ReturnType;
-template < typename R, typename... ARGS >
-struct ReturnType< R( ARGS... ) >
+template<typename R, typename... ARGS>
+struct ReturnType<R(ARGS...)>
 {
   using type = R;
 };
 
-template < typename R, typename... ARGS >
-struct ReturnType< std::function< R( ARGS... ) > >
+template<typename R, typename... ARGS>
+struct ReturnType<std::function<R(ARGS...)>>
 {
   using type = R;
 };
 
-template < int... >
+template<int...>
 struct sequence
 {
 };
 
-template < int N, int... S >
-struct sequence_gen : sequence_gen< N - 1, N - 1, S... >
+template<int N, int... S>
+struct sequence_gen : sequence_gen<N - 1, N - 1, S...>
 {
 };
 
-template < int... S >
-struct sequence_gen< 0, S... >
+template<int... S>
+struct sequence_gen<0, S...>
 {
-  typedef sequence< S... > type;
+  typedef sequence<S...> type;
 };
 
-template < typename C, typename... ARGS >
+template<typename C, typename... ARGS>
 struct apply_helper
 {
-  const std::function< C >& c;
-  const std::tuple< ARGS... >& args;
+  const std::function<C>&    c;
+  const std::tuple<ARGS...>& args;
 
-  template < int... S >
-  auto apply_2( sequence< S... > ) const -> decltype( c( std::get< S >( args )... ) )
+  template<int... S>
+  auto apply_2(sequence<S...>) const -> decltype(c(std::get<S>(args)...))
   {
-    return c( std::get< S >( args )... );
+    return c(std::get<S>(args)...);
   }
-  auto apply_1() const -> decltype( apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() ) )
+  auto apply_1() const -> decltype(apply_2(typename sequence_gen<sizeof...(ARGS)>::type()))
   {
-    return apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() );
+    return apply_2(typename sequence_gen<sizeof...(ARGS)>::type());
   }
 };
 
-template < typename C, typename A, typename... ARGS >
+template<typename C, typename A, typename... ARGS>
 struct apply_helper_2
 {
-  const std::function< C >& c;
-  const A& a;
-  const std::tuple< ARGS... >& args;
+  const std::function<C>&    c;
+  const A&                   a;
+  const std::tuple<ARGS...>& args;
 
-  template < int... S >
-  auto apply_2( sequence< S... > ) const -> decltype( c( a, std::get< S >( args )... ) )
+  template<int... S>
+  auto apply_2(sequence<S...>) const -> decltype(c(a, std::get<S>(args)...))
   {
-    return c( a, std::get< S >( args )... );
+    return c(a, std::get<S>(args)...);
   }
-  auto apply_1() const -> decltype( apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() ) )
+  auto apply_1() const -> decltype(apply_2(typename sequence_gen<sizeof...(ARGS)>::type()))
   {
-    return apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() );
+    return apply_2(typename sequence_gen<sizeof...(ARGS)>::type());
   }
 };
 
-template < typename C, typename... ARGS >
-auto apply( const std::function< C >& c, const std::tuple< ARGS... >& args ) -> typename ReturnType< C >::type
+template<typename C, typename... ARGS>
+auto apply(const std::function<C>& c, const std::tuple<ARGS...>& args) -> typename ReturnType<C>::type
 {
-  apply_helper< C, ARGS... > ah{c, args};
+  apply_helper<C, ARGS...> ah{c, args};
   return ah.apply_1();
 }
 
-template < typename C, typename D, typename... ARGS >
-auto apply( const std::function< C >& c, const D& d, const std::tuple< ARGS... >& args ) -> typename ReturnType< C >::type
+template<typename C, typename D, typename... ARGS>
+auto apply(const std::function<C>& c, const D& d, const std::tuple<ARGS...>& args) -> typename ReturnType<C>::type
 {
-  apply_helper_2< C, D, ARGS... > ah{c, d, args};
+  apply_helper_2<C, D, ARGS...> ah{c, d, args};
   return ah.apply_1();
 }
 
 struct ConnectionState
 {
   DBusWrapper::ConnectionPtr connection;
-  DBusWrapper::ObjectPtr object;
-  DBusWrapper::ProxyPtr proxy, propertiesProxy;
+  DBusWrapper::ObjectPtr     object;
+  DBusWrapper::ProxyPtr      proxy, propertiesProxy;
 };
 
-template < typename RETTYPE, typename... ARGS >
-RETTYPE call( CallId callId, const ConnectionState& connectionState, bool property, const std::string& funcName, const ARGS&... args )
+template<typename RETTYPE, typename... ARGS>
+RETTYPE call(CallId callId, const ConnectionState& connectionState, bool property, const std::string& funcName, const ARGS&... args)
 {
-  const auto &proxy = property ? connectionState.propertiesProxy : connectionState.proxy;
-  if( !proxy )
+  const autoproxy = property ? connectionState.propertiesProxy : connectionState.proxy;
+  if(!proxy)
   {
-    DBUS_DEBUG( "call %d: not initialized", callId.id );
+    DBUS_DEBUG("call %d: not initialized", callId.id);
     return Error{"not initialized"};
   }
 
-  DBUS_DEBUG( "call %d: calling '%s'", callId.id, funcName.c_str() );
+  DBUS_DEBUG("call %d: calling '%s'", callId.id, funcName.c_str());
   auto msg = DBUS_W->eldbus_proxy_method_call_new_impl(proxy, funcName);
-  if( !msg )
+  if(!msg)
   {
-    DBUS_DEBUG( "call %d: failed", callId.id );
+    DBUS_DEBUG("call %d: failed", callId.id);
     return Error{"failed to create message"};
   }
 
-  detail::packValues( callId, msg, args... );
-  auto reply = DBUS_W->eldbus_proxy_send_and_block_impl( proxy, msg );
-  DBUS_DEBUG( "call %d: calling '%s' done", callId.id, funcName.c_str() );
-  if( !reply )
+  detail::packValues(callId, msg, args...);
+  auto reply = DBUS_W->eldbus_proxy_send_and_block_impl(proxy, msg);
+  DBUS_DEBUG("call %d: calling '%s' done", callId.id, funcName.c_str());
+  if(!reply)
   {
-    DBUS_DEBUG( "call %d: failed", callId.id );
+    DBUS_DEBUG("call %d: failed", callId.id);
     return Error{"eldbus returned null as reply"};
   }
   std::string errname, errmsg;
-  if( DBUS_W->eldbus_message_error_get_impl( reply, errname, errmsg ) )
+  if(DBUS_W->eldbus_message_error_get_impl(reply, errname, errmsg))
   {
-    DBUS_DEBUG( "call %d: %s: %s", callId.id, errname.c_str(), errmsg.c_str() );
+    DBUS_DEBUG("call %d: %s: %s", callId.id, errname.c_str(), errmsg.c_str());
     return Error{errname + ": " + errmsg};
   }
-  DBUS_DEBUG( "call %d: got reply with signature '%s'", callId.id,
-    DBUS_W->eldbus_message_signature_get_impl( reply ).c_str() );
-  return detail::unpackValues< RETTYPE >( callId, reply );
+  DBUS_DEBUG("call %d: got reply with signature '%s'", callId.id, DBUS_W->eldbus_message_signature_get_impl(reply).c_str());
+  return detail::unpackValues<RETTYPE>(callId, reply);
 }
 
-template < typename RETTYPE, typename... ARGS >
-void asyncCall( CallId callId, const ConnectionState &connectionState,
-                bool property, const std::string& funcName,
-                std::function< void( RETTYPE ) > callback, const ARGS&... args )
+template<typename RETTYPE, typename... ARGS>
+void asyncCall(CallId callId, const ConnectionState& connectionState, bool property, const std::string& funcName, std::function<void(RETTYPE)> callback, const ARGS&... args)
 {
-  const auto &proxy = property ? connectionState.propertiesProxy : connectionState.proxy;
-  if( !proxy )
+  const autoproxy = property ? connectionState.propertiesProxy : connectionState.proxy;
+  if(!proxy)
   {
-    DBUS_DEBUG( "call %d: not initialized", callId.id );
-    callback( Error{"not initialized"} );
+    DBUS_DEBUG("call %d: not initialized", callId.id);
+    callback(Error{"not initialized"});
     return;
   }
 
-  auto msg = DBUS_W->eldbus_proxy_method_call_new_impl( proxy, funcName );
-  if( !msg )
+  auto msg = DBUS_W->eldbus_proxy_method_call_new_impl(proxy, funcName);
+  if(!msg)
   {
-    DBUS_DEBUG( "call %d: failed", callId.id );
-    callback( Error{"failed to create message"} );
+    DBUS_DEBUG("call %d: failed", callId.id);
+    callback(Error{"failed to create message"});
     return;
   }
 
-  detail::packValues( callId, msg, args... );
-  auto pending = DBUS_W->eldbus_proxy_send_impl( proxy, msg, [callback, callId, proxy]( const DBusWrapper::MessagePtr &reply ) {
-        DBUS_DEBUG( "call %d: calling done", callId.id );
-        if( !reply )
-        {
-          DBUS_DEBUG( "call %d: failed", callId.id );
-          callback( Error{"eldbus returned null as reply"} );
-        }
-        else
-        {
-          std::string errname, errmsg;
-          if( DBUS_W->eldbus_message_error_get_impl( reply, errname, errmsg ) )
-          {
-            DBUS_DEBUG( "call %d: %s: %s", callId.id, errname.c_str(), errmsg.c_str() );
-            callback( Error{errname + ": " + errmsg} );
-          }
-          else
-          {
-            DBUS_DEBUG( "call %d: got reply with signature '%s'", callId.id,
-                DBUS_W->eldbus_message_signature_get_impl( reply ).c_str() );
-            callback( detail::unpackValues< RETTYPE >( callId, reply ) );
-          }
-        }
+  detail::packValues(callId, msg, args...);
+  auto pending = DBUS_W->eldbus_proxy_send_impl(proxy, msg, [callback, callId, proxy](const DBusWrapper::MessagePtr& reply) {
+    DBUS_DEBUG("call %d: calling done", callId.id);
+    if(!reply)
+    {
+      DBUS_DEBUG("call %d: failed", callId.id);
+      callback(Error{"eldbus returned null as reply"});
+    }
+    else
+    {
+      std::string errname, errmsg;
+      if(DBUS_W->eldbus_message_error_get_impl(reply, errname, errmsg))
+      {
+        DBUS_DEBUG("call %d: %s: %s", callId.id, errname.c_str(), errmsg.c_str());
+        callback(Error{errname + ": " + errmsg});
       }
-    );
-  if( pending )
+      else
+      {
+        DBUS_DEBUG("call %d: got reply with signature '%s'", callId.id, DBUS_W->eldbus_message_signature_get_impl(reply).c_str());
+        callback(detail::unpackValues<RETTYPE>(callId, reply));
+      }
+    }
+  });
+  if(pending)
   {
-    DBUS_DEBUG( "call %d: call sent", callId.id );
+    DBUS_DEBUG("call %d: call sent", callId.id);
   }
   else
   {
-    DBUS_DEBUG( "call %d: failed to send call", callId.id );
-    callback( Error{"failed to send call"} );
+    DBUS_DEBUG("call %d: failed to send call", callId.id);
+    callback(Error{"failed to send call"});
   }
 }
 
-inline void displayDebugCallInfo( CallId callId, const std::string& funcName, const std::string& info, const std::string& interfaceName )
+inline void displayDebugCallInfo(CallId callId, const std::string& funcName, const std::string& info, const std::string& interfaceName)
 {
-  DBUS_DEBUG( "call %d: %s iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str() );
+  DBUS_DEBUG("call %d: %s iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str());
 }
 
-inline void displayDebugCallInfoSignal( CallId callId, const std::string& funcName, const std::string& info, const std::string& interfaceName )
+inline void displayDebugCallInfoSignal(CallId callId, const std::string& funcName, const std::string& info, const std::string& interfaceName)
 {
-  DBUS_DEBUG( "call %d: %s signal iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str() );
+  DBUS_DEBUG("call %d: %s signal iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str());
 }
 
-inline void displayDebugCallInfoProperty( CallId callId, const std::string& funcName, std::string info, const std::string& interfaceName,
-                                          const std::string& propertyName )
+inline void displayDebugCallInfoProperty(CallId callId, const std::string& funcName, std::string info, const std::string& interfaceName, const std::string& propertyName)
 {
-  DBUS_DEBUG( "call %d: %s iname = %s pname = %s", callId.id, info.c_str(), interfaceName.c_str(), propertyName.c_str() );
+  DBUS_DEBUG("call %d: %s iname = %s pname = %s", callId.id, info.c_str(), interfaceName.c_str(), propertyName.c_str());
 }
 
 using StringStorage = DBusWrapper::StringStorage;
 
-template < typename A, typename... ARGS >
+template<typename A, typename... ARGS>
 struct EldbusArgGenerator_Helper
 {
-  static void add( std::vector< std::pair<std::string, std::string> >& r )
+  static void add(std::vector<std::pair<std::string, std::string>>& r)
   {
-    auto s = r.size();
-    auto sig = signature< A >::sig();
-    bart_assert( !sig.empty() );
-    auto name = "p" + std::to_string( s + 1 );
-    r.push_back({ std::move(sig), std::move(name) });
-    EldbusArgGenerator_Helper<ARGS...>::add( r );
+    auto s   = r.size();
+    auto sig = signature<A>::sig();
+    bart_assert(!sig.empty());
+    auto name = "p" + std::to_string(s + 1);
+    r.push_back({std::move(sig), std::move(name)});
+    EldbusArgGenerator_Helper<ARGS...>::add(r);
   }
 };
 
-template <>
-struct EldbusArgGenerator_Helper< void >
+template<>
+struct EldbusArgGenerator_Helper<void>
 {
-  static void add( std::vector< std::pair<std::string, std::string> >& )
+  static void add(std::vector<std::pair<std::string, std::string>>&)
   {
   }
 };
 
-template <>
-struct EldbusArgGenerator_Helper< ValueOrError< void >, void >
+template<>
+struct EldbusArgGenerator_Helper<ValueOrError<void>, void>
 {
-  static void add( std::vector< std::pair<std::string, std::string> >& )
+  static void add(std::vector<std::pair<std::string, std::string>>&)
   {
   }
 };
-template <>
-struct EldbusArgGenerator_Helper< ValueOrError<>, void >
+template<>
+struct EldbusArgGenerator_Helper<ValueOrError<>, void>
 {
-  static void add( std::vector< std::pair<std::string, std::string> >& )
+  static void add(std::vector<std::pair<std::string, std::string>>&)
   {
   }
 };
 
-template < typename... ARGS >
-struct EldbusArgGenerator_Helper< std::tuple< ARGS... > >
+template<typename... ARGS>
+struct EldbusArgGenerator_Helper<std::tuple<ARGS...>>
 {
-  static void add( std::vector< std::pair<std::string, std::string> >& r )
+  static void add(std::vector<std::pair<std::string, std::string>>& r)
   {
-    EldbusArgGenerator_Helper< ARGS..., void >::add( r );
+    EldbusArgGenerator_Helper<ARGS..., void>::add(r);
   }
 };
 
-template < typename RetType >
+template<typename RetType>
 struct dbus_interface_return_type_traits
 {
-  using type = ValueOrError< RetType >;
+  using type = ValueOrError<RetType>;
 };
 
-template < typename... ARGS >
-struct dbus_interface_return_type_traits< ValueOrError< ARGS... > >
+template<typename... ARGS>
+struct dbus_interface_return_type_traits<ValueOrError<ARGS...>>
 {
-  using type = ValueOrError< ARGS... >;
+  using type = ValueOrError<ARGS...>;
 };
 
-template < typename T >
+template<typename T>
 struct dbus_interface_traits;
-template < typename RetType, typename... ARGS >
-struct dbus_interface_traits< RetType( ARGS... ) >
+template<typename RetType, typename... ARGS>
+struct dbus_interface_traits<RetType(ARGS...)>
 {
-  using Ret = typename dbus_interface_return_type_traits< RetType >::type;
-  using SyncCB = std::function< Ret( ARGS... ) >;
-  using AsyncCB = std::function< void( std::function< void( Ret ) >, ARGS... ) >;
-  using VEArgs = ValueOrError< ARGS... >;
+  using Ret     = typename dbus_interface_return_type_traits<RetType>::type;
+  using SyncCB  = std::function<Ret(ARGS...)>;
+  using AsyncCB = std::function<void(std::function<void(Ret)>, ARGS...)>;
+  using VEArgs  = ValueOrError<ARGS...>;
 };
 
-template < typename T >
+template<typename T>
 struct EldbusArgGenerator_Args;
-template < typename RetType, typename... ARGS >
-struct EldbusArgGenerator_Args< RetType( ARGS... ) >
+template<typename RetType, typename... ARGS>
+struct EldbusArgGenerator_Args<RetType(ARGS...)>
 {
   static std::string name()
   {
-    return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::name();
+    return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name();
   }
-  static std::vector< std::pair<std::string, std::string> > get()
+  static std::vector<std::pair<std::string, std::string>> get()
   {
-    std::vector< std::pair<std::string, std::string> > tmp;
-    EldbusArgGenerator_Helper< ARGS..., void >::add( tmp );
+    std::vector<std::pair<std::string, std::string>> tmp;
+    EldbusArgGenerator_Helper<ARGS..., void>::add(tmp);
     return tmp;
   }
 };
 
-template < typename T >
+template<typename T>
 struct EldbusArgGenerator_ReturnType;
-template < typename RetType, typename... ARGS >
-struct EldbusArgGenerator_ReturnType< RetType( ARGS... ) >
+template<typename RetType, typename... ARGS>
+struct EldbusArgGenerator_ReturnType<RetType(ARGS...)>
 {
   static std::string name()
   {
-    return signature< RetType >::name();
+    return signature<RetType>::name();
   }
-  static std::vector< std::pair<std::string, std::string> > get( )
+  static std::vector<std::pair<std::string, std::string>> get()
   {
-    std::vector< std::pair<std::string, std::string> > tmp;
-    EldbusArgGenerator_Helper< RetType, void >::add( tmp );
+    std::vector<std::pair<std::string, std::string>> tmp;
+    EldbusArgGenerator_Helper<RetType, void>::add(tmp);
     return tmp;
   }
 };
 
-template < typename T >
+template<typename T>
 struct EldbusArgGenerator_ReturnType;
-template < typename... ARGS >
-struct EldbusArgGenerator_ReturnType< void( ARGS... ) >
+template<typename... ARGS>
+struct EldbusArgGenerator_ReturnType<void(ARGS...)>
 {
   static std::string name()
   {
     return "";
   }
-  static std::vector< std::pair<std::string, std::string> > get( )
+  static std::vector<std::pair<std::string, std::string>> get()
   {
     return {};
   }
 };
 /// \endcond
-}
+} // namespace detail
 
 using ConnectionType = DBusWrapper::ConnectionType;
 
@@ -2635,8 +2943,7 @@ public:
    * @param path_name object's path
    * @param interface_name interface name
    */
-  DBusClient( std::string busName_, std::string pathName_, std::string interfaceName_,
-              ConnectionType tp );
+  DBusClient(std::string busName_, std::string pathName_, std::string interfaceName_, ConnectionType tp);
 
   /**
    * @brief Connects to dbus using connection conn
@@ -2646,8 +2953,7 @@ public:
    * @param interface_name interface name
    * @param conn connection object from getDBusConnectionByType call
    */
-  DBusClient( std::string busName_, std::string pathName_, std::string interfaceName_,
-              const DBusWrapper::ConnectionPtr &conn = {} );
+  DBusClient(std::string busName_, std::string pathName_, std::string interfaceName_, const DBusWrapper::ConnectionPtr& conn = {});
 
   /**
    * @brief Destructor object.
@@ -2656,12 +2962,12 @@ public:
    * All asynchronous calls will be cancelled, their callback's will be called
    * with failure message.
    */
-  ~DBusClient() = default;
-  DBusClient( const DBusClient& ) = delete;
-  DBusClient( DBusClient&& ) = default;
+  ~DBusClient()                 = default;
+  DBusClient(const DBusClient&) = delete;
+  DBusClient(DBusClient&&)      = default;
 
-  DBusClient& operator=( DBusClient&& ) = default;
-  DBusClient& operator=( const DBusClient& ) = delete;
+  DBusClient& operator=(DBusClient&&) = default;
+  DBusClient& operator=(const DBusClient&) = delete;
 
   /**
    * @brief bool operator
@@ -2670,7 +2976,7 @@ public:
    */
   explicit operator bool() const
   {
-    return bool( connectionState->proxy );
+    return bool(connectionState->proxy);
   }
 
   /**
@@ -2683,15 +2989,15 @@ public:
    * defines method, which takes two arguments (two floats) and return
    * single value of type int.
    */
-  template < typename T >
+  template<typename T>
   struct Method
   {
     /// \cond
-    using RetType = typename detail::dbus_interface_traits< T >::Ret;
-    const detail::ConnectionState &connectionState;
-    std::string funcName;
-    std::string info;
-    std::shared_ptr< ConnectionInfo > connectionInfo;
+    using RetType = typename detail::dbus_interface_traits<T>::Ret;
+    const detail::ConnectionState&  connectionState;
+    std::string                     funcName;
+    std::string                     info;
+    std::shared_ptr<ConnectionInfo> connectionInfo;
     /// \endcond
 
     /**
@@ -2702,12 +3008,12 @@ public:
      *
      * @param args arguments to pass to the method
      */
-    template < typename... ARGS >
-    RetType call( const ARGS&... args )
+    template<typename... ARGS>
+    RetType call(const ARGS&... args)
     {
       detail::CallId callId;
-      detail::displayDebugCallInfo( callId, funcName, info, connectionInfo->interfaceName );
-      return detail::call< RetType >( callId, connectionState, false, funcName, args... );
+      detail::displayDebugCallInfo(callId, funcName, info, connectionInfo->interfaceName);
+      return detail::call<RetType>(callId, connectionState, false, funcName, args...);
     }
 
     /**
@@ -2718,12 +3024,12 @@ public:
      * @param callback callback functor, which will be called with return value(s) or error message
      * @param args arguments to pass to the method
      */
-    template < typename... ARGS >
-    void asyncCall( std::function< void( RetType ) > callback, const ARGS&... args )
+    template<typename... ARGS>
+    void asyncCall(std::function<void(RetType)> callback, const ARGS&... args)
     {
       detail::CallId callId;
-      detail::displayDebugCallInfo( callId, funcName, info, connectionInfo->interfaceName );
-      detail::asyncCall< RetType >( callId, connectionState, false, funcName, std::move( callback ), args... );
+      detail::displayDebugCallInfo(callId, funcName, info, connectionInfo->interfaceName);
+      detail::asyncCall<RetType>(callId, connectionState, false, funcName, std::move(callback), args...);
     }
   };
 
@@ -2734,16 +3040,16 @@ public:
    * Note, that library automatically wraps both sent and received value into
    * DBUS's wrapper type.
    */
-  template < typename T >
+  template<typename T>
   struct Property
   {
     /// \cond
-    using RetType = typename detail::dbus_interface_return_type_traits< T >::type;
-    using VariantRetType = typename detail::dbus_interface_return_type_traits< EldbusVariant< T > >::type;
-    const detail::ConnectionState &connectionState;
-    std::string propName;
-    std::string info;
-    std::shared_ptr< ConnectionInfo > connectionInfo;
+    using RetType        = typename detail::dbus_interface_return_type_traits<T>::type;
+    using VariantRetType = typename detail::dbus_interface_return_type_traits<EldbusVariant<T>>::type;
+    const detail::ConnectionState&  connectionState;
+    std::string                     propName;
+    std::string                     info;
+    std::shared_ptr<ConnectionInfo> connectionInfo;
     /// \endcond
 
     /**
@@ -2755,11 +3061,11 @@ public:
     RetType get()
     {
       detail::CallId callId;
-      detail::displayDebugCallInfoProperty( callId, "Get", info, connectionInfo->interfaceName, propName );
-      auto z = detail::call< VariantRetType >( callId, connectionState, true, "Get", connectionInfo->interfaceName, propName );
-      if( !z )
+      detail::displayDebugCallInfoProperty(callId, "Get", info, connectionInfo->interfaceName, propName);
+      auto z = detail::call<VariantRetType>(callId, connectionState, true, "Get", connectionInfo->interfaceName, propName);
+      if(!z)
         return z.getError();
-      return {std::get< 0 >( z.getValues() ).value};
+      return {std::get<0>(z.getValues()).value};
     }
 
     /**
@@ -2769,17 +3075,17 @@ public:
      *
      * @param callback callback functor, which will be called with return value(s) or error message
      */
-    void asyncGet( std::function< void( RetType ) > callback )
+    void asyncGet(std::function<void(RetType)> callback)
     {
       detail::CallId callId;
-      detail::displayDebugCallInfoProperty( callId, "Get", info, connectionInfo->interfaceName, propName );
-      auto cc = [callback]( VariantRetType reply ) {
-        if( reply )
-          callback( std::move( std::get< 0 >( reply.getValues() ).value ) );
+      detail::displayDebugCallInfoProperty(callId, "Get", info, connectionInfo->interfaceName, propName);
+      auto cc = [callback](VariantRetType reply) {
+        if(reply)
+          callback(std::move(std::get<0>(reply.getValues()).value));
         else
-          callback( reply.getError() );
+          callback(reply.getError());
       };
-      detail::asyncCall< VariantRetType >( callId, connectionState, true, "Get", std::move( cc ), connectionInfo->interfaceName, propName );
+      detail::asyncCall<VariantRetType>(callId, connectionState, true, "Get", std::move(cc), connectionInfo->interfaceName, propName);
     }
 
     /**
@@ -2788,12 +3094,12 @@ public:
      * The function returns ValueOrError<void> object, with
      * possible error message.
      */
-    ValueOrError< void > set( const T& r )
+    ValueOrError<void> set(const T& r)
     {
       detail::CallId callId;
-      detail::displayDebugCallInfoProperty( callId, "Set", info, connectionInfo->interfaceName, propName );
-      EldbusVariant< T > variantValue{std::move( r )};
-      return detail::call< ValueOrError< void > >( callId, connectionState, true, "Set", connectionInfo->interfaceName, propName, variantValue );
+      detail::displayDebugCallInfoProperty(callId, "Set", info, connectionInfo->interfaceName, propName);
+      EldbusVariant<T> variantValue{std::move(r)};
+      return detail::call<ValueOrError<void>>(callId, connectionState, true, "Set", connectionInfo->interfaceName, propName, variantValue);
     }
 
     /**
@@ -2803,12 +3109,12 @@ public:
      *
      * @param callback callback functor, which will be called with return value(s) or error message
      */
-    void asyncSet( std::function< void( ValueOrError< void > ) > callback, const T& r )
+    void asyncSet(std::function<void(ValueOrError<void>)> callback, const T& r)
     {
       detail::CallId callId;
-      detail::displayDebugCallInfoProperty( callId, "Set", info, connectionInfo->interfaceName, propName );
-      EldbusVariant< T > variantValue{std::move( r )};
-      detail::asyncCall< ValueOrError< void > >( callId, connectionState, true, "Set", std::move( callback ), connectionInfo->interfaceName, propName, variantValue );
+      detail::displayDebugCallInfoProperty(callId, "Set", info, connectionInfo->interfaceName, propName);
+      EldbusVariant<T> variantValue{std::move(r)};
+      detail::asyncCall<ValueOrError<void>>(callId, connectionState, true, "Set", std::move(callback), connectionInfo->interfaceName, propName, variantValue);
     }
   };
 
@@ -2819,10 +3125,10 @@ public:
    *
    * @param propName property name to set and / or query
    */
-  template < typename PropertyType >
-  Property< PropertyType > property( std::string propName )
+  template<typename PropertyType>
+  Property<PropertyType> property(std::string propName)
   {
-    return Property< PropertyType >{*connectionState, std::move( propName ), info, connectionInfo};
+    return Property<PropertyType>{*connectionState, std::move(propName), info, connectionInfo};
   }
 
   /**
@@ -2832,10 +3138,10 @@ public:
    *
    * @param funcName function name to call
    */
-  template < typename MethodType >
-  Method< MethodType > method( std::string funcName )
+  template<typename MethodType>
+  Method<MethodType> method(std::string funcName)
   {
-    return Method< MethodType >{*connectionState, std::move( funcName ), info, connectionInfo};
+    return Method<MethodType>{*connectionState, std::move(funcName), info, connectionInfo};
   }
 
   /**
@@ -2845,22 +3151,21 @@ public:
    * Note, that template type V must match expected type, otherwise undefined behavior will occur,
    * there's no check for this.
    */
-  template < typename V >
-  void addPropertyChangedEvent( std::string propertyName, std::function< void( V ) > callback )
+  template<typename V>
+  void addPropertyChangedEvent(std::string propertyName, std::function<void(V)> callback)
   {
     detail::CallId callId;
-    detail::displayDebugCallInfoSignal( callId, propertyName, info, connectionInfo->interfaceName );
-    DBUS_DEBUG( "call %d: adding property", callId.id );
-    auto &cI = this->connectionInfo;
-    DBUS_W->add_property_changed_event_listener_impl(connectionState->proxy, cI->interfaceName, propertyName,
-      [callback]( const _Eina_Value *newValue ) {
+    detail::displayDebugCallInfoSignal(callId, propertyName, info, connectionInfo->interfaceName);
+    DBUS_DEBUG("call %d: adding property", callId.id);
+    auto& cI = this->connectionInfo;
+    DBUS_W->add_property_changed_event_listener_impl(connectionState->proxy, cI->interfaceName, propertyName, [callback](const _Eina_Value* newValue) {
       V val = 0;
-      if( !getFromEinaValue( newValue, &val ) )
+      if(!getFromEinaValue(newValue, &val))
       {
-        DBUS_DEBUG( "unable to get property's value" );
+        DBUS_DEBUG("unable to get property's value");
         return;
       }
-      callback( val );
+      callback(val);
     });
   }
 
@@ -2874,46 +3179,45 @@ public:
    * @param signalName name of the signal to register
    * @param callback callback to call
    */
-  template < typename SignalType >
-  void addSignal( std::string signalName, std::function< SignalType > callback )
+  template<typename SignalType>
+  void addSignal(std::string signalName, std::function<SignalType> callback)
   {
     detail::CallId callId;
-    detail::displayDebugCallInfoSignal( callId, signalName, info, connectionInfo->interfaceName );
-    DBUS_W->eldbus_proxy_signal_handler_add_impl( connectionState->proxy, signalName,
-      [callId, callback, signalName]( const DBusWrapper::MessagePtr &msg ) -> void {
-        std::string errname, aux;
-        if( DBUS_W->eldbus_message_error_get_impl( msg, errname, aux ) )
-        {
-          DBUS_DEBUG( "call %d: Eldbus error: %s %s", callId.id, errname.c_str(), aux.c_str() );
-          return;
-        }
-        DBUS_DEBUG( "call %d: received signal with signature '%s'", callId.id, DBUS_W->eldbus_message_signature_get_impl( msg ).c_str() );
-        using ParamsType = typename detail::dbus_interface_traits< SignalType >::VEArgs;
-        auto params = detail::unpackValues< ParamsType >( callId, msg );
-        if( !params )
-        {
-          DBUS_DEBUG( "call %d: failed: %s", callId.id, params.getError().message.c_str() );
-          return;
-        }
-        try
-        {
-          detail::apply( callback, params.getValues() );
-        }
-        catch( ... )
-        {
-          DBUS_DEBUG( "unhandled exception" );
-          bart_assert( 0 );
-        }
-      });
+    detail::displayDebugCallInfoSignal(callId, signalName, info, connectionInfo->interfaceName);
+    DBUS_W->eldbus_proxy_signal_handler_add_impl(connectionState->proxy, signalName, [callId, callback, signalName](const DBusWrapper::MessagePtr& msg) -> void {
+      std::string errname, aux;
+      if(DBUS_W->eldbus_message_error_get_impl(msg, errname, aux))
+      {
+        DBUS_DEBUG("call %d: Eldbus error: %s %s", callId.id, errname.c_str(), aux.c_str());
+        return;
+      }
+      DBUS_DEBUG("call %d: received signal with signature '%s'", callId.id, DBUS_W->eldbus_message_signature_get_impl(msg).c_str());
+      using ParamsType = typename detail::dbus_interface_traits<SignalType>::VEArgs;
+      auto params      = detail::unpackValues<ParamsType>(callId, msg);
+      if(!params)
+      {
+        DBUS_DEBUG("call %d: failed: %s", callId.id, params.getError().message.c_str());
+        return;
+      }
+      try
+      {
+        detail::apply(callback, params.getValues());
+      }
+      catch(...)
+      {
+        DBUS_DEBUG("unhandled exception");
+        bart_assert(0);
+      }
+    });
   }
 
 private:
   /// \cond
-  std::unique_ptr<detail::ConnectionState> connectionState{ new detail::ConnectionState };
-  std::string info;
-  std::shared_ptr< ConnectionInfo > connectionInfo;
+  std::unique_ptr<detail::ConnectionState> connectionState{new detail::ConnectionState};
+  std::string                              info;
+  std::shared_ptr<ConnectionInfo>          connectionInfo;
 
-  static bool getFromEinaValue(const _Eina_Value *v, void *dst);
+  static bool getFromEinaValue(const _Eina_Value* v, void* dst);
   /// \endcond
 };
 
@@ -2927,10 +3231,10 @@ class DBusInterfaceDescription
 
 public:
   /// \cond
-  using MethodInfo = DBusWrapper::MethodInfo;
-  using SignalInfo = DBusWrapper::SignalInfo;
+  using MethodInfo   = DBusWrapper::MethodInfo;
+  using SignalInfo   = DBusWrapper::SignalInfo;
   using PropertyInfo = DBusWrapper::PropertyInfo;
-  using SignalId = DBusWrapper::SignalId;
+  using SignalId     = DBusWrapper::SignalId;
   /// \endcond
 
   /**
@@ -2938,7 +3242,7 @@ public:
    *
    * @param interfaceName name of the interface
    */
-  DBusInterfaceDescription( std::string interfaceName );
+  DBusInterfaceDescription(std::string interfaceName);
 
   /**
    * @brief adds new, synchronous method to the interface
@@ -2958,21 +3262,19 @@ public:
    * @param memberName name of the method
    * @param callback functor, which will be called
    */
-  template < typename T >
-  void addMethod( const std::string& memberName, typename detail::dbus_interface_traits< T >::SyncCB callback )
+  template<typename T>
+  void addMethod(const std::string& memberName, typename detail::dbus_interface_traits<T>::SyncCB callback)
   {
     detail::CallId callId;
-    MethodInfo mi;
-    methods.push_back( std::move( mi ) );
-    auto& z = methods.back();
-    z.in = detail::EldbusArgGenerator_Args< T >::get( );
-    z.out = detail::EldbusArgGenerator_ReturnType< T >::get( );
+    MethodInfo     mi;
+    methods.push_back(std::move(mi));
+    auto& z      = methods.back();
+    z.in         = detail::EldbusArgGenerator_Args<T>::get();
+    z.out        = detail::EldbusArgGenerator_ReturnType<T>::get();
     z.memberName = memberName;
-    DBUS_DEBUG( "call %d: method %s, in %s, out %s", callId.id, memberName.c_str(),
-                detail::EldbusArgGenerator_Args< T >::name().c_str(),
-                detail::EldbusArgGenerator_ReturnType< T >::name().c_str() );
-    z.callback = construct< T >( callId, callback );
-    z.id = callId;
+    DBUS_DEBUG("call %d: method %s, in %s, out %s", callId.id, memberName.c_str(), detail::EldbusArgGenerator_Args<T>::name().c_str(), detail::EldbusArgGenerator_ReturnType<T>::name().c_str());
+    z.callback = construct<T>(callId, callback);
+    z.id       = callId;
   }
 
   /**
@@ -2991,75 +3293,74 @@ public:
    * @param getter functor, which will be called when property is being read
    * @param setter functor, which will be called when property is being set
    */
-  template < typename T >
-  void addProperty( const std::string& memberName, std::function< ValueOrError< T >() > getter, std::function< ValueOrError< void >( T ) > setter )
+  template<typename T>
+  void addProperty(const std::string& memberName, std::function<ValueOrError<T>()> getter, std::function<ValueOrError<void>(T)> setter)
   {
-    properties.push_back( {} );
-    auto& z = properties.back();
-    z.memberName = memberName;
-    z.typeSignature = detail::signature< T >::sig();
-    if( getter )
+    properties.push_back({});
+    auto& z         = properties.back();
+    z.memberName    = memberName;
+    z.typeSignature = detail::signature<T>::sig();
+    if(getter)
     {
       detail::CallId getterId;
       z.getterId = getterId;
-      DBUS_DEBUG( "call %d: property %s (get) type %s", getterId.id, memberName.c_str(), detail::signature< T >::name().c_str() );
-      z.getCallback = [=]( const DBusWrapper::MessagePtr &src, const DBusWrapper::MessageIterPtr &dst ) -> std::string {
+      DBUS_DEBUG("call %d: property %s (get) type %s", getterId.id, memberName.c_str(), detail::signature<T>::name().c_str());
+      z.getCallback = [=](const DBusWrapper::MessagePtr& src, const DBusWrapper::MessageIterPtr& dst) -> std::string {
         try
         {
-          auto v = detail::apply( getter, std::tuple<>{} );
-          if( v )
+          auto v = detail::apply(getter, std::tuple<>{});
+          if(v)
           {
-            detail::signature< T >::set( dst, std::get< 0 >( v.getValues() ) );
-            DBUS_DEBUG( "call %d: success", getterId.id );
+            detail::signature<T>::set(dst, std::get<0>(v.getValues()));
+            DBUS_DEBUG("call %d: success", getterId.id);
             return "";
           }
-          DBUS_DEBUG( "call %d: failed: %s", getterId.id, v.getError().message.c_str() );
+          DBUS_DEBUG("call %d: failed: %s", getterId.id, v.getError().message.c_str());
           return v.getError().message;
         }
-        catch( std::exception& e )
+        catch(std::exception& e)
         {
-          return std::string( "unhandled exception (" ) + e.what() + ")";
+          return std::string("unhandled exception (") + e.what() + ")";
         }
-        catch( ... )
+        catch(...)
         {
           return "unhandled exception";
         }
       };
     }
-    if( setter )
+    if(setter)
     {
       detail::CallId setterId;
       z.setterId = setterId;
-      DBUS_DEBUG( "call %d: property %s (set) type %s", setterId.id, memberName.c_str(), detail::signature< T >::name().c_str() );
-      z.setCallback = [=]( const DBusWrapper::MessagePtr &src, const DBusWrapper::MessageIterPtr &src_iter ) -> std::string {
-        std::tuple< T > value;
-        auto src_signature = DBUS_W->eldbus_message_iter_signature_get_impl( src_iter );
-        if( detail::signature< T >::get( src_iter, std::get< 0 >( value ) ) )
+      DBUS_DEBUG("call %d: property %s (set) type %s", setterId.id, memberName.c_str(), detail::signature<T>::name().c_str());
+      z.setCallback = [=](const DBusWrapper::MessagePtr& src, const DBusWrapper::MessageIterPtr& src_iter) -> std::string {
+        std::tuple<T> value;
+        auto          src_signature = DBUS_W->eldbus_message_iter_signature_get_impl(src_iter);
+        if(detail::signature<T>::get(src_iter, std::get<0>(value)))
         {
           try
           {
-            auto v = detail::apply( setter, std::move( value ) );
-            if( v )
+            auto v = detail::apply(setter, std::move(value));
+            if(v)
             {
-              DBUS_DEBUG( "call %d: success", setterId.id );
+              DBUS_DEBUG("call %d: success", setterId.id);
               return "";
             }
-            DBUS_DEBUG( "call %d: failed: %s", setterId.id, v.getError().message.c_str() );
+            DBUS_DEBUG("call %d: failed: %s", setterId.id, v.getError().message.c_str());
             return v.getError().message;
           }
-          catch( std::exception& e )
+          catch(std::exception& e)
           {
-            return std::string( "unhandled exception (" ) + e.what() + ")";
+            return std::string("unhandled exception (") + e.what() + ")";
           }
-          catch( ... )
+          catch(...)
           {
             return "unhandled exception";
           }
         }
-        DBUS_DEBUG( "call %d: failed to unpack values, got signature '%s', expected '%s'", setterId.id,
-                    src_signature.c_str(), detail::signature< T >::sig().c_str() );
-        return "call " + std::to_string( setterId.id ) + ": failed to unpack values, got signature '" +
-                     src_signature + "', expected '" + detail::signature< T >::sig() + "'";
+        DBUS_DEBUG("call %d: failed to unpack values, got signature '%s', expected '%s'", setterId.id, src_signature.c_str(), detail::signature<T>::sig().c_str());
+        return "call " + std::to_string(setterId.id) + ": failed to unpack values, got signature '" +
+               src_signature + "', expected '" + detail::signature<T>::sig() + "'";
       };
     }
   }
@@ -3071,71 +3372,71 @@ public:
    *
    * @param memberName name of the method
    */
-  template < typename... ARGS >
-  SignalId addSignal( const std::string& memberName )
+  template<typename... ARGS>
+  SignalId addSignal(const std::string& memberName)
   {
     detail::CallId callId;
-    signals.push_back( {} );
-    auto& z = signals.back();
+    signals.push_back({});
+    auto& z      = signals.back();
     z.memberName = memberName;
-    z.args = detail::EldbusArgGenerator_Args< void( ARGS... ) >::get( DBUS_W->Strings );
-    z.id = callId;
-    DBUS_DEBUG( "call %d: signal %s", callId.id, memberName.c_str() );
+    z.args       = detail::EldbusArgGenerator_Args<void(ARGS...)>::get(DBUS_W->Strings);
+    z.id         = callId;
+    DBUS_DEBUG("call %d: signal %s", callId.id, memberName.c_str());
     return SignalId{callId};
   }
 
 private:
   /// \cond
-  std::vector< MethodInfo > methods;
-  std::vector< PropertyInfo > properties;
-  std::vector< SignalInfo > signals;
-  std::string interfaceName;
-
-  template < typename T >
-  std::function< DBusWrapper::MessagePtr( const DBusWrapper::MessagePtr &msg ) > construct( detail::CallId callId,
-                                                                           typename detail::dbus_interface_traits< T >::SyncCB callback )
-  {
-    using VEArgs = typename detail::dbus_interface_traits< T >::VEArgs;
-    return [=]( const DBusWrapper::MessagePtr &msg ) -> DBusWrapper::MessagePtr {
-      DBUS_DEBUG( "call %d: entering", callId.id );
-      DBusWrapper::MessagePtr ret = {};
-      auto args = detail::unpackValues< VEArgs >( callId, msg );
-      if( args )
+  std::vector<MethodInfo>   methods;
+  std::vector<PropertyInfo> properties;
+  std::vector<SignalInfo>   signals;
+  std::string               interfaceName;
+
+  template<typename T>
+  std::function<DBusWrapper::MessagePtr(const DBusWrapper::MessagePtr& msg)> construct(detail::CallId                                    callId,
+                                                                                       typename detail::dbus_interface_traits<T>::SyncCB callback)
+  {
+    using VEArgs = typename detail::dbus_interface_traits<T>::VEArgs;
+    return [=](const DBusWrapper::MessagePtr& msg) -> DBusWrapper::MessagePtr {
+      DBUS_DEBUG("call %d: entering", callId.id);
+      DBusWrapper::MessagePtr ret  = {};
+      auto                    args = detail::unpackValues<VEArgs>(callId, msg);
+      if(args)
       {
         try
         {
-          auto v = detail::apply( callback, std::move( args.getValues() ) );
-          if( v )
+          auto v = detail::apply(callback, std::move(args.getValues()));
+          if(v)
           {
-            DBUS_DEBUG( "call %d: success", callId.id );
-            ret = DBUS_W->eldbus_message_method_return_new_impl( msg );
-            detail::packValues( callId, ret, v );
+            DBUS_DEBUG("call %d: success", callId.id);
+            ret = DBUS_W->eldbus_message_method_return_new_impl(msg);
+            detail::packValues(callId, ret, v);
           }
           else
           {
-            DBUS_DEBUG( "call %d: failed: %s", callId.id, v.getError().message.c_str() );
-            ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.Failed", v.getError().message );
+            DBUS_DEBUG("call %d: failed: %s", callId.id, v.getError().message.c_str());
+            ret = DBUS_W->eldbus_message_error_new_impl(msg, "org.freedesktop.DBus.Error.Failed", v.getError().message);
           }
         }
-        catch( std::exception& e )
+        catch(std::exception& e)
         {
-          auto txt = std::string( "unhandled exception (" ) + e.what() + ")";
-          DBUS_DEBUG( "call %d: failed: %s", callId.id, txt.c_str() );
-          ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.Failed", txt );
+          auto txt = std::string("unhandled exception (") + e.what() + ")";
+          DBUS_DEBUG("call %d: failed: %s", callId.id, txt.c_str());
+          ret = DBUS_W->eldbus_message_error_new_impl(msg, "org.freedesktop.DBus.Error.Failed", txt);
         }
-        catch( ... )
+        catch(...)
         {
-          DBUS_DEBUG( "call %d: failed: %s", callId.id, "unhandled exception" );
-          ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.Failed", "unhandled exception" );
+          DBUS_DEBUG("call %d: failed: %s", callId.id, "unhandled exception");
+          ret = DBUS_W->eldbus_message_error_new_impl(msg, "org.freedesktop.DBus.Error.Failed", "unhandled exception");
         }
       }
       else
       {
         std::ostringstream err;
-        err << "expected signature '" << detail::signature< VEArgs >::sig() << "', got '" << DBUS_W->eldbus_message_signature_get_impl( msg ) << "'";
+        err << "expected signature '" << detail::signature<VEArgs>::sig() << "', got '" << DBUS_W->eldbus_message_signature_get_impl(msg) << "'";
         auto str = err.str();
-        DBUS_DEBUG( "call %d: failed: %s", callId.id, str.c_str() );
-        ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.InvalidArgs", str );
+        DBUS_DEBUG("call %d: failed: %s", callId.id, str.c_str());
+        ret = DBUS_W->eldbus_message_error_new_impl(msg, "org.freedesktop.DBus.Error.InvalidArgs", str);
       }
       return ret;
     };
@@ -3161,12 +3462,12 @@ public:
   /**
    * @brief Constructs dbus server on either system or user dbus connection.
    */
-  DBusServer( ConnectionType tp );
+  DBusServer(ConnectionType tp);
 
   /**
    * @brief Constructs dbus server on connection from getDBusConnectionByType
    */
-  DBusServer( const DBusWrapper::ConnectionPtr &conn );
+  DBusServer(const DBusWrapper::ConnectionPtr& conn);
 
   /**
    * @brief Destructor
@@ -3176,11 +3477,11 @@ public:
    */
   ~DBusServer() = default;
 
-  DBusServer( const DBusServer& ) = delete;
-  DBusServer( DBusServer&& ) = default;
+  DBusServer(const DBusServer&) = delete;
+  DBusServer(DBusServer&&)      = default;
 
-  DBusServer& operator=( DBusServer&& ) = default;
-  DBusServer& operator=( const DBusServer& ) = delete;
+  DBusServer& operator=(DBusServer&&) = default;
+  DBusServer& operator=(const DBusServer&) = delete;
 
   /**
    * @brief Registers interface on given path name
@@ -3189,7 +3490,7 @@ public:
    * @param dscr
    * @param fallback
    */
-  void addInterface( const std::string& pathName, DBusInterfaceDescription& dscr, bool fallback = false );
+  void addInterface(const std::string& pathName, DBusInterfaceDescription& dscr, bool fallback = false);
 
   /**
    * @brief Gets bus name of the current connection (must be connected)
@@ -3211,14 +3512,13 @@ public:
    * @param signal identifier of the signal
    * @param args values to emit
    */
-  template < typename... ARGS >
-  void emit2( const std::string& path, const std::string& interfaceName,
-              const std::string& signalName, const ARGS&... args )
+  template<typename... ARGS>
+  void emit2(const std::string& path, const std::string& interfaceName, const std::string& signalName, const ARGS&... args)
   {
-    auto msg = DBUS_W->eldbus_message_signal_new_impl( path, interfaceName, signalName );
+    auto           msg = DBUS_W->eldbus_message_signal_new_impl(path, interfaceName, signalName);
     detail::CallId id;
-    detail::packValues( id, msg, args... );
-    DBUS_W->eldbus_connection_send_impl( connection, msg );
+    detail::packValues(id, msg, args...);
+    DBUS_W->eldbus_connection_send_impl(connection, msg);
   }
 
   /**
@@ -3242,7 +3542,10 @@ public:
    * };
    * \endcode
    */
-  static std::string getCurrentObjectPath() { return currentObjectPath; }
+  static std::string getCurrentObjectPath()
+  {
+    return currentObjectPath;
+  }
 
   /**
    * @brief Returns current connection object, when handling call to property / method
@@ -3265,68 +3568,73 @@ public:
    * };
    * \endcode
    */
-  static const DBusWrapper::ConnectionPtr &getCurrentConnection() { return currentConnection; }
+  static const DBusWrapper::ConnectionPtr& getCurrentConnection()
+  {
+    return currentConnection;
+  }
 
   /// \cond
   class CurrentObjectSetter
   {
   public:
-    CurrentObjectSetter( DBusWrapper::ConnectionPtr con, std::string path )
+    CurrentObjectSetter(DBusWrapper::ConnectionPtr con, std::string path)
     {
       currentObjectPath = std::move(path);
-      currentConnection = std::move( con );
+      currentConnection = std::move(con);
     }
     ~CurrentObjectSetter()
     {
       currentObjectPath = "";
       currentConnection = {};
     }
-    CurrentObjectSetter( const CurrentObjectSetter& ) = delete;
-    CurrentObjectSetter( CurrentObjectSetter&& ) = delete;
-    void operator=( const CurrentObjectSetter& ) = delete;
-    void operator=( CurrentObjectSetter&& ) = delete;
+    CurrentObjectSetter(const CurrentObjectSetter&) = delete;
+    CurrentObjectSetter(CurrentObjectSetter&&)      = delete;
+    void operator=(const CurrentObjectSetter&) = delete;
+    void operator=(CurrentObjectSetter&&) = delete;
   };
   /// \endcond
 
 private:
   /// \cond
   DBusWrapper::ConnectionPtr connection;
-  struct DestructorObject {
+  struct DestructorObject
+  {
     std::vector<std::function<void()>> destructors;
-    ~DestructorObject() {
-      for(auto &a : destructors) a();
+    ~DestructorObject()
+    {
+      for(auto& a : destructors) a();
     }
   };
 
-  std::unique_ptr<DestructorObject> destructorObject { new DestructorObject() };
-  static thread_local std::string currentObjectPath;
+  std::unique_ptr<DestructorObject>              destructorObject{new DestructorObject()};
+  static thread_local std::string                currentObjectPath;
   static thread_local DBusWrapper::ConnectionPtr currentConnection;
 
   /// \endcond
 };
 
 /// \cond
-DBusWrapper::ConnectionPtr getDBusConnectionByType( ConnectionType tp );
-DBusWrapper::ConnectionPtr getDBusConnectionByName( const std::string& name );
-std::string getConnectionName( const DBusWrapper::ConnectionPtr& );
+DBusWrapper::ConnectionPtr getDBusConnectionByType(ConnectionType tp);
+DBusWrapper::ConnectionPtr getDBusConnectionByName(const std::string& name);
+std::string                getConnectionName(const DBusWrapper::ConnectionPtr&);
 /// \endcond
-}
+} // namespace DBus
 
 /// \cond
 namespace std
 {
-template < size_t INDEX, typename... ARGS >
-inline auto get( DBus::ValueOrError< ARGS... >& v ) -> decltype( std::get< INDEX >( v.getValues() ) ) &
+template<size_t INDEX, typename... ARGS>
+inline auto get(DBus::ValueOrError<ARGS...>& v) -> decltype(std::get<INDEX>(v.getValues()))&
 {
-  return std::get< INDEX >( v.getValues() );
+  return std::get<INDEX>(v.getValues());
 }
 
-template < size_t INDEX, typename... ARGS >
-inline auto get( const DBus::ValueOrError< ARGS... >& v ) -> decltype( std::get< INDEX >( v.getValues() ) )
+template<size_t INDEX, typename... ARGS>
+inline auto get(const DBus::ValueOrError<ARGS...>& v) -> decltype(std::get<INDEX>(v.getValues()))
 {
-  return std::get< INDEX >( v.getValues() );
-}
+  return std::get<INDEX>(v.getValues());
 }
+} // namespace std
 /// \endcond
 
 #endif // DALI_INTERNAL_ACCESSIBILITY_BRIDGE_DBUS_H
index 1d8d611..f65a7c4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 namespace Dali
 {
-
 namespace Toolkit
 {
-
 namespace Internal
 {
-
-DummyVisualPtr DummyVisual::New( const Property::Map& properties )
+DummyVisualPtr DummyVisual::New(const Property::Map& properties)
 {
   VisualFactoryCache* factoryCache = new VisualFactoryCache(false);
 
-  DummyVisualPtr dummyVisualPtr( new DummyVisual( *factoryCache ) );
+  DummyVisualPtr dummyVisualPtr(new DummyVisual(*factoryCache));
   dummyVisualPtr->Initialize();
   return dummyVisualPtr;
 }
 
-DummyVisual::DummyVisual( VisualFactoryCache& factoryCache )
-: Visual::Base( factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::Type::COLOR ),
-  mActionCounter( 0 )
+DummyVisual::DummyVisual(VisualFactoryCache& factoryCache)
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::Type::COLOR),
+  mActionCounter(0)
 {
 }
 
@@ -48,17 +45,17 @@ void DummyVisual::OnInitialize()
   // Implement if required
 }
 
-void DummyVisual::DoCreatePropertyMap( Property::Map& map ) const
+void DummyVisual::DoCreatePropertyMap(Property::Map& map) const
 {
   // Implement if required
 }
 
-void DummyVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
+void DummyVisual::DoCreateInstancePropertyMap(Property::Map& map) const
 {
   // Implement if required
 }
 
-void DummyVisual::DoSetProperties( const Property::Map& propertyMap )
+void DummyVisual::DoSetProperties(const Property::Map& propertyMap)
 {
   // Implement if required
 }
@@ -68,16 +65,25 @@ void DummyVisual::OnSetTransform()
   // Implement if required
 }
 
-void DummyVisual::DoSetOnScene( Actor& actor )
+void DummyVisual::DoSetOnScene(Actor& actor)
 {
   // Implement if required
 }
 
-void DummyVisual::OnDoAction( const Property::Index actionName, const Property::Value& attributes )
+void DummyVisual::OnDoAction(const Property::Index actionName, const Property::Value& attributes)
+{
+  if(DummyVisual::TEST_ACTION == actionName)
+  {
+    mActionCounter++; // GetActionCounter can be used to test for this.
+  }
+  // Further Actions can be added here
+}
+
+void DummyVisual::OnDoActionExtension(const Property::Index actionName, const Dali::Any& attributes)
 {
-  if ( DummyVisual::TEST_ACTION == actionName )
+  if(DummyVisual::TEST_ACTION_EXTENSION == actionName)
   {
-    mActionCounter++;  // GetActionCounter can be used to test for this.
+    mActionCounter++; // GetActionCounter can be used to test for this.
   }
   // Further Actions can be added here
 }
@@ -92,7 +98,7 @@ void DummyVisual::ResetActionCounter()
   mActionCounter = 0;
 }
 
-} // Internal
+} // namespace Internal
 
 } // namespace Toolkit
 
index 41b2c21..f52fca7 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEST_DUMMY_VISUAL_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,8 +20,8 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/dali-toolkit.h>
-#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-base.h>
+#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 
 
 namespace Dali
 {
-
 namespace Toolkit
 {
-
 namespace Internal
 {
-
 class DummyVisual;
 
-typedef IntrusivePtr< DummyVisual > DummyVisualPtr;
+typedef IntrusivePtr<DummyVisual> DummyVisualPtr;
 
 /**
  * Dummy Visual that can be used for testing
@@ -48,23 +45,22 @@ typedef IntrusivePtr< DummyVisual > DummyVisualPtr;
 class DummyVisual : public Visual::Base
 {
 public:
-
   // Actions that the dummy visual can perform.  These actions are called through the Visual::Base::DoAction API.
   enum Type
   {
-    TEST_ACTION = 0,  ///< Updates the action counter
+    TEST_ACTION           = 0, ///< Updates the action counter
+    TEST_ACTION_EXTENSION = 1, ///< Updates the action counter when we call DoActionExtension
   };
 
 public:
-
   // Constructor for DummyVisual
-  static DummyVisualPtr New( const Property::Map& properties );
+  static DummyVisualPtr New(const Property::Map& properties);
 
   // Prevent default methods being used.
-  DummyVisual( const DummyVisual& dummyVisual ) = delete;
-  DummyVisual( const DummyVisual&& dummyVisual ) = delete;
-  DummyVisual& operator=( const DummyVisual& dummyVisual ) = delete;
-  DummyVisual& operator=( const DummyVisual&& dummyVisual ) = delete;
+  DummyVisual(const DummyVisual& dummyVisual)  = delete;
+  DummyVisual(const DummyVisual&& dummyVisual) = delete;
+  DummyVisual& operator=(const DummyVisual& dummyVisual) = delete;
+  DummyVisual& operator=(const DummyVisual&& dummyVisual) = delete;
 
   // Get the Action counter, action counter incremented with every successful Action
   unsigned int GetActionCounter() const;
@@ -72,24 +68,22 @@ public:
   void ResetActionCounter();
 
 protected:
-
-  DummyVisual( VisualFactoryCache& factoryCache );
+  DummyVisual(VisualFactoryCache& factoryCache);
 
   void OnInitialize() override;
-  void DoCreatePropertyMap( Property::Map& map ) const override;
-  void DoCreateInstancePropertyMap( Property::Map& map ) const override;
-  void DoSetProperties( const Property::Map& propertyMap ) override;
+  void DoCreatePropertyMap(Property::Map& map) const override;
+  void DoCreateInstancePropertyMap(Property::Map& map) const override;
+  void DoSetProperties(const Property::Map& propertyMap) override;
   void OnSetTransform() override;
-  void DoSetOnScene( Actor& actor ) override;
-  void OnDoAction( const Property::Index actionName, const Property::Value& attributes ) override;
+  void DoSetOnScene(Actor& actor) override;
+  void OnDoAction(const Property::Index actionName, const Property::Value& attributes) override;
+  void OnDoActionExtension(const Property::Index actionName, const Dali::Any& attributes) override;
 
 private:
   unsigned int mActionCounter;
-
 };
 
-
-} // Internal
+} // namespace Internal
 
 } // namespace Toolkit
 
index 7db4632..088d5d8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -146,6 +146,10 @@ void CreateTextModel(const std::string&                text,
   textModel->mEllipsisPosition = ellipsisPosition;
   textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
 
+  //Inset
+  textModel->mRemoveFrontInset = true;
+  textModel->mRemoveBackInset = true;
+
   // 1) Convert to utf32
   Vector<Character>& utf32Characters = logicalModel->mText;
   utf32Characters.Resize(textSize);
@@ -159,7 +163,9 @@ void CreateTextModel(const std::string&                text,
   Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
   lineBreakInfo.Resize(characterCount);
 
-  SetLineBreakInfo(utf32Characters,
+  TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
+  SetLineBreakInfo(segmentation,
+                   utf32Characters,
                    0u,
                    characterCount,
                    lineBreakInfo);
@@ -178,6 +184,8 @@ void CreateTextModel(const std::string&                text,
     CharacterIndex end                 = characterCount;
     LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
 
+    TextAbstraction::Hyphenation hyphenation = TextAbstraction::Hyphenation::Get();
+
     for(CharacterIndex index = 0; index < end; index++)
     {
       CharacterIndex wordEnd = index;
@@ -191,7 +199,7 @@ void CreateTextModel(const std::string&                text,
         wordEnd++;
       }
 
-      Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
+      Vector<bool> hyphens = GetWordHyphens(hyphenation, utf32Characters.Begin() + index, wordEnd - index, nullptr);
 
       for(CharacterIndex i = 0; i < (wordEnd - index); i++)
       {
@@ -227,7 +235,8 @@ void CreateTextModel(const std::string&                text,
 
   // Validates the fonts. If there is a character with no assigned font it sets a default one.
   // After this call, fonts are validated.
-  multilanguageSupport.ValidateFonts(utf32Characters,
+  multilanguageSupport.ValidateFonts(fontClient,
+                                     utf32Characters,
                                      scripts,
                                      fontDescriptionRuns,
                                      fontDescription,
@@ -245,7 +254,11 @@ void CreateTextModel(const std::string&                text,
   Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
 
   // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
-  SetBidirectionalInfo(utf32Characters,
+
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
+  SetBidirectionalInfo(bidirectionalSupport,
+                       utf32Characters,
                        scripts,
                        lineBreakInfo,
                        0u,
@@ -261,14 +274,16 @@ void CreateTextModel(const std::string&                text,
   if(0u != bidirectionalInfo.Count())
   {
     // Only set the character directions if there is right to left characters.
-    GetCharactersDirection(bidirectionalInfo,
+    GetCharactersDirection(bidirectionalSupport,
+                           bidirectionalInfo,
                            characterCount,
                            0u,
                            characterCount,
                            characterDirections);
 
     // This paragraph has right to left text. Some characters may need to be mirrored.
-    textMirrored = GetMirroredText(utf32Characters,
+    textMirrored = GetMirroredText(bidirectionalSupport,
+                                   utf32Characters,
                                    characterDirections,
                                    bidirectionalInfo,
                                    0u,
@@ -290,7 +305,11 @@ void CreateTextModel(const std::string&                text,
 
   const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
 
-  ShapeText(textToShape,
+  TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
+
+  ShapeText(shaping,
+            fontClient,
+            textToShape,
             lineBreakInfo,
             scripts,
             validFonts,
@@ -340,7 +359,9 @@ void CreateTextModel(const std::string&                text,
   textModel->mHorizontalAlignment   = Text::HorizontalAlignment::BEGIN;
   textModel->mIgnoreSpacesAfterText = true;
   Layout::Parameters layoutParameters(textArea,
-                                      textModel);
+                                      textModel,
+                                      fontClient,
+                                      bidirectionalSupport);
 
   Vector<LineRun>& lines = visualModel->mLines;
 
index eee08a7..02ca1fa 100644 (file)
@@ -39,6 +39,7 @@ void utc_dali_toolkit_accessibility_accessible_startup(void)
 void utc_dali_toolkit_accessibility_accessible_cleanup(void)
 {
   test_return_value = TET_PASS;
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
 }
 
 int utcDaliAccessibilityCheckBoxButtonGetStates(void)
index ad2fba7..6d5acf5 100644 (file)
@@ -1,5 +1,6 @@
 #include <automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.h>
 #include <automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h>
+#include <automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-timer.h>
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/dali-toolkit.h>
 #include <dali-toolkit/devel-api/controls/buttons/toggle-button.h>
 #include <dali/devel-api/common/stage.h>
 #include <cstdlib>
 
-#include <automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h>
-
 using namespace Dali::Toolkit;
 
-void utc_dali_accessibility_controls_bridge_up_startup(void)
+namespace
+{
+const auto flushCoalescableMessage = [](Dali::ToolkitTestApplication& application) {
+  Dali::Timer timer = Timer::New(0);
+  for(int i = 0; i < 11; ++i)
+  {
+    application.SendNotification();
+    application.Render();
+    timer.MockEmitSignal();
+  }
+};
+}
+
+void utc_dali_toolkit_accessibility_control_bridgeup_startup(void)
 {
   test_return_value = TET_UNDEF;
   DBusWrapper::Install(std::unique_ptr<DBusWrapper>(new TestDBusWrapper));
 }
 
-void utc_dali_accessibility_controls_bridge_up_cleanup(void)
+void utc_dali_toolkit_accessibility_control_bridgeup_cleanup(void)
 {
   test_return_value = TET_PASS;
-  //DBusWrapper::Install({}) is a de-install
-  DBusWrapper::Install({});
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
 }
 
 namespace Dali
@@ -96,6 +107,7 @@ int UtcDaliControlAccessibilityName(void)
   DALI_TEST_EQUALS("Accessibility_Name_With_Callback", q->GetName(), TEST_LOCATION);
 
   Dali::Accessibility::TestEnableSC(true);
+  DALI_TEST_CHECK(!Dali::Accessibility::TestPropertyChangeCalled());
 
   DALI_TEST_EQUALS("Accessibility_Name_With_Callback", TestGetName(q->GetAddress()), TEST_LOCATION);
 
@@ -110,7 +122,9 @@ int UtcDaliControlAccessibilityName(void)
   DALI_TEST_EQUALS("Changed_Accessiblity_Name", q->GetName(), TEST_LOCATION);
   DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::ACCESSIBILITY_NAME).Get<std::string>(), "Changed_Accessiblity_Name", TEST_LOCATION);
 
-  //TODO test emission of name change signal
+  // test emission of property change signal
+  DALI_TEST_CHECK(Dali::Accessibility::TestPropertyChangeCalled());
+
   Dali::Accessibility::TestEnableSC(false);
 
   END_TEST;
@@ -140,6 +154,7 @@ int UtcDaliControlAccessibilityDescription(void)
   DALI_TEST_EQUALS("Accessibility_Description_With_Callback", q->GetDescription(), TEST_LOCATION);
 
   Dali::Accessibility::TestEnableSC(true);
+  DALI_TEST_CHECK(!Dali::Accessibility::TestPropertyChangeCalled());
 
   DALI_TEST_EQUALS("Accessibility_Description_With_Callback", TestGetDescription(q->GetAddress()), TEST_LOCATION);
 
@@ -154,7 +169,53 @@ int UtcDaliControlAccessibilityDescription(void)
   DALI_TEST_EQUALS("Changed_Accessiblity_Description", q->GetDescription(), TEST_LOCATION);
   DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::ACCESSIBILITY_DESCRIPTION).Get<std::string>(), "Changed_Accessiblity_Description", TEST_LOCATION);
 
-  //TODO test emission of description change signal
+  // test emission of property change signal
+  DALI_TEST_CHECK(Dali::Accessibility::TestPropertyChangeCalled());
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  END_TEST;
+}
+
+int UtcDaliControlAccessibilityValue(void)
+{
+  ToolkitTestApplication application;
+
+  auto control = Control::New();
+
+  auto q = Dali::Accessibility::Accessible::Get(control);
+  DALI_TEST_CHECK(q);
+
+  DALI_TEST_EQUALS("", q->GetValue(), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_VALUE, "Accessibility_Value");
+  DALI_TEST_EQUALS("Accessibility_Value", q->GetValue(), TEST_LOCATION);
+
+  auto property = control.GetProperty(DevelControl::Property::ACCESSIBILITY_VALUE).Get<std::string>();
+  DALI_TEST_EQUALS("Accessibility_Value", property, TEST_LOCATION);
+
+  Dali::Accessibility::TestEnableSC(true);
+  DALI_TEST_CHECK(!Dali::Accessibility::TestPropertyChangeCalled());
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_VALUE, "Changed_Accessiblity_Value");
+  DALI_TEST_EQUALS("Changed_Accessiblity_Value", q->GetValue(), TEST_LOCATION);
+  DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::ACCESSIBILITY_VALUE).Get<std::string>(), "Changed_Accessiblity_Value", TEST_LOCATION);
+
+  // value property change signal is not emitted if not highlighted
+  DALI_TEST_CHECK(!Dali::Accessibility::TestPropertyChangeCalled());
+
+  auto component = dynamic_cast<Dali::Accessibility::Component*>(q);
+
+  DALI_TEST_CHECK(component);
+  component->GrabHighlight();
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_VALUE, "Changed_Accessiblity_Value_2");
+  DALI_TEST_EQUALS("Changed_Accessiblity_Value_2", q->GetValue(), TEST_LOCATION);
+  DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::ACCESSIBILITY_VALUE).Get<std::string>(), "Changed_Accessiblity_Value_2", TEST_LOCATION);
+
+  // value property change signal is emitted if highlighted
+  DALI_TEST_CHECK(Dali::Accessibility::TestPropertyChangeCalled());
+
   Dali::Accessibility::TestEnableSC(false);
 
   END_TEST;
@@ -170,26 +231,115 @@ int UtcDaliControlAccessibilityRole(void)
 
   DALI_TEST_EQUALS(role_unknown, control.GetProperty(DevelControl::Property::ACCESSIBILITY_ROLE).Get<Dali::Accessibility::Role>(), TEST_LOCATION);
 
-  auto q = Dali::Accessibility::Accessible::Get(control);
-  DALI_TEST_EQUALS(role_unknown, q->GetRole(), TEST_LOCATION);
-  DALI_TEST_EQUALS("unknown", q->GetRoleName(), TEST_LOCATION);
+  auto accessible = Dali::Accessibility::Accessible::Get(control);
+  DALI_TEST_EQUALS(role_unknown, accessible->GetRole(), TEST_LOCATION);
+  DALI_TEST_EQUALS("unknown", accessible->GetRoleName(), TEST_LOCATION);
 
   Dali::Accessibility::TestEnableSC(true);
-  DALI_TEST_CHECK(q);
-  DALI_TEST_EQUALS(static_cast<uint32_t>(role_unknown), TestGetRole(q->GetAddress()), TEST_LOCATION);
-  DALI_TEST_EQUALS("unknown", TestGetRoleName(q->GetAddress()), TEST_LOCATION);
-  DALI_TEST_EQUALS("unknown", TestGetLocalizedRoleName(q->GetAddress()), TEST_LOCATION);
+  DALI_TEST_CHECK(accessible);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(role_unknown), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+  DALI_TEST_EQUALS("unknown", TestGetRoleName(accessible->GetAddress()), TEST_LOCATION);
+  DALI_TEST_EQUALS("unknown", TestGetLocalizedRoleName(accessible->GetAddress()), TEST_LOCATION);
+
+  // Invalid role
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, 9999);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::UNKNOWN), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  // V2 Roles
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::ADJUSTABLE);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::SLIDER), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::ALERT);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::ALERT), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
 
-  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, role_pushbutton);
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::BUTTON);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::PUSH_BUTTON), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
 
-  DALI_TEST_EQUALS(static_cast<uint32_t>(role_pushbutton), TestGetRole(q->GetAddress()), TEST_LOCATION);
-  DALI_TEST_EQUALS("push button", TestGetRoleName(q->GetAddress()), TEST_LOCATION);
-  DALI_TEST_EQUALS("push button", TestGetLocalizedRoleName(q->GetAddress()), TEST_LOCATION);
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::CHECK_BOX);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::CHECK_BOX), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::COMBO_BOX);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::COMBO_BOX), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::CONTAINER);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::FILLER), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::DIALOG);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::DIALOG), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::ENTRY);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::ENTRY), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::HEADER);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::HEADER), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::IMAGE);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::IMAGE), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::LINK);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::LINK), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::LIST);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::LIST), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::LIST_ITEM);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::LIST_ITEM), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::MENU);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::MENU), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::MENU_BAR);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::MENU_BAR), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::MENU_ITEM);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::MENU_ITEM), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::NONE);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::UNKNOWN), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::PASSWORD_TEXT);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::PASSWORD_TEXT), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::POPUP_MENU);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::POPUP_MENU), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::PROGRESS_BAR);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::PROGRESS_BAR), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::RADIO_BUTTON);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::RADIO_BUTTON), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::SCROLL_BAR);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::SCROLL_BAR), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::SPIN_BUTTON);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::SPIN_BUTTON), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::TAB);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::PAGE_TAB), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::TAB_LIST);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::PAGE_TAB_LIST), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::TEXT);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::TEXT), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::TOGGLE_BUTTON);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::TOGGLE_BUTTON), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::TOOL_BAR);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(Dali::Accessibility::Role::TOOL_BAR), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+
+  // Directly sets ATSPI role  (V1)
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::PUSH_BUTTON);
+  DALI_TEST_EQUALS(static_cast<uint32_t>(role_pushbutton), TestGetRole(accessible->GetAddress()), TEST_LOCATION);
+  DALI_TEST_EQUALS("push button", TestGetRoleName(accessible->GetAddress()), TEST_LOCATION);
+  DALI_TEST_EQUALS("push button", TestGetLocalizedRoleName(accessible->GetAddress()), TEST_LOCATION);
 
   Dali::Accessibility::TestEnableSC(false);
 
-  DALI_TEST_EQUALS(role_pushbutton, q->GetRole(), TEST_LOCATION);
-  DALI_TEST_EQUALS("push button", q->GetRoleName(), TEST_LOCATION);
+  DALI_TEST_EQUALS(role_pushbutton, accessible->GetRole(), TEST_LOCATION);
+  DALI_TEST_EQUALS("push button", accessible->GetRoleName(), TEST_LOCATION);
 
   END_TEST;
 }
@@ -261,13 +411,156 @@ int UtcDaliControlAccessibilityState(void)
   ToolkitTestApplication application;
 
   auto control = Control::New();
-  auto q       = Dali::Accessibility::Accessible::Get(control);
+  control.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+  control.SetProperty(Actor::Property::POSITION, Vector3(10, 10, 100));
+
+  application.GetScene().Add(control);
+  auto accessible = Dali::Accessibility::Accessible::Get(control);
 
   Dali::Accessibility::TestEnableSC(true);
+  DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
 
-  auto states_by_bridge = Dali::Accessibility::States{TestGetStates(q->GetAddress())};
-  auto states           = DevelControl::GetAccessibilityStates(control);
-  DALI_TEST_CHECK(states_by_bridge == states);
+  // Test setting AccessibilityState property updates at-spi states
+  DevelControl::AccessibilityStates inputStates;
+  {
+    inputStates[DevelControl::AccessibilityState::ENABLED] = false;
+    inputStates[DevelControl::AccessibilityState::CHECKED] = true;
+    inputStates[DevelControl::AccessibilityState::BUSY]    = true;
+
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+
+    auto states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::ENABLED]);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::SELECTED]);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::CHECKED]);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::BUSY]);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::EXPANDED]);
+  }
+
+  // state-changed:checked event is NOT emitted if the object is not highlighted
+  {
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::CHECK_BOX);
+
+    inputStates[DevelControl::AccessibilityState::CHECKED] = false; // CHECKED: true -> false
+
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+
+    auto states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::ENABLED]);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::SELECTED]);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::CHECKED]);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::BUSY]);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::EXPANDED]);
+  }
+
+  auto component = dynamic_cast<Dali::Accessibility::Component*>(accessible);
+  component->GrabHighlight();
+
+  // state-changed:checked event is emitted if the object is highlighted and checkable
+  const std::array<DevelControl::AccessibilityRole, 3> checkableRoles{DevelControl::AccessibilityRole::CHECK_BOX, DevelControl::AccessibilityRole::RADIO_BUTTON, DevelControl::AccessibilityRole::TOGGLE_BUTTON};
+  for(auto role : checkableRoles)
+  {
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, role);
+
+    // CHECKED: false -> true
+    inputStates[DevelControl::AccessibilityState::CHECKED] = true;
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("checked", 1));
+
+    auto states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::CHECKED]);
+
+    Dali::Accessibility::TestResetStateChangedResult();
+    flushCoalescableMessage(application);
+
+    // CHECKED: true -> false
+    inputStates[DevelControl::AccessibilityState::CHECKED] = false;
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("checked", 0));
+
+    states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::CHECKED]);
+
+    Dali::Accessibility::TestResetStateChangedResult();
+    flushCoalescableMessage(application);
+  }
+
+  // state-changed:selected event is emitted if the object is highlighted and selectable
+  const std::array<DevelControl::AccessibilityRole, 3> selectableRoles{DevelControl::AccessibilityRole::BUTTON, DevelControl::AccessibilityRole::LIST_ITEM, DevelControl::AccessibilityRole::MENU_ITEM};
+  for(auto role : selectableRoles)
+  {
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, role);
+
+    // SELECTED: false -> true
+    inputStates[DevelControl::AccessibilityState::SELECTED] = true;
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("selected", 1));
+
+    auto states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::SELECTED]);
+
+    Dali::Accessibility::TestResetStateChangedResult();
+    flushCoalescableMessage(application);
+
+    // SELECTED: true -> false
+    inputStates[DevelControl::AccessibilityState::SELECTED] = false;
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+    DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("selected", 0));
+
+    states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::SELECTED]);
+    Dali::Accessibility::TestResetStateChangedResult();
+    flushCoalescableMessage(application);
+  }
+
+  // state-changed event is NOT emitted if object is not checkable or selectable
+  {
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::CONTAINER);
+
+    inputStates[DevelControl::AccessibilityState::CHECKED]  = true; // CHECKED: false -> true
+    inputStates[DevelControl::AccessibilityState::SELECTED] = true; // SELECTED: false -> true
+
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+
+    auto states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::SELECTED]);
+    DALI_TEST_CHECK(states[Dali::Accessibility::State::CHECKED]);
+  }
+
+  // state-changed event is NOT emitted if object is v1 role
+  {
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::CHECK_BOX);
+
+    inputStates[DevelControl::AccessibilityState::CHECKED] = false; // CHECKED: true -> false
+
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_STATES, static_cast<int32_t>(inputStates.GetRawData32()));
+
+    DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+
+    auto states = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(!states[Dali::Accessibility::State::CHECKED]);
+  }
+
+  // Test bridge behavior
+  {
+    auto states_by_bridge = Dali::Accessibility::States{TestGetStates(accessible->GetAddress())};
+    auto states           = DevelControl::GetAccessibilityStates(control);
+    DALI_TEST_CHECK(states_by_bridge == states);
+  }
 
   Dali::Accessibility::TestEnableSC(false);
 
@@ -278,13 +571,27 @@ int UtcDaliControlAccessibilityModal(void)
 {
   ToolkitTestApplication application;
 
-  auto control = Dali::Toolkit::Popup::New();
-  auto q       = Dali::Accessibility::Accessible::Get(control);
-
   Dali::Accessibility::TestEnableSC(true);
 
-  auto states_by_bridge = Dali::Accessibility::States{TestGetStates(q->GetAddress())};
-  DALI_TEST_CHECK(states_by_bridge[Dali::Accessibility::State::MODAL]);
+  // Modal state is set by Dialog role
+  {
+    auto control    = Dali::Toolkit::Popup::New();
+    auto accessible = Dali::Accessibility::Accessible::Get(control);
+
+    auto states_by_bridge = Dali::Accessibility::States{TestGetStates(accessible->GetAddress())};
+    DALI_TEST_CHECK(states_by_bridge[Dali::Accessibility::State::MODAL]);
+  }
+
+  // Modal state is set by isModal property
+  {
+    auto control = Control::New();
+    control.SetProperty(DevelControl::Property::ACCESSIBILITY_IS_MODAL, true);
+
+    auto accessible = Dali::Accessibility::Accessible::Get(control);
+
+    auto states_by_bridge = Dali::Accessibility::States{TestGetStates(accessible->GetAddress())};
+    DALI_TEST_CHECK(states_by_bridge[Dali::Accessibility::State::MODAL]);
+  }
 
   Dali::Accessibility::TestEnableSC(false);
 
@@ -325,6 +632,47 @@ int UtcDaliControlAccessibilityHighlightable(void)
   states_by_bridge = Dali::Accessibility::States{TestGetStates(q->GetAddress())};
   DALI_TEST_CHECK(!states_by_bridge[Dali::Accessibility::State::HIGHLIGHTABLE]);
 
+  // Highlightable state is set if V2 role is set and is not Role::None
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::CONTAINER);
+  states_by_bridge = Dali::Accessibility::States{TestGetStates(q->GetAddress())};
+  DALI_TEST_CHECK(states_by_bridge[Dali::Accessibility::State::HIGHLIGHTABLE]);
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  END_TEST;
+}
+
+int UtcDaliControlAccessibilityScrollable(void)
+{
+  ToolkitTestApplication application;
+  auto                   control = Control::New();
+
+  auto scrollable = control.GetProperty<bool>(DevelControl::Property::ACCESSIBILITY_SCROLLABLE);
+  DALI_TEST_EQUALS(scrollable, false, TEST_LOCATION);
+
+  // negative testcase - trying to set unconvertible value
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE, "deadbeef");
+  scrollable = control.GetProperty<bool>(DevelControl::Property::ACCESSIBILITY_SCROLLABLE);
+  DALI_TEST_EQUALS(scrollable, false, TEST_LOCATION);
+
+  auto accessible = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(control));
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  DALI_TEST_CHECK(!accessible->IsScrollable());
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE, true);
+  DALI_TEST_EQUALS(Property::BOOLEAN, control.GetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE).GetType(), TEST_LOCATION);
+  DALI_TEST_EQUALS(true, control.GetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE).Get<bool>(), TEST_LOCATION);
+
+  DALI_TEST_CHECK(accessible->IsScrollable());
+
+  control.SetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE, false);
+  DALI_TEST_EQUALS(Property::BOOLEAN, control.GetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE).GetType(), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, control.GetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE).Get<bool>(), TEST_LOCATION);
+
+  DALI_TEST_CHECK(!accessible->IsScrollable());
+
   Dali::Accessibility::TestEnableSC(false);
 
   END_TEST;
@@ -945,10 +1293,10 @@ int UtcDaliAccessibilityAction(void)
   auto a       = Dali::Accessibility::Accessible::Get(control);
   auto b       = dynamic_cast<Dali::Accessibility::Action*>(a);
 
-  std::vector<std::string> actions{"activate", "accessibilityActivated", "ReadingSkipped", "ReadingCancelled", "ReadingStopped", "ReadingPaused", "ReadingResumed", "show", "hide"};
+  std::vector<std::string> actions{"activate", "escape", "increment", "decrement", "ReadingSkipped", "ReadingCancelled", "ReadingStopped", "ReadingPaused", "ReadingResumed", "show", "hide"};
   auto                     count = b->GetActionCount();
 
-  DALI_TEST_EQUALS(count, 9, TEST_LOCATION);
+  DALI_TEST_EQUALS(count, 11, TEST_LOCATION);
 
   for(auto i = 0u; i < count; ++i)
   {
@@ -966,7 +1314,7 @@ int UtcDaliAccessibilityAction(void)
 
   count = TestGetActionCount(b->GetAddress());
 
-  DALI_TEST_EQUALS(count, 9, TEST_LOCATION);
+  DALI_TEST_EQUALS(count, 11, TEST_LOCATION);
 
   for(auto i = 0u; i < count; ++i)
   {
@@ -988,79 +1336,126 @@ int UtcDaliAccessibilityAction(void)
 
 int UtcDaliAccessibilityDoAction(void)
 {
+  using Dali::Accessibility::ActionType;
+
   ToolkitTestApplication application;
 
   Dali::Accessibility::TestEnableSC(true);
-  thread_local std::vector<bool> actions_done{false, false, false, false, false, false};
+  thread_local std::vector<ActionType> actions_done;
+  thread_local std::vector<bool>       legacy_actions_done(5, false);
+
+  const auto reset_results = [&]() {
+    actions_done.clear();
+    std::fill(legacy_actions_done.begin(), legacy_actions_done.end(), false);
+  };
+
+  const auto check_actions_done = [&](std::vector<ActionType> actions_sent) {
+    for(ActionType action : actions_sent)
+    {
+      auto it = std::find(actions_done.begin(), actions_done.end(), action);
+      DALI_TEST_CHECK(it != actions_done.end());
+    }
+  };
+
+  const auto check_all_actions_done_and_reset = [&]() {
+    check_actions_done({ActionType::ACTIVATE, ActionType::ESCAPE, ActionType::INCREMENT, ActionType::DECREMENT});
+    DALI_TEST_CHECK(std::all_of(legacy_actions_done.begin(), legacy_actions_done.end(), [](bool x) { return x == true; }));
+    reset_results();
+  };
 
   auto                     control = Control::New();
   auto                     a       = Dali::Accessibility::Accessible::Get(control);
   auto                     b       = dynamic_cast<Dali::Accessibility::Action*>(a);
-  std::vector<std::string> actions{"activate", "accessibilityActivated", "ReadingSkipped", "ReadingCancelled", "ReadingStopped", "ReadingPaused", "ReadingResumed", "show", "hide"};
-
-  // Test calling action by name
-  DALI_TEST_CHECK(b->DoAction(actions[2])); // ReadingSkipped
-  DALI_TEST_CHECK(b->DoAction(actions[4])); // ReadingStopped
-  DALI_TEST_CHECK(b->DoAction(actions[4])); // ReadingStopped
+  std::vector<std::string> actions{"activate", "escape", "increment", "decrement", "ReadingSkipped", "ReadingCancelled", "ReadingStopped", "ReadingPaused", "ReadingResumed"};
 
   // Negative test of calling action with not defined name
   DALI_TEST_CHECK(!b->DoAction("undefined"));
 
+  // Returns fail if no signal is connected
+  DALI_TEST_CHECK(!b->DoAction(actions[0])); // activate
+  DALI_TEST_CHECK(!b->DoAction(actions[1])); // escape
+  DALI_TEST_CHECK(!b->DoAction(actions[2])); // increment
+  DALI_TEST_CHECK(!b->DoAction(actions[3])); // decrement
+
+  DevelControl::AccessibilityActionSignal(control).Connect([](const Dali::Accessibility::ActionInfo& action_info) {
+    actions_done.push_back(action_info.type);
+    return true;
+  });
   DevelControl::AccessibilityReadingSkippedSignal(control).Connect([]() {
-    actions_done[1] = true;
+    legacy_actions_done[0] = true;
   });
   DevelControl::AccessibilityReadingCancelledSignal(control).Connect([]() {
-    actions_done[2] = true;
+    legacy_actions_done[1] = true;
   });
   DevelControl::AccessibilityReadingStoppedSignal(control).Connect([]() {
-    actions_done[3] = true;
+    legacy_actions_done[2] = true;
   });
   DevelControl::AccessibilityReadingPausedSignal(control).Connect([]() {
-    actions_done[4] = true;
+    legacy_actions_done[3] = true;
   });
   DevelControl::AccessibilityReadingResumedSignal(control).Connect([]() {
-    actions_done[5] = true;
-  });
-  DevelControl::AccessibilityActivateSignal(control).Connect([]() {
-    actions_done[0] = true;
+    legacy_actions_done[4] = true;
   });
 
   // Test calling action by index
-  DALI_TEST_CHECK(b->DoAction(1));
-  DALI_TEST_CHECK(b->DoAction(2));
-  DALI_TEST_CHECK(b->DoAction(3));
-  DALI_TEST_CHECK(b->DoAction(4));
-  DALI_TEST_CHECK(b->DoAction(5));
-  DALI_TEST_CHECK(b->DoAction(6));
-
-  for(auto i = 0u; i < actions_done.size(); ++i)
+  for(size_t i = 0; i < actions.size(); ++i)
   {
-    DALI_TEST_CHECK(actions_done[i]);
-    actions_done[i] = false;
+    DALI_TEST_CHECK(b->DoAction(i));
   }
 
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), 1));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), 2));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), 3));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), 4));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), 5));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), 6));
+  check_all_actions_done_and_reset();
 
-  for(auto i = 0u; i < actions_done.size(); ++i)
+  // Test calling action by name
+  for(size_t i = 0; i < actions.size(); ++i)
   {
-    DALI_TEST_CHECK(actions_done[i]);
-    actions_done[i] = false;
+    DALI_TEST_CHECK(b->DoAction(actions[i]));
   }
 
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[1]));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[2]));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[3]));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[4]));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[5]));
-  DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[6]));
+  check_all_actions_done_and_reset();
+
+  // Test "DoAction" through d-bus call
+  for(size_t i = 0; i < actions.size(); ++i)
+  {
+    DALI_TEST_CHECK(TestDoAction(b->GetAddress(), i));
+  }
 
-  for(auto i = 0u; i < actions_done.size(); ++i)
-    DALI_TEST_CHECK(actions_done[i]);
+  check_all_actions_done_and_reset();
+
+  // Test "DoActionName" through d-bus call
+  for(size_t i = 0; i < actions.size(); ++i)
+  {
+    DALI_TEST_CHECK(TestDoAction(b->GetAddress(), actions[i]));
+  }
+
+  check_all_actions_done_and_reset();
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  END_TEST;
+}
+
+int UtcDaliAccessibilityActivateFallbackToLegacy(void)
+{
+  using Dali::Accessibility::ActionType;
+
+  ToolkitTestApplication application;
+
+  Dali::Accessibility::TestEnableSC(true);
+  thread_local std::vector<ActionType> actions_done;
+  thread_local bool                    legacy_activate_done = false;
+
+  auto control = Control::New();
+  auto a       = Dali::Accessibility::Accessible::Get(control);
+  auto b       = dynamic_cast<Dali::Accessibility::Action*>(a);
+
+  DevelControl::AccessibilityActivateSignal(control).Connect([]() {
+    legacy_activate_done = true;
+  });
+
+  DALI_TEST_CHECK(b->DoAction("activate"));   // fallback to legacy "activate" when ActionSignal is not connected
+  DALI_TEST_CHECK(!b->DoAction("increment")); // "increment" does not exist in legacy actions
+
+  DALI_TEST_CHECK(legacy_activate_done);
 
   Dali::Accessibility::TestEnableSC(false);
 
@@ -1155,6 +1550,115 @@ int UtcDaliAccessibilityScrollToChildScrollView(void)
   END_TEST;
 }
 
+int UtcDaliAccessibilityScrollToChildCustomScrollable(void)
+{
+  ToolkitTestApplication application;
+
+  thread_local Dali::Accessibility::ActionInfo action_done;
+  const auto                                   check_scroll_to_child_action_done_and_reset = [&](Actor child) {
+    DALI_TEST_CHECK(action_done.type == Dali::Accessibility::ActionType::SCROLL_TO_CHILD);
+    DALI_TEST_CHECK(action_done.target == child);
+    action_done = Dali::Accessibility::ActionInfo{};
+  };
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  auto scrollable = Control::New();
+  // set control as scrollable
+  scrollable.SetProperty(DevelControl::Property::ACCESSIBILITY_SCROLLABLE, true);
+
+  DevelControl::AccessibilityActionSignal(scrollable).Connect([](const Dali::Accessibility::ActionInfo& action_info) {
+    action_done = action_info;
+    return true;
+  });
+
+  application.GetScene().Add(scrollable);
+
+  PushButton          actorA    = PushButton::New();
+  const Dali::Vector3 positionA = Vector3(100.0f, 400.0f, 0.0f);
+  actorA.SetProperty(Dali::Actor::Property::POSITION, positionA);
+  scrollable.Add(actorA);
+
+  PushButton          actorB    = PushButton::New();
+  const Dali::Vector3 positionB = Vector3(500.0f, 200.0f, 0.0f);
+  actorB.SetProperty(Dali::Actor::Property::POSITION, positionB);
+  scrollable.Add(actorB);
+
+  TableView tableView = TableView::New(2, 2); // 2 by 2 grid.
+  tableView.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  scrollable.Add(tableView);
+
+  PushButton actorC = PushButton::New();
+  actorC.SetProperty(Actor::Property::SIZE, Vector2(50.0f, 50.0f));
+  tableView.AddChild(actorC, TableView::CellPosition(0, 0));
+
+  PushButton actorD = PushButton::New();
+  application.GetScene().Add(actorD);
+
+  Wait(application);
+
+  auto* accessibleParent = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(scrollable));
+  DALI_TEST_CHECK(accessibleParent);
+  auto* accessibleA = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(actorA));
+  DALI_TEST_CHECK(accessibleA);
+  auto* accessibleB = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(actorB));
+  DALI_TEST_CHECK(accessibleB);
+  auto* accessibleC = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(actorC));
+  DALI_TEST_CHECK(accessibleC);
+  auto* accessibleD = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(actorD));
+  DALI_TEST_CHECK(accessibleD);
+
+  accessibleA->GrabHighlight();
+  Wait(application);
+  check_scroll_to_child_action_done_and_reset(actorA);
+
+  accessibleB->GrabHighlight();
+  Wait(application);
+  check_scroll_to_child_action_done_and_reset(actorB);
+
+  // scrollable is ancestor of actorC
+  // This should work without a crash
+  accessibleC->GrabHighlight();
+  check_scroll_to_child_action_done_and_reset(actorC);
+
+  // Grabbing highlight on a non-child actor to scrollable does not emit SCROLL_TO_CHILD
+  accessibleD->GrabHighlight();
+  DALI_TEST_CHECK(action_done.type == Dali::Accessibility::ActionType::MAX_COUNT);
+  DALI_TEST_CHECK(action_done.target != actorD);
+
+  Dali::Accessibility::TestEnableSC(false);
+  END_TEST;
+}
+
+int UtcDaliAccessibilityScrollToChild(void)
+{
+  ToolkitTestApplication application;
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  auto parent = Control::New();
+
+  auto                child    = Control::New();
+  const Dali::Vector3 position = Vector3(100.0f, 400.0f, 0.0f);
+  child.SetProperty(Dali::Actor::Property::POSITION, position);
+
+  auto* accessibleParent = dynamic_cast<DevelControl::ControlAccessible*>(Dali::Accessibility::Accessible::Get(parent));
+  DALI_TEST_CHECK(accessibleParent);
+
+  // ScrollToChild fails if no ActionSignal is connected
+  DALI_TEST_CHECK(!accessibleParent->ScrollToChild(child));
+
+  DevelControl::AccessibilityActionSignal(parent).Connect([](const Dali::Accessibility::ActionInfo& action_info) {
+    return true;
+  });
+
+  // ScrollToChild succeeds is an ActionSinal is connected
+  DALI_TEST_CHECK(accessibleParent->ScrollToChild(child));
+
+  Dali::Accessibility::TestEnableSC(false);
+  END_TEST;
+}
+
 namespace
 {
 class TestItemFactory : public ItemFactory
@@ -1354,10 +1858,17 @@ int UtcDaliWebViewAccessible(void)
 
   DALI_TEST_CHECK(children.empty());
 
+  // Enables accessibility
   Dali::Accessibility::TestEnableSC(true);
 
+  // Assuming the webengine lazy sets accessibility address on LoadUrl
   children = webViewAccessible->GetChildren();
+  DALI_TEST_CHECK(children.empty());
 
+  // our test webengine sets accessibility address here
+  webView.LoadUrl("http://www.somewhere.valid1.com");
+
+  children = webViewAccessible->GetChildren();
   DALI_TEST_EQUALS(children.size(), 1u, TEST_LOCATION);
 
   auto* child = children[0];
@@ -1379,3 +1890,173 @@ int UtcDaliWebViewAccessible(void)
 
   END_TEST;
 }
+
+int UtcDaliEmitAccessibilityStateChanged(void)
+{
+  ToolkitTestApplication application;
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  auto root = Control::New();
+  root.SetProperty(Actor::Property::SIZE, Vector2(300, 300));
+  root.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::CONTAINER);
+
+  auto dialog = Control::New();
+  dialog.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+  dialog.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::DIALOG);
+  root.Add(dialog);
+
+  auto button = Control::New();
+  button.SetProperty(Actor::Property::SIZE, Vector2(20, 20));
+  button.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::BUTTON);
+  root.Add(button);
+
+  auto rootAccessible   = dynamic_cast<DevelControl::ControlAccessible*>(Accessibility::Accessible::Get(root));
+  auto dialogAccessible = dynamic_cast<DevelControl::ControlAccessible*>(Accessibility::Accessible::Get(dialog));
+  auto buttonAccessible = dynamic_cast<DevelControl::ControlAccessible*>(Accessibility::Accessible::Get(button));
+
+  application.GetScene().Add(root);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(Accessibility::Bridge::GetCurrentBridge()->GetDefaultLabel(rootAccessible) != dialogAccessible);
+
+  // modal role: Showing State is emitted and Default label is registered
+  DevelControl::EmitAccessibilityStateChanged(dialog, Accessibility::State::SHOWING, 1);
+
+  DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+  DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("showing", 1));
+  DALI_TEST_CHECK(Accessibility::Bridge::GetCurrentBridge()->GetDefaultLabel(rootAccessible) == dialogAccessible);
+
+  Dali::Accessibility::TestResetStateChangedResult();
+  flushCoalescableMessage(application);
+
+  // modal role: Showing State is emitted and Default label is unregistered
+  DevelControl::EmitAccessibilityStateChanged(dialog, Accessibility::State::SHOWING, 0);
+
+  DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+  DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("showing", 0));
+  DALI_TEST_CHECK(Accessibility::Bridge::GetCurrentBridge()->GetDefaultLabel(rootAccessible) != dialogAccessible);
+
+  Dali::Accessibility::TestResetStateChangedResult();
+  flushCoalescableMessage(application);
+
+  // modal role: Visible State is not emitted
+  DevelControl::EmitAccessibilityStateChanged(dialog, Accessibility::State::VISIBLE, 1);
+
+  DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+  DALI_TEST_CHECK(Accessibility::Bridge::GetCurrentBridge()->GetDefaultLabel(rootAccessible) != dialogAccessible);
+
+  Dali::Accessibility::TestResetStateChangedResult();
+  flushCoalescableMessage(application);
+
+  // non-modal role: Showing State is not emitted and Default label is not registered
+  DevelControl::EmitAccessibilityStateChanged(button, Accessibility::State::SHOWING, 1);
+
+  DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+  DALI_TEST_CHECK(Accessibility::Bridge::GetCurrentBridge()->GetDefaultLabel(rootAccessible) != buttonAccessible);
+
+  Dali::Accessibility::TestResetStateChangedResult();
+  flushCoalescableMessage(application);
+
+  // non-modal role: Visible State is not emitted
+  DevelControl::EmitAccessibilityStateChanged(button, Accessibility::State::VISIBLE, 1);
+
+  DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+  DALI_TEST_CHECK(Accessibility::Bridge::GetCurrentBridge()->GetDefaultLabel(rootAccessible) != buttonAccessible);
+
+  Dali::Accessibility::TestResetStateChangedResult();
+  flushCoalescableMessage(application);
+
+  // non-modal role: Showing State is emitted if highlighted and not showing
+  buttonAccessible->GrabHighlight();
+  DevelControl::EmitAccessibilityStateChanged(button, Accessibility::State::SHOWING, 0);
+  DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedCalled());
+  DALI_TEST_CHECK(Dali::Accessibility::TestStateChangedResult("showing", 0));
+
+  Dali::Accessibility::TestResetStateChangedResult();
+  flushCoalescableMessage(application);
+
+  // non-modal role: Showing State is not emitted if highlighted and showing
+  DevelControl::EmitAccessibilityStateChanged(button, Accessibility::State::SHOWING, 1);
+  DALI_TEST_CHECK(!Dali::Accessibility::TestStateChangedCalled());
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  END_TEST;
+}
+
+int UtcDaliAccessibleRemovalOnActorDestoyed(void)
+{
+  ToolkitTestApplication application;
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  auto layer = Layer::New();
+
+  auto control = Control::New();
+
+  std::weak_ptr<Accessibility::Accessible> layerAccessible   = Accessibility::Accessible::GetOwningPtr(layer);   // AdaptorAccessible
+  std::weak_ptr<Accessibility::Accessible> controlAccessible = Accessibility::Accessible::GetOwningPtr(control); // ControlAccessible
+  DALI_TEST_CHECK(layerAccessible.lock());
+  DALI_TEST_CHECK(controlAccessible.lock());
+
+  // Test Getting already added accessible from the map
+  DALI_TEST_CHECK(!layerAccessible.expired());
+  DALI_TEST_CHECK(!controlAccessible.expired());
+  DALI_TEST_CHECK(Accessibility::Accessible::Get(layer) == layerAccessible.lock().get());
+  DALI_TEST_CHECK(Accessibility::Accessible::Get(control) == controlAccessible.lock().get());
+
+  // Test ControlAccessible Removal
+  control.Reset();
+  DALI_TEST_CHECK(controlAccessible.expired());
+  DALI_TEST_CHECK(Accessibility::Accessible::Get(control) == nullptr);
+
+  // Test AdaptorAccessible Removal
+  layer.Reset();
+  DALI_TEST_CHECK(layerAccessible.expired());
+  DALI_TEST_CHECK(Accessibility::Accessible::Get(layer) == nullptr);
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  END_TEST;
+}
+
+int UtcDaliIncludeHidden(void)
+{
+  ToolkitTestApplication application;
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  auto root          = Layer::New();
+  auto control       = Control::New();
+  auto hiddenControl = Control::New();
+  hiddenControl.SetProperty(DevelControl::Property::ACCESSIBILITY_HIDDEN, true);
+
+  application.GetScene().Add(root);
+  root.Add(control);
+  root.Add(hiddenControl);
+
+  auto appAccessible = Accessibility::Bridge::GetCurrentBridge()->GetApplication();
+  DALI_TEST_CHECK(appAccessible);
+
+  auto rootAccessible = Accessibility::Accessible::Get(root);
+  DALI_TEST_CHECK(rootAccessible);
+
+  auto appAddress = appAccessible->GetAddress();
+
+  DALI_TEST_CHECK(!TestGetIncludeHidden(appAddress));
+  DALI_TEST_CHECK(rootAccessible->GetChildCount() == 1); // hidden control is excluded by default
+
+  TestSetIncludeHidden(appAddress, true);
+  DALI_TEST_CHECK(TestGetIncludeHidden(appAddress));
+  DALI_TEST_CHECK(rootAccessible->GetChildCount() == 2); // hidden control is included after setting includeHidden to true
+
+  TestSetIncludeHidden(appAddress, false);
+  DALI_TEST_CHECK(!TestGetIncludeHidden(appAddress));
+  DALI_TEST_CHECK(rootAccessible->GetChildCount() == 1); // hidden control is excluded after setting includeHidden to false
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  END_TEST;
+}
index 8b54340..b2bd544 100644 (file)
@@ -26,8 +26,7 @@ void utc_dali_accessibility_controls_startup(void)
 void utc_dali_accessibility_controls_cleanup(void)
 {
   test_return_value = TET_PASS;
-  //DBusWrapper::Install({}) is a de-install
-  DBusWrapper::Install({});
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
 }
 
 int UtcDaliControlAccessibileGet01(void)
@@ -131,24 +130,6 @@ int UtcDaliControlAccessibileBlockAccessibleCreation(void)
   END_TEST;
 }
 
-int UtcDaliControlPropertyAccessibilityTranslationDomain(void)
-{
-  ToolkitTestApplication application;
-
-  auto control = Control::New();
-
-  auto accessibility_translation_domain = DevelControl::Property::ACCESSIBILITY_TRANSLATION_DOMAIN;
-  DALI_TEST_EQUALS("", control.GetProperty<std::string>(accessibility_translation_domain), TEST_LOCATION);
-
-  control.SetProperty(accessibility_translation_domain, "translation_domain_test_1");
-  DALI_TEST_EQUALS("translation_domain_test_1", control.GetProperty(accessibility_translation_domain).Get<std::string>(), TEST_LOCATION);
-
-  control.SetProperty(accessibility_translation_domain, "translation_domain_test_2");
-  DALI_TEST_EQUALS("translation_domain_test_2", control.GetProperty(accessibility_translation_domain).Get<std::string>(), TEST_LOCATION);
-
-  END_TEST;
-}
-
 // This test shows that when the accessibility bridge is
 // not up, there is no possibility to grab or clear highlight
 int UtcDaliControlAccessibilityHighlight(void)
@@ -727,11 +708,11 @@ int UtcDaliAccessibilityTextAnchor(void)
   DALI_TEST_EQUALS(hyperlink->IsValid(), true, TEST_LOCATION);
   auto action = dynamic_cast<Dali::Accessibility::Action*>(accessible);
   // activation of valid hyperlink
-  DALI_TEST_CHECK(action->DoAction("accessibilityActivated"));
+  DALI_TEST_CHECK(action->DoAction("activate"));
   // making hyperlink invalid
   textanchor.SetProperty(Toolkit::TextAnchor::Property::URI, "");
   DALI_TEST_EQUALS(hyperlink->IsValid(), false, TEST_LOCATION);
-  DALI_TEST_CHECK(!action->DoAction("accessibilityActivated"));
+  DALI_TEST_CHECK(!action->DoAction("activate"));
 
   Dali::Accessibility::TestEnableSC(false);
 
index 4389b90..ad6a870 100644 (file)
@@ -39,6 +39,7 @@ void utc_dali_toolkit_accessibility_text_startup(void)
 void utc_dali_toolkit_accessibility_text_cleanup(void)
 {
   test_return_value = TET_PASS;
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
 }
 
 int utcDaliAccessibilityTextEditorGetName(void)
index d19755a..caabef7 100644 (file)
@@ -45,6 +45,7 @@ void utc_dali_toolkit_accessibility_value_startup(void)
 void utc_dali_toolkit_accessibility_value_cleanup(void)
 {
   test_return_value = TET_PASS;
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
 }
 
 int utcDaliAccessibilityProgressBarGetMinimum(void)
index 4f6e20c..90b729f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -131,7 +131,10 @@ bool SetBidirectionalInfoTest(const SetBidirectionalInfoData& data)
   }
 
   // 3) Call the SetBidirectionalInfo() function.
-  SetBidirectionalInfo(logicalModel->mText,
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
+  SetBidirectionalInfo(bidirectionalSupport,
+                       logicalModel->mText,
                        logicalModel->mScriptRuns,
                        logicalModel->mLineBreakInfo,
                        data.startIndex,
@@ -139,8 +142,6 @@ bool SetBidirectionalInfoTest(const SetBidirectionalInfoData& data)
                        bidirectionalInfo);
 
   // 4) Compare with the expected results.
-  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
-
   if(data.numberOfParagraphs != bidirectionalInfo.Count())
   {
     // Different number of expected bidirectional paragraphs.
@@ -213,17 +214,21 @@ bool GetMirroredTextTest(const GetMirroredTextData& data)
   VisualModelPtr  visualModel  = textModel->mVisualModel;
 
   // 2) Call the GetMirroredText() function for the whole text
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
   Vector<Character> mirroredText;
   bool              mirrored = false;
-  mirrored                   = GetMirroredText(logicalModel->mText,
-                             logicalModel->mCharacterDirections,
-                             logicalModel->mBidirectionalParagraphInfo,
-                             0u,
-                             logicalModel->mText.Count(),
-                             mirroredText);
+  mirrored                   = GetMirroredText(bidirectionalSupport,
+                                               logicalModel->mText,
+                                               logicalModel->mCharacterDirections,
+                                               logicalModel->mBidirectionalParagraphInfo,
+                                               0u,
+                                               logicalModel->mText.Count(),
+                                               mirroredText);
 
   // 3) Call the GetMirroredText() function for the given index + number of characters
-  mirrored = GetMirroredText(logicalModel->mText,
+  mirrored = GetMirroredText(bidirectionalSupport,
+                             logicalModel->mText,
                              logicalModel->mCharacterDirections,
                              logicalModel->mBidirectionalParagraphInfo,
                              data.startIndex,
@@ -306,8 +311,10 @@ bool GetCharactersDirectionTest(const GetCharactersDirectionData& data)
   }
 
   // 3) Call GetCharactersDirection() function.
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
 
-  GetCharactersDirection(bidirectionalInfo,
+  GetCharactersDirection(bidirectionalSupport,
+                         bidirectionalInfo,
                          logicalModel->mText.Count(),
                          data.startIndex,
                          data.numberOfCharacters,
index cec590c..21e3ec7 100644 (file)
@@ -36,6 +36,9 @@ void dali_color_conversion_startup(void)
 void dali_color_conversion_cleanup(void)
 {
   test_return_value = TET_PASS;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
+#endif
 }
 
 int UtcDaliPropertyHelperConvertHtmlStringToColor(void)
index 69365fb..f1438db 100755 (executable)
@@ -93,6 +93,9 @@ void dali_debug_rendering_startup(void)
 void dali_debug_rendering_cleanup(void)
 {
   test_return_value = TET_PASS;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
+#endif
 }
 
 int UtcDaliDebugRenderingGetVisual1(void)
index 4041450..0c9eef6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-#include <iostream>
 #include <stdlib.h>
+#include <iostream>
 
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/dali-toolkit.h>
 #include <toolkit-event-thread-callback.h>
 #include <toolkit-text-utils.h>
 
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
 
 #include <../dali-toolkit/dali-toolkit-test-utils/dummy-control.h>
 #include <dummy-visual.h>
@@ -35,7 +35,7 @@ int UtcImageVisualShaderFeatureBuilderSetGetProperty01(void)
 {
   ToolkitTestApplication application;
   tet_infoline("Register an ImageVisual and and perform an Action on Visual directly");
-  auto featureBuilder = Dali::Toolkit::Internal::ImageVisualShaderFeatureBuilder()
+  auto featureBuilder = Dali::Toolkit::Internal::ImageVisualShaderFeature::FeatureBuilder()
                           .EnableTextureAtlas(Dali::Toolkit::Internal::ImageVisualShaderFeature::TextureAtlas::ENABLED)
                           .ApplyDefaultTextureWrapMode(true);
 
@@ -63,7 +63,7 @@ int UtcImageVisualShaderFeatureBuilderSetGetProperty02(void)
 {
   ToolkitTestApplication application;
   tet_infoline("Register an ImageVisual and and perform an Action on Visual directly");
-  auto featureBuilder = Dali::Toolkit::Internal::ImageVisualShaderFeatureBuilder()
+  auto featureBuilder = Dali::Toolkit::Internal::ImageVisualShaderFeature::FeatureBuilder()
                           .EnableRoundedCorner(true)
                           .EnableBorderline(true)
                           .EnableAlphaMaskingOnRendering(true)
index 38fdc58..d4a859f 100644 (file)
@@ -36,6 +36,9 @@ void dali_property_helper_startup(void)
 void dali_property_helper_cleanup(void)
 {
   test_return_value = TET_PASS;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
+#endif
 }
 
 int UtcDaliPropertyHelperGetStringFromPropertyWithString(void)
diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-RenderEffect-internal.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-RenderEffect-internal.cpp
new file mode 100644 (file)
index 0000000..9a3bbc6
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
+
+void utc_dali_toolkit_internal_render_effect_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void utc_dali_toolkit_internal_render_effect_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace Dali::Toolkit
+{
+namespace Internal
+{
+class TestRenderEffectImpl;
+} // namespace Internal
+
+class TestRenderEffect : public Dali::Toolkit::RenderEffect
+{
+public:
+  static TestRenderEffect New();
+  TestRenderEffect()                               = default;
+  TestRenderEffect(const TestRenderEffect& handle) = default;
+  ~TestRenderEffect()                              = default;
+
+public: // Not intended for use by Application developers
+  explicit TestRenderEffect(Internal::TestRenderEffectImpl* testRenderEffectImpl);
+};
+
+namespace Internal
+{
+class TestRenderEffectImpl;
+using TestRenderEffectImplPtr = IntrusivePtr<TestRenderEffectImpl>;
+class TestRenderEffectImpl : public RenderEffectImpl
+{
+public:
+  static TestRenderEffectImplPtr New()
+  {
+    TestRenderEffectImplPtr handle = new TestRenderEffectImpl();
+    handle->Initialize();
+    return handle;
+  }
+
+protected:
+  TestRenderEffectImpl()
+  : mOnActivated(false)
+  {
+  }
+  virtual ~TestRenderEffectImpl()
+  {
+  }
+
+  void OnInitialize() override
+  {
+  }
+
+  void OnActivate() override
+  {
+    mOnActivated = true;
+  }
+
+  void OnDeactivate() override
+  {
+    mOnActivated = false;
+  }
+
+public:
+  Dali::Toolkit::Control GetOwnerControl() const
+  {
+    return RenderEffectImpl::GetOwnerControl();
+  }
+
+public:
+  bool mOnActivated;
+};
+
+inline Dali::Toolkit::Internal::TestRenderEffectImpl& GetImplementation(Dali::Toolkit::TestRenderEffect& handle)
+{
+  BaseObject& object = handle.GetBaseObject();
+  return static_cast<Toolkit::Internal::TestRenderEffectImpl&>(object);
+}
+} // namespace Internal
+
+TestRenderEffect TestRenderEffect::New()
+{
+  Internal::TestRenderEffectImplPtr internal = Internal::TestRenderEffectImpl::New();
+  return TestRenderEffect(internal.Get());
+}
+
+TestRenderEffect::TestRenderEffect(Internal::TestRenderEffectImpl* testRenderEffectImpl)
+: RenderEffect(testRenderEffectImpl)
+{
+}
+} // namespace Dali::Toolkit
+
+using namespace Dali::Toolkit;
+
+int UtcDaliInternalRenderEffectNewP(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliInternalRenderEffectNewP");
+
+  Toolkit::TestRenderEffect testEffect = Toolkit::TestRenderEffect::New();
+  DALI_TEST_CHECK(testEffect);
+
+  // Check that effect is not activate yet.
+  Toolkit::Internal::TestRenderEffectImpl& impl = Toolkit::Internal::GetImplementation(testEffect);
+  DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliInternalRenderEffectGetOwnerControl01(void)
+{
+  tet_infoline("UtcDaliInternalRenderEffecGetOwnerControl01");
+
+  Toolkit::TestRenderEffect testEffect = Toolkit::TestRenderEffect::New();
+  DALI_TEST_CHECK(testEffect);
+
+  // Check that effect is not activate yet.
+  Toolkit::Internal::TestRenderEffectImpl& impl = Toolkit::Internal::GetImplementation(testEffect);
+  DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+  {
+    ToolkitTestApplication application;
+
+    Integration::Scene scene = application.GetScene();
+
+    Control control = Control::New();
+    control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    control.SetProperty(Actor::Property::SIZE, Vector2(10.0f, 10.0f));
+    scene.Add(control);
+
+    tet_printf("Test effect set, and activate due to control scene on.\n");
+    control.SetRenderEffect(testEffect);
+
+    DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.GetOwnerControl(), control, TEST_LOCATION);
+
+    control.Unparent();
+
+    DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.GetOwnerControl(), control, TEST_LOCATION);
+
+    scene.Add(control);
+
+    DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.GetOwnerControl(), control, TEST_LOCATION);
+
+    // Control released.
+
+    control.Unparent();
+    control.Reset();
+
+    tet_printf("Test effect owner control is empty after control destructed.\n");
+    DALI_TEST_CHECK(testEffect);
+
+    DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+
+    control = Control::New();
+    control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    control.SetProperty(Actor::Property::SIZE, Vector2(10.0f, 10.0f));
+    scene.Add(control);
+
+    tet_printf("Test effect set again.\n");
+    control.SetRenderEffect(testEffect);
+
+    DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+    DALI_TEST_EQUALS(impl.GetOwnerControl(), control, TEST_LOCATION);
+
+    // Terminate application.
+  }
+
+  tet_printf("Test effect owner control is empty and deactivated after application destructed.\n");
+  DALI_TEST_CHECK(testEffect);
+
+  DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliInternalRenderEffectGetOwnerControl02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliInternalRenderEffecGetOwnerControl02");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control1 = Control::New();
+  control1.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control1.SetProperty(Actor::Property::SIZE, Vector2(10.0f, 10.0f));
+  scene.Add(control1);
+
+  Control control2 = Control::New();
+  control2.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control2.SetProperty(Actor::Property::SIZE, Vector2(10.0f, 10.0f));
+  scene.Add(control2);
+
+  Toolkit::TestRenderEffect testEffect = Toolkit::TestRenderEffect::New();
+  DALI_TEST_CHECK(testEffect);
+
+  // Check that effect is not activate yet.
+  Toolkit::Internal::TestRenderEffectImpl& impl = Toolkit::Internal::GetImplementation(testEffect);
+  DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+
+  tet_printf("Test effect set, and activate due to control scene on.\n");
+  control1.SetRenderEffect(testEffect);
+
+  DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), control1, TEST_LOCATION);
+
+  tet_printf("Test effect set to another control\n");
+  control2.SetRenderEffect(testEffect);
+
+  DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), control2, TEST_LOCATION);
+
+  tet_printf("Test control1 call ClearRenderEffect don't have any effort to effect\n");
+  control1.ClearRenderEffect();
+
+  DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), control2, TEST_LOCATION);
+
+  tet_printf("Test control2 call ClearRenderEffect\n");
+  control2.ClearRenderEffect();
+
+  DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+
+  tet_printf("Reset control1 effect\n");
+  control1.SetRenderEffect(testEffect);
+
+  DALI_TEST_EQUALS(impl.IsActivated(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), control1, TEST_LOCATION);
+
+  tet_printf("Test control1 call SetRenderEffect with empty handle\n");
+  control1.SetRenderEffect(Toolkit::RenderEffect());
+
+  DALI_TEST_EQUALS(impl.IsActivated(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.mOnActivated, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(impl.GetOwnerControl(), Toolkit::Control(), TEST_LOCATION);
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-SvgLoader.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-SvgLoader.cpp
new file mode 100644 (file)
index 0000000..eb824f3
--- /dev/null
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <iostream>
+
+#include <stdlib.h>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-environment-variable.h>
+#include <toolkit-event-thread-callback.h>
+#include <toolkit-timer.h>
+
+#include <dali-toolkit/internal/visuals/svg/svg-loader.h>
+
+#include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/svg/svg-loader-observer.h>
+#include <dali-toolkit/internal/visuals/visual-factory-impl.h> ///< For VisualFactory's member SvgLoader.
+#include <dali-toolkit/public-api/image-loader/image-url.h>
+#include <dali-toolkit/public-api/image-loader/image.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+
+#include <test-encoded-image-buffer.h>
+
+#if defined(ELDBUS_ENABLED)
+#include <automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h>
+#endif
+
+using namespace Dali::Toolkit::Internal;
+
+void utc_dali_toolkit_internal_svg_loader_startup(void)
+{
+  setenv("LOG_SVG_LOADER", "3", 1);
+  test_return_value = TET_UNDEF;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install(std::unique_ptr<DBusWrapper>(new TestDBusWrapper));
+#endif
+}
+
+void utc_dali_toolkit_internal_svg_loader_cleanup(void)
+{
+  test_return_value = TET_PASS;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
+#endif
+}
+
+namespace
+{
+const char* TEST_SVG_FILE_NAME                   = TEST_RESOURCE_DIR "/svg1.svg";
+const char* TEST_SVG_INVALID_RASTERIZE_FILE_NAME = TEST_RESOURCE_DIR "/invalid1.svg"; ///< Load succes but rasterize fail.
+
+constexpr Dali::Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+constexpr float         DEFAULT_DPI(218.5f);
+
+class TestObserver : public Dali::Toolkit::Internal::SvgLoaderObserver
+{
+public:
+  TestObserver()
+  : mLoadCalled(false),
+    mLoadSuccess(false),
+    mRasterizeCalled(false),
+    mRasterizeSuccess(false),
+    mVectorImageRenderer(),
+    mTextureSet(),
+    mAtlasRect(FULL_TEXTURE_RECT)
+  {
+  }
+
+public: ///< Implement of SvgLoaderObserver
+  void LoadComplete(int32_t loadId, Dali::VectorImageRenderer vectorImageRenderer) override
+  {
+    mLoadCalled          = true;
+    mVectorImageRenderer = vectorImageRenderer;
+
+    mLoadSuccess = !!mVectorImageRenderer;
+  }
+
+  void RasterizeComplete(int32_t rasterizeId, Dali::TextureSet textureSet, Vector4 atlasRect) override
+  {
+    mRasterizeCalled = true;
+    mTextureSet      = textureSet;
+    mAtlasRect       = atlasRect;
+
+    mRasterizeSuccess = !!mTextureSet;
+  }
+
+public: /// Check for test
+  void CheckTest(bool loadCalled, bool loadSuccess, bool rasterizeCalled, bool rasterizeSuccess, const char* location)
+  {
+    DALI_TEST_EQUALS(mLoadCalled, loadCalled, location);
+    DALI_TEST_EQUALS(mLoadSuccess, loadSuccess, location);
+    DALI_TEST_EQUALS(mRasterizeCalled, rasterizeCalled, location);
+    DALI_TEST_EQUALS(mRasterizeSuccess, rasterizeSuccess, location);
+  }
+  void CheckLoadTest(bool loadCalled, bool loadSuccess, const char* location)
+  {
+    DALI_TEST_EQUALS(mLoadCalled, loadCalled, location);
+    DALI_TEST_EQUALS(mLoadSuccess, loadSuccess, location);
+  }
+  void CheckRasterizeTest(bool rasterizeCalled, bool rasterizeSuccess, const char* location)
+  {
+    DALI_TEST_EQUALS(mRasterizeCalled, rasterizeCalled, location);
+    DALI_TEST_EQUALS(mRasterizeSuccess, rasterizeSuccess, location);
+  }
+
+public:
+  bool mLoadCalled;
+  bool mLoadSuccess;
+  bool mRasterizeCalled;
+  bool mRasterizeSuccess;
+
+  Dali::VectorImageRenderer mVectorImageRenderer;
+
+  TextureSet mTextureSet;
+  Vector4    mAtlasRect;
+};
+
+class TestObserverWithCustomFunction : public TestObserver
+{
+public:
+  TestObserverWithCustomFunction()
+  : TestObserver(),
+    mLoadSignals{},
+    mRasterizeSignals{},
+    mLoadData{nullptr},
+    mRasterizeData{nullptr},
+    mKeepLoadSignal{false},
+    mKeepRasterizeSignal{false}
+  {
+  }
+
+public: ///< Implement of SvgLoaderObserver
+  void LoadComplete(int32_t loadId, Dali::VectorImageRenderer vectorImageRenderer) override
+  {
+    TestObserver::LoadComplete(loadId, vectorImageRenderer);
+
+    // Execute signals.
+    for(size_t i = 0; i < mLoadSignals.size(); i++)
+    {
+      mLoadSignals[i](mLoadData);
+    }
+
+    // Clear signals.
+    if(!mKeepLoadSignal)
+    {
+      mLoadSignals.clear();
+    }
+  }
+
+  void RasterizeComplete(int32_t rasterizeId, Dali::TextureSet textureSet, Vector4 atlasRect) override
+  {
+    TestObserver::RasterizeComplete(rasterizeId, textureSet, atlasRect);
+
+    // Execute signals.
+    for(size_t i = 0; i < mRasterizeSignals.size(); i++)
+    {
+      mRasterizeSignals[i](mRasterizeData);
+    }
+
+    // Clear signals.
+    if(!mKeepRasterizeSignal)
+    {
+      mRasterizeSignals.clear();
+    }
+  }
+
+public:
+  void ConnectLoadFunction(std::function<void(void*)> signal)
+  {
+    mLoadSignals.push_back(signal);
+  }
+
+  void ConnectRasterizeFunction(std::function<void(void*)> signal)
+  {
+    mRasterizeSignals.push_back(signal);
+  }
+
+public:
+  std::vector<std::function<void(void*)>> mLoadSignals;
+  std::vector<std::function<void(void*)>> mRasterizeSignals;
+  void*                                   mLoadData;
+  void*                                   mRasterizeData;
+  bool                                    mKeepLoadSignal;
+  bool                                    mKeepRasterizeSignal;
+};
+
+} // namespace
+
+int UtcSvgLoaderBasicLoadAndRasterize(void)
+{
+  tet_infoline("Test various cases basic behavior\n");
+
+  ToolkitTestApplication application;
+
+  EncodedImageBuffer svgBuffer = Dali::ConvertFileToEncodedImageBuffer(TEST_SVG_FILE_NAME, EncodedImageBuffer::ImageType::VECTOR_IMAGE);
+
+  auto       visualFactory = Toolkit::VisualFactory::Get();
+  SvgLoader& svgLoader     = GetImplementation(visualFactory).GetSvgLoader(); // Use VisualFactory's svg loader, for use atlas and EncodedImageBuffer.
+
+  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(svgBuffer);
+  svgBuffer.Reset();
+
+  const std::string fileNames[] = {
+    TEST_SVG_FILE_NAME,
+    TEST_SVG_INVALID_RASTERIZE_FILE_NAME,
+    imageUrl.GetUrl(),
+    "invalid.svg",
+  };
+  const int fileNamesCount = sizeof(fileNames) / sizeof(fileNames[0]);
+
+  const std::vector<std::pair<uint32_t, uint32_t>> rasterizeSizes = {
+    {0u, 0u},
+    {100u, 100u},
+    {600u, 600u}, ///< To big so atlas attempt failed.
+  };
+  const int rasterizeSizesCount = rasterizeSizes.size();
+
+  for(int fileType = 0; fileType < fileNamesCount; ++fileType)
+  {
+    const bool loadSuccess      = (fileType == 0 || fileType == 1 || fileType == 2);
+    const bool rasterizeSuccess = loadSuccess && (fileType == 0 || fileType == 2);
+    for(int synchronousLoading = 0; synchronousLoading < 2; ++synchronousLoading)
+    {
+      for(int attemptAtlasing = 0; attemptAtlasing < 2; ++attemptAtlasing)
+      {
+        for(int sizeType = 0; sizeType < rasterizeSizesCount; ++sizeType)
+        {
+          const bool atlasAttempted = (attemptAtlasing == 1) && (sizeType == 0 || sizeType == 1);
+
+          tet_printf("\n\nTesting fileType %d, synchronousLoading %d, attemptAtlasing %d, sizeType %d\n\n", fileType, synchronousLoading, attemptAtlasing, sizeType);
+
+          TestObserver observer;
+          std::string  filename(fileNames[fileType]);
+
+          SvgLoader::SvgLoadId      loadId      = svgLoader.Load(filename, DEFAULT_DPI, &observer, synchronousLoading == 1);
+          SvgLoader::SvgRasterizeId rasterizeId = svgLoader.Rasterize(loadId, rasterizeSizes[sizeType].first, rasterizeSizes[sizeType].second, attemptAtlasing == 1, &observer, synchronousLoading == 1);
+          DALI_TEST_CHECK(loadId != SvgLoader::INVALID_SVG_LOAD_ID);
+          DALI_TEST_CHECK(rasterizeId != SvgLoader::INVALID_SVG_RASTERIZE_ID);
+
+          if(synchronousLoading == 1)
+          {
+            observer.CheckTest(true, loadSuccess, true, rasterizeSuccess, TEST_LOCATION);
+          }
+          else
+          {
+            observer.CheckTest(false, false, false, false, TEST_LOCATION);
+
+            // Wait async load complete
+            DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+            observer.CheckTest(true, loadSuccess, false, false, TEST_LOCATION);
+
+            if(loadSuccess)
+            {
+              // Wait async rasterize complete
+              DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+              // TODO : We don't notify rasterize failed even if load failed. Should we notify it?
+              observer.CheckTest(true, loadSuccess, true, rasterizeSuccess, TEST_LOCATION);
+            }
+          }
+
+          DALI_TEST_EQUALS(!!observer.mVectorImageRenderer, loadSuccess, TEST_LOCATION);
+          DALI_TEST_EQUALS(!!observer.mTextureSet, rasterizeSuccess, TEST_LOCATION);
+          if(rasterizeSuccess)
+          {
+            if(atlasAttempted)
+            {
+              DALI_TEST_NOT_EQUALS(observer.mAtlasRect, FULL_TEXTURE_RECT, 0.01f, TEST_LOCATION);
+            }
+            else
+            {
+              DALI_TEST_EQUALS(observer.mAtlasRect, FULL_TEXTURE_RECT, TEST_LOCATION);
+            }
+          }
+
+          // Remove cache
+          svgLoader.RequestLoadRemove(loadId, &observer);
+          svgLoader.RequestRasterizeRemove(rasterizeId, &observer, false);
+
+          // Ensure svg loader cache removed.
+          application.SendNotification();
+          application.Render();
+          application.SendNotification();
+          application.Render();
+        }
+      }
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcSvgLoaderCacheLoadAndRasterize01(void)
+{
+  tet_infoline("Test Load and Rsterize cached well\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserver observer1;
+  TestObserver observer2;
+  TestObserver observer3;
+  TestObserver observer4;
+  TestObserver observer5;
+
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer1, false);
+  auto loadId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer2, false);
+
+  tet_printf("Test Load cached well\n");
+  DALI_TEST_EQUALS(loadId1, loadId2, TEST_LOCATION);
+
+  observer1.CheckLoadTest(false, false, TEST_LOCATION);
+  observer2.CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check both observer1 and observer2 loaded.
+  observer1.CheckLoadTest(true, true, TEST_LOCATION);
+  observer2.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test difference url and dpi return not equal id\n");
+  auto loadId3 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, &observer3, false);
+  auto loadId4 = svgLoader.Load(std::string(TEST_SVG_INVALID_RASTERIZE_FILE_NAME), DEFAULT_DPI, &observer4, false);
+  DALI_TEST_CHECK(loadId1 != loadId3);
+  DALI_TEST_CHECK(loadId1 != loadId4);
+  DALI_TEST_CHECK(loadId3 != loadId4);
+
+  observer3.CheckLoadTest(false, false, TEST_LOCATION);
+  observer4.CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 2 times : loadId3 loadId4
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+  observer4.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test Load cached well even after load completed\n");
+  auto loadId5 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer5, false);
+
+  DALI_TEST_EQUALS(loadId1, loadId5, TEST_LOCATION);
+  // Check observer5 loaded.
+  observer5.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Request Rasterize\n");
+  auto rasterizeId1 = svgLoader.Rasterize(loadId1, 100u, 100u, false, &observer1, false);
+  auto rasterizeId2 = svgLoader.Rasterize(loadId1, 100u, 100u, false, &observer2, false);
+
+  tet_printf("Test Rasterize cached well\n");
+  DALI_TEST_EQUALS(rasterizeId1, rasterizeId2, TEST_LOCATION);
+
+  observer1.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer2.CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  // Wait async rasterize complete 1 time : rasterizeId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check both observer1 and observer2 rasterized.
+  observer1.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer2.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test difference loadId and size return not equal id\n");
+  auto rasterizeId3 = svgLoader.Rasterize(loadId1, 200u, 200u, false, &observer3, false);
+  auto rasterizeId4 = svgLoader.Rasterize(loadId3, 100u, 100u, false, &observer4, false);
+  DALI_TEST_CHECK(rasterizeId1 != rasterizeId3);
+  DALI_TEST_CHECK(rasterizeId1 != rasterizeId4);
+  DALI_TEST_CHECK(rasterizeId3 != rasterizeId4);
+
+  observer3.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer4.CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  // Wait async rasterize complete 2 times : rasterizeId3 and rasterizeId4
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  observer3.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer4.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test Rasterize cached well even after rasterize completed\n");
+  auto rasterizeId5 = svgLoader.Rasterize(loadId1, 100u, 100u, false, &observer5, false);
+
+  DALI_TEST_EQUALS(rasterizeId1, rasterizeId5, TEST_LOCATION);
+  // Check observer5 loaded.
+  observer5.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderCacheLoadAndRasterize02(void)
+{
+  tet_infoline("Test Load removed during rasterize\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserver observer1;
+  TestObserver observer2;
+  TestObserver observer3;
+
+  tet_printf("load request for loadId1\n");
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer1, false);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  observer1.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Request Rasterize\n");
+  auto rasterizeId1 = svgLoader.Rasterize(loadId1, 100u, 100u, false, &observer1, false);
+
+  tet_printf("Remove loadId1 during rasterize execute\n");
+  svgLoader.RequestLoadRemove(loadId1, &observer1);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait async rasterize complete 1 time : rasterizeId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  observer1.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  auto loadId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer2, false);
+
+  tet_printf("Test rasterize request increase the reference count of loadId1\n");
+
+  DALI_TEST_EQUALS(loadId1, loadId2, TEST_LOCATION);
+  // Check observer2 loaded.
+  observer2.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Remove loadId2 and rasterizeId1 synchronously\n");
+  svgLoader.RequestLoadRemove(loadId2, &observer2);
+
+  application.SendNotification();
+  application.Render();
+
+  svgLoader.RequestRasterizeRemove(rasterizeId1, &observer1, true);
+
+  tet_printf("Test loadId3 is not cached.\n");
+  [[maybe_unused]] auto loadId3 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer3, false);
+  observer3.CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId3
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderCacheLoadAndRasterize03(void)
+{
+  tet_infoline("Test Load and Rsterize call synchronously during async cached call\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserver observer1;
+  TestObserver observer2;
+  TestObserver observer3;
+
+  tet_printf("Load request async / sync / and async again\n");
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer1, false);
+  auto loadId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer2, true);
+  auto loadId3 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer3, false);
+
+  tet_printf("Test Load cached well\n");
+  DALI_TEST_EQUALS(loadId1, loadId2, TEST_LOCATION);
+  DALI_TEST_EQUALS(loadId1, loadId3, TEST_LOCATION);
+  DALI_TEST_EQUALS(loadId2, loadId3, TEST_LOCATION);
+
+  tet_printf("Test async observer didn't notify. (Sync load didn't notify other observers)\n");
+  observer1.CheckLoadTest(false, false, TEST_LOCATION);
+  observer2.CheckLoadTest(true, true, TEST_LOCATION);
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check all observers loaded well.
+  observer1.CheckLoadTest(true, true, TEST_LOCATION);
+  observer2.CheckLoadTest(true, true, TEST_LOCATION);
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Request Rasterize async / sync / and async again\n");
+  auto rasterizeId1 = svgLoader.Rasterize(loadId1, 100u, 100u, false, &observer1, false);
+  auto rasterizeId2 = svgLoader.Rasterize(loadId2, 100u, 100u, false, &observer2, true);
+  auto rasterizeId3 = svgLoader.Rasterize(loadId3, 100u, 100u, false, &observer3, false);
+
+  tet_printf("Test Rasterize cached well\n");
+  DALI_TEST_EQUALS(rasterizeId1, rasterizeId2, TEST_LOCATION);
+  DALI_TEST_EQUALS(rasterizeId1, rasterizeId3, TEST_LOCATION);
+  DALI_TEST_EQUALS(rasterizeId2, rasterizeId3, TEST_LOCATION);
+
+  tet_printf("Test async observer didn't notify. (Sync load didn't notify other observers)\n");
+  observer1.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer2.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer3.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  // Wait async rasterize complete 1 time : rasterizeId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check all observers rasterized well.
+  observer1.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer2.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer3.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderLoadCancel(void)
+{
+  tet_infoline("Test Load cancel well\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserver observer1;
+  TestObserver observer2;
+  TestObserver observer3;
+
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer1, false);
+
+  observer1.CheckLoadTest(false, false, TEST_LOCATION);
+
+  svgLoader.RequestLoadRemove(loadId1, &observer1);
+
+  application.SendNotification();
+  application.Render();
+
+  // load task is not finished yet.
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  tet_printf("load request for loadId1 not notify\n");
+  observer1.CheckLoadTest(false, false, TEST_LOCATION);
+
+  auto loadId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer2, false);
+
+  observer2.CheckLoadTest(false, false, TEST_LOCATION);
+
+  svgLoader.RequestLoadRemove(loadId2, &observer2);
+
+  application.SendNotification();
+  application.Render();
+
+  // load task is not finished yet.
+  // But during loading task running, request same item again
+  auto loadId3 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer3, false);
+  DALI_TEST_EQUALS(loadId2, loadId3, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  tet_printf("load request for loadId2 not notify, but loadId3 notify\n");
+  observer2.CheckLoadTest(false, false, TEST_LOCATION);
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderDestructDuringObserver01(void)
+{
+  tet_infoline("Test destroy observer during load observer\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserverWithCustomFunction* observer1 = new TestObserverWithCustomFunction();
+  TestObserverWithCustomFunction* observer2 = new TestObserverWithCustomFunction();
+
+  struct CustomData
+  {
+    TestObserver* self{nullptr};
+    TestObserver* other{nullptr};
+
+    bool loadCalled{false};
+    bool loadSuccess{false};
+  } mData;
+
+  mData.self  = observer1;
+  mData.other = observer2;
+
+  observer1->mLoadData = &mData;
+  observer1->ConnectLoadFunction([](void* data) {
+    DALI_TEST_CHECK(data);
+    CustomData*   customData = static_cast<CustomData*>(data);
+    TestObserver* observer1  = customData->self;
+    TestObserver* observer2  = customData->other;
+    DALI_TEST_CHECK(observer1);
+    DALI_TEST_CHECK(observer2);
+    tet_printf("Destroy observer1 and observer2 (self)n");
+
+    customData->loadCalled  = observer1->mLoadCalled;
+    customData->loadSuccess = observer1->mLoadSuccess;
+
+    delete observer1;
+    delete observer2;
+  });
+
+  observer2->ConnectLoadFunction([](void* data) {
+    tet_printf("observer2 Should be destroyed by observer1. Test failed\n");
+
+    tet_result(TET_FAIL);
+  });
+  tet_printf("load request for loadId1 and loadId2. observer1 should be called first.\n");
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, observer1, false);
+  auto loadId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, observer2, false);
+  DALI_TEST_EQUALS(loadId1, loadId2, TEST_LOCATION);
+
+  observer1->CheckLoadTest(false, false, TEST_LOCATION);
+  observer2->CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(mData.loadCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(mData.loadSuccess, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderDestructDuringObserver02(void)
+{
+  tet_infoline("Test destroy observer during rasterize observer\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserverWithCustomFunction* observer1 = new TestObserverWithCustomFunction();
+  TestObserverWithCustomFunction* observer2 = new TestObserverWithCustomFunction();
+
+  struct CustomData
+  {
+    TestObserver* self{nullptr};
+    TestObserver* other{nullptr};
+
+    bool rasterizeCalled{false};
+    bool rasterizeSuccess{false};
+  } mData;
+
+  mData.self  = observer1;
+  mData.other = observer2;
+
+  observer1->mRasterizeData = &mData;
+  observer1->ConnectRasterizeFunction([](void* data) {
+    DALI_TEST_CHECK(data);
+    CustomData*   customData = static_cast<CustomData*>(data);
+    TestObserver* observer1  = customData->self;
+    TestObserver* observer2  = customData->other;
+    DALI_TEST_CHECK(observer1);
+    DALI_TEST_CHECK(observer2);
+    tet_printf("Destroy observer1(self) and observer2\n");
+
+    customData->rasterizeCalled  = observer1->mRasterizeCalled;
+    customData->rasterizeSuccess = observer1->mRasterizeSuccess;
+
+    delete observer1;
+    delete observer2;
+  });
+
+  observer2->ConnectRasterizeFunction([](void* data) {
+    tet_printf("observer2 Should be destroyed by observer1. Test failed\n");
+
+    tet_result(TET_FAIL);
+  });
+  tet_printf("load request for loadId1 and loadId2. observer1 should be called first.\n");
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, observer1, false);
+  auto loadId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, observer2, false);
+  DALI_TEST_EQUALS(loadId1, loadId2, TEST_LOCATION);
+
+  observer1->CheckLoadTest(false, false, TEST_LOCATION);
+  observer2->CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  observer1->CheckLoadTest(true, true, TEST_LOCATION);
+  observer2->CheckLoadTest(true, true, TEST_LOCATION);
+
+  auto rasterizeId1 = svgLoader.Rasterize(loadId1, 100u, 100u, false, observer1, false);
+  auto rasterizeId2 = svgLoader.Rasterize(loadId2, 100u, 100u, false, observer2, false);
+  DALI_TEST_EQUALS(rasterizeId1, rasterizeId2, TEST_LOCATION);
+
+  observer1->CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer2->CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  // Wait async rasterize complete 1 time : rasterizeId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(mData.rasterizeCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(mData.rasterizeSuccess, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderReqestDuringObserver01(void)
+{
+  tet_infoline("Test request load observer during load observer\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserverWithCustomFunction observer1;
+  TestObserver                   observer2;
+  TestObserver                   observer3;
+  TestObserver                   observer4;
+  TestObserver                   observer5;
+  TestObserver*                  observer6 = new TestObserver();
+
+  struct CustomData
+  {
+    TestObserver* self{nullptr};
+    TestObserver* loadCached{nullptr};
+    TestObserver* loadNonCached1{nullptr};
+    TestObserver* loadNonCached2{nullptr};
+
+    TestObserver* loadAndRemove{nullptr};
+    TestObserver* loadAndDestruct{nullptr};
+
+    SvgLoader::SvgLoadId cachedId{SvgLoader::INVALID_SVG_LOAD_ID};
+    SvgLoader::SvgLoadId nonCachedId1{SvgLoader::INVALID_SVG_LOAD_ID};
+    SvgLoader::SvgLoadId nonCachedId2{SvgLoader::INVALID_SVG_LOAD_ID};
+  } mData;
+
+  mData.self            = &observer1;
+  mData.loadCached      = &observer2;
+  mData.loadNonCached1  = &observer3;
+  mData.loadNonCached2  = &observer4;
+  mData.loadAndRemove   = &observer5;
+  mData.loadAndDestruct = observer6;
+
+  observer1.mLoadData = &mData;
+  observer1.ConnectLoadFunction([&svgLoader](void* data) {
+    DALI_TEST_CHECK(data);
+    CustomData*   customData = static_cast<CustomData*>(data);
+    TestObserver* observer1  = customData->self;
+    TestObserver* observer2  = customData->loadCached;
+    TestObserver* observer3  = customData->loadNonCached1;
+    TestObserver* observer4  = customData->loadNonCached2;
+    TestObserver* observer5  = customData->loadAndRemove;
+    TestObserver* observer6  = customData->loadAndDestruct;
+    DALI_TEST_CHECK(observer1);
+    DALI_TEST_CHECK(observer2);
+    DALI_TEST_CHECK(observer3);
+    DALI_TEST_CHECK(observer4);
+    DALI_TEST_CHECK(observer5);
+    DALI_TEST_CHECK(observer6);
+
+    tet_printf("Request for observer2(cached) and observer3, observer4(non-cached)\n");
+    customData->cachedId     = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, observer2, false);
+    customData->nonCachedId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, observer3, false);
+    customData->nonCachedId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, observer4, false);
+
+    tet_printf("Test observer2 still not notify yet even if it is cached\n");
+    observer2->CheckLoadTest(false, false, TEST_LOCATION);
+    observer3->CheckLoadTest(false, false, TEST_LOCATION);
+    observer4->CheckLoadTest(false, false, TEST_LOCATION);
+
+    tet_printf("Test observer5 load request and cancel\n");
+    auto loadId = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, observer5, false);
+    svgLoader.RequestLoadRemove(loadId, observer5);
+
+    tet_printf("Test observer6 load request and destruct\n");
+    loadId = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, observer6, false);
+    delete observer6;
+  });
+
+  tet_printf("load request for loadId1.\n");
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer1, false);
+
+  observer1.CheckLoadTest(false, false, TEST_LOCATION);
+  observer2.CheckLoadTest(false, false, TEST_LOCATION);
+  observer3.CheckLoadTest(false, false, TEST_LOCATION);
+  observer4.CheckLoadTest(false, false, TEST_LOCATION);
+  observer5.CheckLoadTest(false, false, TEST_LOCATION);
+  observer6->CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  observer1.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test observer2 notify after observer1 notify finished\n");
+  DALI_TEST_EQUALS(loadId1, mData.cachedId, TEST_LOCATION);
+  DALI_TEST_CHECK(loadId1 != mData.nonCachedId1);
+  DALI_TEST_EQUALS(mData.nonCachedId1, mData.nonCachedId2, TEST_LOCATION);
+  observer2.CheckLoadTest(true, true, TEST_LOCATION);
+  observer3.CheckLoadTest(false, false, TEST_LOCATION);
+  observer4.CheckLoadTest(false, false, TEST_LOCATION);
+  observer5.CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : mData.nonCachedId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  tet_printf("Test observer5 not notify\n");
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+  observer4.CheckLoadTest(true, true, TEST_LOCATION);
+  observer5.CheckLoadTest(false, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderReqestDuringObserver02(void)
+{
+  tet_infoline("Test request load observer during load observer\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserverWithCustomFunction observer1;
+  TestObserver                   observer2;
+  TestObserver                   observer3;
+  TestObserver                   observer4;
+
+  struct CustomData
+  {
+    TestObserver* self{nullptr};
+    TestObserver* loadCached{nullptr};
+    TestObserver* loadNonCached1{nullptr};
+    TestObserver* loadNonCached2{nullptr};
+
+    SvgLoader::SvgLoadId cachedId{SvgLoader::INVALID_SVG_LOAD_ID};
+    SvgLoader::SvgLoadId nonCachedId1{SvgLoader::INVALID_SVG_LOAD_ID};
+    SvgLoader::SvgLoadId nonCachedId2{SvgLoader::INVALID_SVG_LOAD_ID};
+  } mData;
+
+  mData.self           = &observer1;
+  mData.loadCached     = &observer2;
+  mData.loadNonCached1 = &observer3;
+  mData.loadNonCached2 = &observer4;
+
+  observer1.mLoadData = &mData;
+  observer1.ConnectLoadFunction([&svgLoader](void* data) {
+    DALI_TEST_CHECK(data);
+    CustomData*   customData = static_cast<CustomData*>(data);
+    TestObserver* observer1  = customData->self;
+    TestObserver* observer2  = customData->loadCached;
+    TestObserver* observer3  = customData->loadNonCached1;
+    TestObserver* observer4  = customData->loadNonCached2;
+    DALI_TEST_CHECK(observer1);
+    DALI_TEST_CHECK(observer2);
+    DALI_TEST_CHECK(observer3);
+    DALI_TEST_CHECK(observer4);
+
+    tet_printf("Request for observer2(cached) and observer3, observer4(non-cached)\n");
+    tet_printf("For here, let we request observer4 as sync!\n");
+    customData->cachedId     = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, observer2, false);
+    customData->nonCachedId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, observer3, false);
+    customData->nonCachedId2 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI + 2.0f, observer4, true);
+
+    tet_printf("Test observer2 still not notify yet even if it is cached\n");
+    observer2->CheckLoadTest(false, false, TEST_LOCATION);
+
+    tet_printf("Test observer4 notify, but observer3 yet\n");
+    observer3->CheckLoadTest(false, false, TEST_LOCATION);
+    observer4->CheckLoadTest(true, true, TEST_LOCATION);
+  });
+
+  tet_printf("load request for loadId1.\n");
+  auto loadId1 = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, &observer1, false);
+
+  observer1.CheckLoadTest(false, false, TEST_LOCATION);
+  observer2.CheckLoadTest(false, false, TEST_LOCATION);
+  observer3.CheckLoadTest(false, false, TEST_LOCATION);
+  observer4.CheckLoadTest(false, false, TEST_LOCATION);
+
+  // Wait async load complete 1 time : loadId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  observer1.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test observer2 notify after observer1 notify finished\n");
+  DALI_TEST_EQUALS(loadId1, mData.cachedId, TEST_LOCATION);
+  DALI_TEST_CHECK(loadId1 != mData.nonCachedId1);
+  DALI_TEST_EQUALS(mData.nonCachedId1, mData.nonCachedId2, TEST_LOCATION);
+  observer2.CheckLoadTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test observer3 notify due to we load it synchronously already\n");
+  observer3.CheckLoadTest(true, true, TEST_LOCATION);
+  observer4.CheckLoadTest(true, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderReqestDuringObserver03(void)
+{
+  tet_infoline("Test request rasterize observer during rasterize observer\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserverWithCustomFunction observer1;
+  TestObserver                   observer2;
+  TestObserver                   observer3;
+  TestObserver                   observer4;
+  TestObserver                   observer5;
+  TestObserver*                  observer6 = new TestObserver();
+
+  struct CustomData
+  {
+    TestObserver* self{nullptr};
+    TestObserver* rasterizeCached{nullptr};
+    TestObserver* rasterizeNonCached1{nullptr};
+    TestObserver* rasterizeNonCached2{nullptr};
+
+    TestObserver* rasterizeAndRemove{nullptr};
+    TestObserver* rasterizeAndDestruct{nullptr};
+
+    SvgLoader::SvgRasterizeId cachedId{SvgLoader::INVALID_SVG_RASTERIZE_ID};
+    SvgLoader::SvgRasterizeId nonCachedId1{SvgLoader::INVALID_SVG_RASTERIZE_ID};
+    SvgLoader::SvgRasterizeId nonCachedId2{SvgLoader::INVALID_SVG_RASTERIZE_ID};
+  } mData;
+
+  mData.self                 = &observer1;
+  mData.rasterizeCached      = &observer2;
+  mData.rasterizeNonCached1  = &observer3;
+  mData.rasterizeNonCached2  = &observer4;
+  mData.rasterizeAndRemove   = &observer5;
+  mData.rasterizeAndDestruct = observer6;
+
+  // Sync load and cache it.
+  auto loadId = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, nullptr, true);
+
+  observer1.mRasterizeData = &mData;
+  observer1.ConnectRasterizeFunction([&svgLoader, &loadId](void* data) {
+    DALI_TEST_CHECK(data);
+    CustomData*   customData = static_cast<CustomData*>(data);
+    TestObserver* observer1  = customData->self;
+    TestObserver* observer2  = customData->rasterizeCached;
+    TestObserver* observer3  = customData->rasterizeNonCached1;
+    TestObserver* observer4  = customData->rasterizeNonCached2;
+    TestObserver* observer5  = customData->rasterizeAndRemove;
+    TestObserver* observer6  = customData->rasterizeAndDestruct;
+    DALI_TEST_CHECK(observer1);
+    DALI_TEST_CHECK(observer2);
+    DALI_TEST_CHECK(observer3);
+    DALI_TEST_CHECK(observer4);
+    DALI_TEST_CHECK(observer5);
+    DALI_TEST_CHECK(observer6);
+
+    tet_printf("Request for observer2(cached) and observer3, observer4(non-cached)\n");
+    customData->cachedId     = svgLoader.Rasterize(loadId, 100u, 100u, false, observer2, false);
+    customData->nonCachedId1 = svgLoader.Rasterize(loadId, 200u, 200u, false, observer3, false);
+    customData->nonCachedId2 = svgLoader.Rasterize(loadId, 200u, 200u, false, observer4, false);
+
+    tet_printf("Test observer2 still not notify yet even if it is cached\n");
+    observer2->CheckRasterizeTest(false, false, TEST_LOCATION);
+    observer3->CheckRasterizeTest(false, false, TEST_LOCATION);
+    observer4->CheckRasterizeTest(false, false, TEST_LOCATION);
+
+    tet_printf("Test observer5 rasterize request and cancel\n");
+    auto rasterizeId = svgLoader.Rasterize(loadId, 200u, 200u, false, observer5, false);
+    svgLoader.RequestRasterizeRemove(rasterizeId, observer5, true);
+
+    tet_printf("Test observer6 rasterize request and destruct\n");
+    rasterizeId = svgLoader.Rasterize(loadId, 200u, 200u, false, observer6, false);
+    delete observer6;
+  });
+
+  tet_printf("rasterize request for rasterizeId1.\n");
+  auto rasterizeId1 = svgLoader.Rasterize(loadId, 100u, 100u, false, &observer1, false);
+
+  observer1.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer2.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer3.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer4.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer5.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer6->CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  // Wait async rasterize complete 1 time : rasterizeId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  observer1.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test observer2 notify after observer1 notify finished\n");
+  DALI_TEST_EQUALS(rasterizeId1, mData.cachedId, TEST_LOCATION);
+  DALI_TEST_CHECK(rasterizeId1 != mData.nonCachedId1);
+  DALI_TEST_EQUALS(mData.nonCachedId1, mData.nonCachedId2, TEST_LOCATION);
+  observer2.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer3.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer4.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer5.CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  // Wait async rasterize complete 1 time : mData.nonCachedId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  tet_printf("Test observer5 not notify\n");
+  observer3.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer4.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer5.CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcSvgLoaderReqestDuringObserver04(void)
+{
+  tet_infoline("Test request rasterize observer during rasterize observer\n");
+
+  ToolkitTestApplication application;
+
+  SvgLoader svgLoader; ///Create svg loader without visual factory cache.
+
+  TestObserverWithCustomFunction observer1;
+  TestObserver                   observer2;
+  TestObserver                   observer3;
+  TestObserver                   observer4;
+
+  struct CustomData
+  {
+    TestObserver* self{nullptr};
+    TestObserver* rasterizeCached{nullptr};
+    TestObserver* rasterizeNonCached1{nullptr};
+    TestObserver* rasterizeNonCached2{nullptr};
+
+    SvgLoader::SvgRasterizeId cachedId{SvgLoader::INVALID_SVG_RASTERIZE_ID};
+    SvgLoader::SvgRasterizeId nonCachedId1{SvgLoader::INVALID_SVG_RASTERIZE_ID};
+    SvgLoader::SvgRasterizeId nonCachedId2{SvgLoader::INVALID_SVG_RASTERIZE_ID};
+  } mData;
+
+  mData.self                = &observer1;
+  mData.rasterizeCached     = &observer2;
+  mData.rasterizeNonCached1 = &observer3;
+  mData.rasterizeNonCached2 = &observer4;
+
+  // Sync load and cache it.
+  auto loadId = svgLoader.Load(std::string(TEST_SVG_FILE_NAME), DEFAULT_DPI, nullptr, true);
+
+  observer1.mRasterizeData = &mData;
+  observer1.ConnectRasterizeFunction([&svgLoader, &loadId](void* data) {
+    DALI_TEST_CHECK(data);
+    CustomData*   customData = static_cast<CustomData*>(data);
+    TestObserver* observer1  = customData->self;
+    TestObserver* observer2  = customData->rasterizeCached;
+    TestObserver* observer3  = customData->rasterizeNonCached1;
+    TestObserver* observer4  = customData->rasterizeNonCached2;
+    DALI_TEST_CHECK(observer1);
+    DALI_TEST_CHECK(observer2);
+    DALI_TEST_CHECK(observer3);
+    DALI_TEST_CHECK(observer4);
+
+    tet_printf("Request for observer2(cached) and observer3, observer4(non-cached)\n");
+    tet_printf("For here, let we request observer4 as sync!\n");
+    customData->cachedId     = svgLoader.Rasterize(loadId, 100u, 100u, false, observer2, false);
+    customData->nonCachedId1 = svgLoader.Rasterize(loadId, 200u, 200u, false, observer3, false);
+    customData->nonCachedId2 = svgLoader.Rasterize(loadId, 200u, 200u, false, observer4, true);
+
+    tet_printf("Test observer2 still not notify yet even if it is cached\n");
+    observer2->CheckRasterizeTest(false, false, TEST_LOCATION);
+
+    tet_printf("Test observer4 notify, but observer3 yet\n");
+    observer3->CheckRasterizeTest(false, false, TEST_LOCATION);
+    observer4->CheckRasterizeTest(true, true, TEST_LOCATION);
+  });
+
+  tet_printf("rasterize request for rasterizeId1.\n");
+  auto rasterizeId1 = svgLoader.Rasterize(loadId, 100u, 100u, false, &observer1, false);
+
+  observer1.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer2.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer3.CheckRasterizeTest(false, false, TEST_LOCATION);
+  observer4.CheckRasterizeTest(false, false, TEST_LOCATION);
+
+  // Wait async rasterize complete 1 time : rasterizeId1
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  observer1.CheckRasterizeTest(true, true, TEST_LOCATION);
+  tet_printf("Test observer2 notify after observer1 notify finished\n");
+  DALI_TEST_EQUALS(rasterizeId1, mData.cachedId, TEST_LOCATION);
+  DALI_TEST_CHECK(rasterizeId1 != mData.nonCachedId1);
+  DALI_TEST_EQUALS(mData.nonCachedId1, mData.nonCachedId2, TEST_LOCATION);
+  observer2.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  tet_printf("Test observer3 notify due to we load it synchronously already\n");
+  observer3.CheckRasterizeTest(true, true, TEST_LOCATION);
+  observer4.CheckRasterizeTest(true, true, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index 1c45e02..9d6ee21 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,6 +76,7 @@ bool LayoutTextTest(const LayoutTextData& data)
   // Load some fonts.
   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
   fontClient.SetDpi(96u, 96u);
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
 
   char*             pathNamePtr = get_current_dir_name();
   const std::string pathName(pathNamePtr);
@@ -167,8 +168,11 @@ bool LayoutTextTest(const LayoutTextData& data)
   textModel->mLineWrapMode          = LineWrap::WORD;
   textModel->mIgnoreSpacesAfterText = true;
   //textModel->mMatchSystemLanguageDirection = false;
+
   Layout::Parameters layoutParameters(data.textArea,
-                                      textModel);
+                                      textModel,
+                                      fontClient,
+                                      bidirectionalSupport);
 
   layoutParameters.isLastNewParagraph = isLastNewParagraph;
 
index 774cce1..8f8ffe1 100644 (file)
@@ -725,6 +725,8 @@ int UtcDaliTextControllerSetGetLineSpacingProperty(void)
   ControllerPtr controller = Controller::New();
 
   ConfigureTextLabel(controller);
+  controller->SetRemoveFrontInset(true);
+  controller->SetRemoveBackInset(true);
 
   // single line, line spacing = 0px
   {
index ed5ba45..e198992 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -90,12 +90,14 @@ namespace
     controller->SetMultiLineEnabled( data.isMultiLines );
     controller->SetLineWrapMode( (Text::LineWrap::Mode)(data.lineWrapMode) );
     controller->SetEllipsisPosition( data.ellipsisPosition );
+    controller->SetRemoveFrontInset(true);
+    controller->SetRemoveBackInset(true);
 
     controller->SetText(data.text);
     controller->Relayout( data.size );
 
     // Elide the glyphs.
-    model->ElideGlyphs();
+    model->ElideGlyphs(fontClient);
 
     if( data.numberOfLines != model->GetNumberOfLines() )
     {
@@ -259,6 +261,9 @@ namespace
     controller->SetTextElideEnabled( true );
     controller->SetEllipsisPosition( data.ellipsisPosition );
 
+    controller->SetRemoveFrontInset(true);
+    controller->SetRemoveBackInset(true);
+
     controller->SetText( data.text );
     controller->Relayout( data.size );
 
index ef83bc0..f1a19fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,6 +76,7 @@ bool LayoutTextTest(const LayoutTextData& data)
   // Load some fonts.
   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
   fontClient.SetDpi(96u, 96u);
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
 
   char*             pathNamePtr = get_current_dir_name();
   const std::string pathName(pathNamePtr);
@@ -167,8 +168,11 @@ bool LayoutTextTest(const LayoutTextData& data)
   textModel->mHorizontalAlignment   = Text::HorizontalAlignment::BEGIN;
   textModel->mLineWrapMode          = LineWrap::WORD;
   textModel->mIgnoreSpacesAfterText = true;
+
   Layout::Parameters layoutParameters(data.textArea,
-                                      textModel);
+                                      textModel,
+                                      fontClient,
+                                      bidirectionalSupport);
 
   layoutParameters.isLastNewParagraph = isLastNewParagraph;
 
index a81a462..853b3e9 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -292,7 +292,8 @@ bool ValidateFontTest( const ValidateFontsData& data )
   Vector<FontRun> fontRuns;
 
   // 3) Validate the fonts.
-  multilanguageSupport.ValidateFonts( utf32,
+  multilanguageSupport.ValidateFonts( fontClient,
+                                      utf32,
                                       scripts,
                                       data.fontDescriptionRuns,
                                       defaultFontDescription,
@@ -310,7 +311,8 @@ bool ValidateFontTest( const ValidateFontsData& data )
                         data.index + data.numberOfCharacters - 1u,
                         fontRuns );
 
-    multilanguageSupport.ValidateFonts( utf32,
+    multilanguageSupport.ValidateFonts( fontClient,
+                                        utf32,
                                         scripts,
                                         data.fontDescriptionRuns,
                                         defaultFontDescription,
@@ -1898,60 +1900,6 @@ int UtcDaliTextMultiLanguageValidateFonts01(void)
   END_TEST;
 }
 
-int UtcDaliTextMultiLanguageValidateFontsPerScriptCache(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline(" UtcDaliTextMultiLanguageValidateFontsPerScriptCache");
-
-  std::size_t MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE = 63u;
-  std::size_t VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT   = 8u;
-
-  Dali::Toolkit::Text::Internal::ValidateFontsPerScript* validateFontsPerScript = new Dali::Toolkit::Text::Internal::ValidateFontsPerScript();
-
-  for(std::size_t i = 0u; i < MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE; i ++)
-  {
-    FontId fontId = i;
-    validateFontsPerScript->Cache(fontId);
-  }
-
-  DALI_TEST_EQUALS(MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE, validateFontsPerScript->mValidFonts.Count(), TEST_LOCATION);
-
-  FontId fontId = MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE;
-  validateFontsPerScript->Cache(fontId);
-
-  DALI_TEST_EQUALS(VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT, validateFontsPerScript->mValidFonts.Count(), TEST_LOCATION);
-
-  END_TEST;
-}
-
-int UtcDaliTextMultiLanguageDefaultFontsCache(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline(" UtcDaliTextMultiLanguageDefaultFontsCache");
-
-  std::size_t MAX_DEFAULT_FONTS_CACHE_SIZE = 15;
-  std::size_t DEFAULT_FONTS_REMAIN_COUNT   = 2;
-
-  Dali::Toolkit::Text::Internal::DefaultFonts* defaultFontsPerScript = new Dali::Toolkit::Text::Internal::DefaultFonts();
-
-  for(std::size_t i = 0u; i < MAX_DEFAULT_FONTS_CACHE_SIZE; i ++)
-  {
-    TextAbstraction::FontDescription fontDescription;
-    FontId fontId = i;
-    defaultFontsPerScript->Cache(fontDescription, fontId);
-  }
-
-  DALI_TEST_EQUALS(MAX_DEFAULT_FONTS_CACHE_SIZE, defaultFontsPerScript->mFonts.size(), TEST_LOCATION);
-
-  TextAbstraction::FontDescription fontDescription;
-  FontId fontId = MAX_DEFAULT_FONTS_CACHE_SIZE;
-  defaultFontsPerScript->Cache(fontDescription, fontId);
-
-  DALI_TEST_EQUALS(DEFAULT_FONTS_REMAIN_COUNT, defaultFontsPerScript->mFonts.size(), TEST_LOCATION);
-
-  END_TEST;
-}
-
 int UtcDaliTextMultiLanguageLocaleChange(void)
 {
   ToolkitTestApplication application;
index cfbf325..43674ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -52,6 +52,8 @@ struct BreakInfoData
 
 bool LineBreakInfoTest( const BreakInfoData& data )
 {
+  TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
+
   // 1) Convert to utf32
   Vector<Character> utf32;
   utf32.Resize( data.text.size() );
@@ -67,7 +69,8 @@ bool LineBreakInfoTest( const BreakInfoData& data )
   Vector<LineBreakInfo> lineBreakInfo;
   lineBreakInfo.Resize( numberOfCharacters );
 
-  SetLineBreakInfo( utf32,
+  SetLineBreakInfo( segmentation,
+                    utf32,
                     0u,
                     numberOfCharacters,
                     lineBreakInfo );
@@ -81,7 +84,8 @@ bool LineBreakInfoTest( const BreakInfoData& data )
                          lineBreakInfo.Begin() + data.index + data.numberOfCharacters );
 
     // Update the word line info.
-    SetLineBreakInfo( utf32,
+    SetLineBreakInfo( segmentation,
+                      utf32,
                       data.index,
                       data.numberOfCharacters,
                       lineBreakInfo );
@@ -110,6 +114,8 @@ bool LineBreakInfoTest( const BreakInfoData& data )
 
 bool WordBreakInfoTest( const BreakInfoData& data )
 {
+  TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
+
   // 1) Convert to utf32
   Vector<Character> utf32;
   utf32.Resize( data.text.size() );
@@ -125,7 +131,8 @@ bool WordBreakInfoTest( const BreakInfoData& data )
   Vector<WordBreakInfo> wordBreakInfo;
   wordBreakInfo.Resize( numberOfCharacters );
 
-  SetWordBreakInfo( utf32,
+  SetWordBreakInfo( segmentation,
+                    utf32,
                     0u,
                     numberOfCharacters,
                     wordBreakInfo );
@@ -139,7 +146,8 @@ bool WordBreakInfoTest( const BreakInfoData& data )
                          wordBreakInfo.Begin() + data.index + data.numberOfCharacters );
 
     // Update the word break info.
-    SetWordBreakInfo( utf32,
+    SetWordBreakInfo( segmentation,
+                      utf32,
                       data.index,
                       data.numberOfCharacters,
                       wordBreakInfo );
index 774c1cb..614162c 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -207,10 +207,14 @@ bool ShapeInfoTest(const ShapeInfoData& data)
   }
 
   // 3) Call the ShapeText() function.
+  TextAbstraction::Shaping    shaping    = TextAbstraction::Shaping::Get();
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
 
   Vector<GlyphIndex> newParagraphGlyphs;
 
-  ShapeText(logicalModel->mText,
+  ShapeText(shaping,
+            fontClient,
+            logicalModel->mText,
             logicalModel->mLineBreakInfo,
             logicalModel->mScriptRuns,
             logicalModel->mFontRuns,
index dc3f714..9e98370 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -113,7 +113,7 @@ bool ElideTest(const ElideData& data)
   controller->Relayout(data.size);
 
   // Elide the glyphs.
-  model->ElideGlyphs();
+  model->ElideGlyphs(fontClient);
 
   if(data.numberOfLines != model->GetNumberOfLines())
   {
@@ -250,6 +250,8 @@ int UtcDaliTextViewModelGetLayoutSize(void)
 
   // Sets a text and relais-out.
   controller->SetMarkupProcessorEnabled(true);
+  controller->SetRemoveFrontInset(true);
+  controller->SetRemoveBackInset(true);
   controller->SetText("<font family='TizenSansRegular' size='10'>Lorem ipsum dolor sit amet, aeque definiebas ea mei, posse iracundia ne cum.</font>");
   controller->Relayout(CONTROL_SIZE);
 
@@ -479,7 +481,9 @@ int UtcDaliTextViewModelGetGlyphsLayout(void)
   controller->Relayout(CONTROL_SIZE);
 
   // Elide the glyphs.
-  model->ElideGlyphs();
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(93u, 93u);
+  model->ElideGlyphs(fontClient);
 
   DALI_TEST_EQUALS(LOREM_NUMBER_OF_GLYPHS_ELIDED, model->GetNumberOfGlyphs(), TEST_LOCATION);
   DALI_TEST_CHECK(NULL != model->GetGlyphs());
@@ -568,7 +572,9 @@ int UtcDaliTextViewModelElideText01(void)
   const Vector2*         layoutsModel = model->GetLayout();
 
   // Elide the glyphs. Text shouldn't be elided with this configuration.
-  model->ElideGlyphs();
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(93u, 93u);
+  model->ElideGlyphs(fontClient);
 
   DALI_TEST_CHECK(glyphsModel == model->GetGlyphs());
   DALI_TEST_CHECK(layoutsModel == model->GetLayout());
@@ -587,7 +593,7 @@ int UtcDaliTextViewModelElideText01(void)
   DALI_TEST_EQUALS(0u, model->GetNumberOfLines(), TEST_LOCATION);
 
   // Elide the glyphs. Should not add the ellipsis glyph.
-  model->ElideGlyphs();
+  model->ElideGlyphs(fontClient);
 
   DALI_TEST_EQUALS(0u, model->GetNumberOfGlyphs(), TEST_LOCATION);
 
@@ -596,7 +602,7 @@ int UtcDaliTextViewModelElideText01(void)
   controller->Relayout(CONTROL_SIZE);
 
   // Elide the glyphs.
-  model->ElideGlyphs();
+  model->ElideGlyphs(fontClient);
 
   DALI_TEST_EQUALS(6u, model->GetNumberOfGlyphs(), TEST_LOCATION);
   DALI_TEST_EQUALS(2u, model->GetNumberOfLines(), TEST_LOCATION);
@@ -606,7 +612,7 @@ int UtcDaliTextViewModelElideText01(void)
   controller->Relayout(CONTROL_SIZE);
 
   // Elide the glyphs.
-  model->ElideGlyphs();
+  model->ElideGlyphs(fontClient);
 
   DALI_TEST_EQUALS(LOREM_NUMBER_OF_GLYPHS_ELIDED, model->GetNumberOfGlyphs(), TEST_LOCATION);
   DALI_TEST_EQUALS(LOREM_NUMBER_OF_LINES_ELIDED, model->GetNumberOfLines(), TEST_LOCATION);
@@ -629,7 +635,7 @@ int UtcDaliTextViewModelElideText01(void)
   controller->Relayout(Size(testWidth, sizeAB.height));
 
   // Elide the glyphs.
-  model->ElideGlyphs();
+  model->ElideGlyphs(fontClient);
   DALI_TEST_EQUALS(1u, model->GetNumberOfGlyphs(), TEST_LOCATION);
   DALI_TEST_EQUALS(1u, model->GetNumberOfLines(), TEST_LOCATION);
 
index 663e6db..47ac271 100644 (file)
@@ -51,6 +51,9 @@ int UtcDaliTextFieldMultipleBackgroundText(void)
   ControllerPtr                 controller     = textFieldImpl.GetTextController();
   Controller::Impl&             controllerImpl = Controller::Impl::GetImplementation(*controller.Get());
 
+  // Check the case where there is only one character in the text
+  controller->SetText("S");
+
   // Add multiple background colors for the text.
   ColorRun backgroundColorRun1;
   backgroundColorRun1.characterRun.characterIndex     = 0u;
@@ -70,19 +73,16 @@ int UtcDaliTextFieldMultipleBackgroundText(void)
   backgroundColorRun3.color                           = Color::GREEN;
   controllerImpl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun3);
 
-  // Check the case where there is only one character in the text
-  controller->SetText("S");
-
   application.SendNotification();
   application.Render();
 
   // The offscreen root actor should have one child: the renderable.
   Actor stencil = textField.GetChildAt(0u);
-  DALI_TEST_CHECK(stencil.GetChildCount() == 1u);
+  DALI_TEST_EQUALS(stencil.GetChildCount(), 1u, TEST_LOCATION);
 
   // The renderable actor should have two children: the text and the background.
   Actor renderableActor = stencil.GetChildAt(0u);
-  DALI_TEST_CHECK(renderableActor.GetChildCount() == 2u);
+  DALI_TEST_EQUALS(renderableActor.GetChildCount(), 2u, TEST_LOCATION);
 
   // Check that the background is created
   Actor backgroundActor = renderableActor.GetChildAt(0u);
@@ -92,6 +92,14 @@ int UtcDaliTextFieldMultipleBackgroundText(void)
   // Change the text to contain more characters
   controller->SetText("Text Multiple Background Test");
 
+  // After SetText, BackgroundColorRun should be empty.
+  DALI_TEST_CHECK(controllerImpl.mModel->mLogicalModel->mBackgroundColorRuns.Empty());
+
+  // Add multiple background colors for the text.
+  controllerImpl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun1);
+  controllerImpl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun2);
+  controllerImpl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun3);
+
   application.SendNotification();
   application.Render();
 
@@ -102,9 +110,9 @@ int UtcDaliTextFieldMultipleBackgroundText(void)
   application.Render();
 
   // Now the offscreen root actor should have four children: the renderable, the clipped cursor, the highlight, and the background.
-  DALI_TEST_CHECK(stencil.GetChildCount() == 4u);
+  DALI_TEST_EQUALS(stencil.GetChildCount(), 4u, TEST_LOCATION);
   // The renderable actor should have one child only: the text
-  DALI_TEST_CHECK(renderableActor.GetChildCount() == 1u);
+  DALI_TEST_EQUALS(renderableActor.GetChildCount(), 1u, TEST_LOCATION);
 
   // The background should now be lowered below the highlight
   backgroundActor = stencil.GetChildAt(0u);
index 31de850..47237f8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
 #include <stdlib.h>
 #include <iostream>
 
+#include <toolkit-event-thread-callback.h>
+
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/dali-toolkit.h>
 
@@ -31,6 +33,38 @@ using namespace Dali;
 using namespace Toolkit;
 using namespace Text;
 
+namespace
+{
+static int ASYNC_TEXT_THREAD_TIMEOUT = 5;
+
+static bool  gAsyncTextRenderedCalled;
+static float gAsyncTextRenderedWidth;
+static float gAsyncTextRenderedHeight;
+
+struct CallbackFunctor
+{
+  CallbackFunctor(bool* callbackFlag)
+  : mCallbackFlag(callbackFlag)
+  {
+  }
+
+  void operator()()
+  {
+    *mCallbackFlag = true;
+  }
+  bool* mCallbackFlag;
+};
+
+static void TestAsyncTextRendered(TextLabel control, float width, float height)
+{
+  tet_infoline(" TestAsyncTextRendered");
+  gAsyncTextRenderedCalled = true;
+  gAsyncTextRenderedWidth  = width;
+  gAsyncTextRenderedHeight = height;
+}
+
+} // namespace
+
 int UtcDaliTextLabelMarkupUnderline(void)
 {
   ToolkitTestApplication application;
@@ -1339,12 +1373,12 @@ int UtcDaliTextLabelMarkupSpanCharacterSpacing(void)
   END_TEST;
 }
 
-int UtcDaliTextLabelLocaleChange(void)
+int UtcDaliTextLabelLocaleChange01(void)
 {
   ToolkitTestApplication application;
-  tet_infoline(" UtcDaliTextLabelLocaleChange ");
+  tet_infoline(" UtcDaliTextLabelLocaleChange01");
 
-  Adaptor &adaptor = application.GetAdaptor();
+  Adaptor&  adaptor   = application.GetAdaptor();
   TextLabel textLabel = TextLabel::New();
   application.GetScene().Add(textLabel);
 
@@ -1360,4 +1394,123 @@ int UtcDaliTextLabelLocaleChange(void)
   DALI_TEST_EQUALS(newLocale.data(), GetImpl(textLabel).GetLocale(), TEST_LOCATION);
 
   END_TEST;
+}
+
+int UtcDaliTextLabelLocaleChange02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextLabelLocaleChange02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float expectedWidth  = 100.0f;
+  float expectedHeight = 100.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(false, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  Adaptor& adaptor = application.GetAdaptor();
+
+  application.SendNotification();
+  application.Render();
+
+  std::string newLocale = "label_TEST";
+  adaptor.LocaleChangedSignal().Emit(newLocale);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(newLocale.data(), GetImpl(label).GetLocale(), TEST_LOCATION);
+
+  // dummy text for test.
+  TextLabel dummy1 = TextLabel::New();
+  DALI_TEST_CHECK(dummy1);
+  dummy1.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy1.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+  dummy1.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy1.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy2 = TextLabel::New();
+  DALI_TEST_CHECK(dummy2);
+  dummy2.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy2.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+  dummy2.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy2.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  expectedWidth   = 50.0f;
+  expectedHeight  = 50.0f;
+  float dummySize = 100.0f;
+
+  std::string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+  dummy1.SetProperty(TextLabel::Property::TEXT, text);
+  dummy2.SetProperty(TextLabel::Property::TEXT, text);
+  label.SetProperty(TextLabel::Property::TEXT, text);
+
+  // Request size computation, due to dummy's requests, text manager's loader queue is full.
+  DevelTextLabel::RequestAsyncNaturalSize(dummy1);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy1, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy2);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy2, dummySize);
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  newLocale = "label_TEST_2";
+  adaptor.LocaleChangedSignal().Emit(newLocale);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(5, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(newLocale.data(), GetImpl(label).GetLocale(), TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index c5ad959..db4adcf 100644 (file)
@@ -42,6 +42,9 @@ void dali_textselectionpopupinternal_startup(void)
 void dali_textselectionpopupinternal_cleanup(void)
 {
   test_return_value = TET_PASS;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
+#endif
 }
 
 int UtcDaliToolkitTextSelectionPopupIconPropertiesN(void)
index a4e177c..04f9537 100644 (file)
@@ -27,7 +27,7 @@
 #include <dali-toolkit/internal/texture-manager/texture-async-loading-helper.h>
 #include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
 #include <dali-toolkit/internal/texture-manager/texture-upload-observer.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
 #include <dali-toolkit/internal/visuals/visual-factory-impl.h> ///< For VisualFactory's member TextureManager.
 #include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali-toolkit/public-api/image-loader/image.h>
@@ -53,6 +53,9 @@ void utc_dali_toolkit_texture_manager_startup(void)
 void utc_dali_toolkit_texture_manager_cleanup(void)
 {
   test_return_value = TET_PASS;
+#if defined(ELDBUS_ENABLED)
+  DBusWrapper::Install({}); // Clean up TestDBusWrapper
+#endif
 }
 
 namespace
index 77b2d14..40ae0b8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -71,9 +71,8 @@ int UtcDaliVisualAction(void)
 
   tet_infoline("Perform TEST_ACTION action on Visual. Should increase the action counter");
 
-  Property::Map                    attributes;
-  Toolkit::Internal::Visual::Base& internalVisualBase = GetImplementation(visualBaseHandle);
-  internalVisualBase.DoAction(Dali::Toolkit::Internal::DummyVisual::TEST_ACTION, attributes);
+  Property::Map attributes;
+  visualBaseHandle.DoAction(Dali::Toolkit::Internal::DummyVisual::TEST_ACTION, attributes);
   application.SendNotification();
   DALI_TEST_EQUALS(dummyVisualPtr->GetActionCounter(), 1, TEST_LOCATION);
 
@@ -113,7 +112,82 @@ int UtcDaliVisualActionNotImplemented(void)
 
   tet_infoline("Perform TEST_ACTION action on Color Visual which does not support it.. Should not increment the action counter");
   Property::Map attributes;
-  GetImplementation(visual).DoAction(Dali::Toolkit::Internal::DummyVisual::TEST_ACTION, attributes);
+  visual.DoAction(Dali::Toolkit::Internal::DummyVisual::TEST_ACTION, attributes);
+  application.SendNotification();
+  DALI_TEST_EQUALS(dummyVisualPtr->GetActionCounter(), 0, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualActionExtension(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Register an ImageVisual and and perform an Action with Any attributes on Visual directly");
+  Vector2 controlSize(20.f, 30.f);
+
+  //Created DummyVisual
+  Property::Map                     settings;
+  Toolkit::Internal::DummyVisualPtr dummyVisualPtr = Toolkit::Internal::DummyVisual::New(settings);
+
+  DummyControl        dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+
+  tet_infoline("Register visual and stage control");
+
+  Toolkit::Visual::Base visualBaseHandle = Toolkit::Visual::Base(dummyVisualPtr.Get());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visualBaseHandle);
+  dummyControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  tet_infoline("Check action counter is 0 before DoAction");
+  DALI_TEST_EQUALS(dummyVisualPtr->GetActionCounter(), 0, TEST_LOCATION);
+
+  tet_infoline("Perform TEST_ACTION_EXTENSION action on Visual. Should increase the action counter");
+  Dali::Any attributes;
+  visualBaseHandle.DoActionExtension(Dali::Toolkit::Internal::DummyVisual::TEST_ACTION_EXTENSION, attributes);
+  application.SendNotification();
+  DALI_TEST_EQUALS(dummyVisualPtr->GetActionCounter(), 1, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualActionExtensionNotImplemented(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Register an ImageVisual and and perform an ActionExtension on a Visual which does not support any Actions");
+  Vector2 controlSize(20.f, 30.f);
+
+  //Created DummyVisual
+  Property::Map                     settings;
+  Toolkit::Internal::DummyVisualPtr dummyVisualPtr = Toolkit::Internal::DummyVisual::New(settings);
+
+  DummyControl        dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+
+  tet_infoline("Register visual and stage control");
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
+  propertyMap.Insert(ColorVisual::Property::MIX_COLOR, Color::BLUE);
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+  dummyControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  tet_infoline("Check action counter is 0 before DoActionExtension");
+  DALI_TEST_EQUALS(dummyVisualPtr->GetActionCounter(), 0, TEST_LOCATION);
+
+  tet_infoline("Perform TEST_ACTION_EXTENSION action on Color Visual which does not support it.. Should not increment the action counter");
+  Dali::Any attributes;
+  visual.DoActionExtension(Dali::Toolkit::Internal::DummyVisual::TEST_ACTION_EXTENSION, attributes);
   application.SendNotification();
   DALI_TEST_EQUALS(dummyVisualPtr->GetActionCounter(), 0, TEST_LOCATION);
 
@@ -131,7 +205,7 @@ int UtcDaliVisualSetProperties(void)
   Property::Map propertyMap1;
   propertyMap1.Insert(Visual::Property::TYPE, Visual::COLOR);
   propertyMap1.Insert(ColorVisual::Property::MIX_COLOR, Color::RED);
-  Toolkit::Internal::ColorVisualPtr colorVisualPtr = Toolkit::Internal::ColorVisual::New(*factoryCache, propertyMap1);
+  Toolkit::Internal::DummyVisualPtr colorVisualPtr = Toolkit::Internal::DummyVisual::New(propertyMap1);
 
   DummyControl        dummyControl = DummyControl::New(true);
   Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
@@ -297,6 +371,7 @@ int UtcDaliArcVisualCreateInstancePropertyMap(void)
 
   END_TEST;
 }
+
 int UtcDaliVisualUpdateBrokenImageRenderer(void)
 {
   ToolkitTestApplication application;
@@ -340,3 +415,21 @@ int UtcDaliVisualUpdateBrokenImageRenderer(void)
 
   END_TEST;
 }
+
+int UtcDaliVisualCreateWithoutCoreN(void)
+{
+  // DO NOT USE Application for this UTC.
+  tet_infoline("UtcDaliVisualCreateWithoutCore Test, for line coverage");
+
+  //Created DummyVisual
+  Property::Map                     settings;
+  Toolkit::Internal::DummyVisualPtr dummyVisualPtr   = Toolkit::Internal::DummyVisual::New(settings);
+  Toolkit::Visual::Base             visualBaseHandle = Toolkit::Visual::Base(dummyVisualPtr.Get());
+
+  visualBaseHandle.Reset();
+
+  // Always success
+  DALI_TEST_CHECK(true);
+
+  END_TEST;
+}
index dcce7cd..f27d5cd 100644 (file)
@@ -58,6 +58,7 @@ SET(TEST_HARNESS_SOURCES
    ../dali-toolkit/dali-toolkit-test-utils/test-graphics-shader.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-render-controller.cpp
+   ../dali-toolkit/dali-toolkit-test-utils/test-render-surface.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-trace-call-stack.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-native-image.cpp
 )
index 9633069..61547b2 100644 (file)
@@ -45,6 +45,7 @@ SET(TEST_HARNESS_SOURCES
    ../dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-platform-abstraction.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-render-controller.cpp
+   ../dali-toolkit/dali-toolkit-test-utils/test-render-surface.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-trace-call-stack.cpp
    ../dali-toolkit/dali-toolkit-test-utils/test-native-image.cpp
 )
index 8d33504..340f5f5 100755 (executable)
@@ -11,6 +11,7 @@ SET(TC_SOURCES
   utc-Dali-AnimatedImageVisual.cpp
   utc-Dali-AnimatedVectorImageVisual.cpp
   utc-Dali-ArcVisual.cpp
+  utc-Dali-RenderEffect.cpp
   utc-Dali-BloomView.cpp
   utc-Dali-BubbleEmitter.cpp
   utc-Dali-Builder.cpp
@@ -55,6 +56,7 @@ SET(TC_SOURCES
   utc-Dali-TextField.cpp
   utc-Dali-TextGeometry.cpp
   utc-Dali-TextLabel.cpp
+  utc-Dali-TextLabel-Async.cpp
   utc-Dali-TextSelectionPopup.cpp
   utc-Dali-TextSelectionPopupMirroringLTR.cpp
   utc-Dali-TextSelectionPopupMirroringRTL.cpp
@@ -150,6 +152,7 @@ SET(TEST_HARNESS_SOURCES
   dali-toolkit-test-utils/test-graphics-shader.cpp
   dali-toolkit-test-utils/test-platform-abstraction.cpp
   dali-toolkit-test-utils/test-render-controller.cpp
+  dali-toolkit-test-utils/test-render-surface.cpp
   dali-toolkit-test-utils/test-trace-call-stack.cpp
   dali-toolkit-test-utils/test-native-image.cpp
   test-text-geometry-utils.cpp
@@ -171,9 +174,9 @@ FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
 ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
 
 INCLUDE_DIRECTORIES(
-    ../../../
-    ${${CAPI_LIB}_INCLUDE_DIRS}
-    dali-toolkit-test-utils
+  ../../../
+  ${${CAPI_LIB}_INCLUDE_DIRS}
+  dali-toolkit-test-utils
 )
 
 ADD_CUSTOM_COMMAND(
index 71bc051..1fa33c4 100644 (file)
@@ -29,7 +29,8 @@ TestApplication::TestApplication(uint32_t surfaceWidth,
                                  uint32_t verticalDpi,
                                  bool     initialize,
                                  bool     enablePartialUpdate)
-: mCore(NULL),
+: mRenderSurface(nullptr),
+  mCore(nullptr),
   mSurfaceWidth(surfaceWidth),
   mSurfaceHeight(surfaceHeight),
   mFrame(0u),
@@ -83,10 +84,10 @@ void TestApplication::CreateScene()
   mScene.SetDpi(Vector2(static_cast<float>(mDpi.x), static_cast<float>(mDpi.y)));
 
   // Create render target for the scene
+  mRenderSurface = new TestRenderSurface(Dali::PositionSize(0, 0, mSurfaceWidth, mSurfaceHeight));
   Graphics::RenderTargetCreateInfo rtInfo{};
   rtInfo.SetExtent({mSurfaceWidth, mSurfaceHeight});
-  rtInfo.SetSurface(&mSurfaceWidth); // Can point to anything, really.
-
+  rtInfo.SetSurface(mRenderSurface);
   mScene.SetSurfaceRenderTarget(rtInfo);
 
   mScenes.push_back(mScene);
@@ -100,6 +101,10 @@ void TestApplication::InitializeCore()
 
 TestApplication::~TestApplication()
 {
+  if(DALI_LIKELY(mCore))
+  {
+    mCore->ContextDestroyed();
+  }
   Dali::Integration::Log::UninstallLogFunction();
   delete mCore;
 }
index 555822d..6876dc8 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "test-graphics-controller.h"
 #include "test-render-controller.h"
+#include "test-render-surface.h"
 
 namespace Dali
 {
@@ -102,6 +103,7 @@ protected:
   TestPlatformAbstraction mPlatformAbstraction;
   TestRenderController    mRenderController;
   TestGraphicsController  mGraphicsController;
+  TestRenderSurface*      mRenderSurface;
 
   Integration::UpdateStatus mStatus;
   Integration::RenderStatus mRenderStatus;
index 84480ef..c30f1f2 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_GRAPHICS_COMMAND_BUFFER_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -66,6 +66,8 @@ enum class CommandType
   SET_DEPTH_TEST_ENABLE   = 1 << 25,
   SET_DEPTH_WRITE_ENABLE  = 1 << 26,
   DRAW_NATIVE             = 1 << 27,
+  BEGIN                   = 1 << 28,
+  END                     = 1 << 29
 };
 
 std::ostream& operator<<(std::ostream& os, Graphics::StencilOp op);
@@ -109,7 +111,7 @@ struct UniformBufferBindingDescriptor
   const TestGraphicsBuffer* buffer{nullptr};
   uint32_t                  binding{0u};
   uint32_t                  offset{0u};
-  bool                      emulated; ///<true if UBO is emulated for old gfx API
+  bool                      emulated; ///< true if UBO is emulated for old gfx API
 };
 
 /**
@@ -226,6 +228,11 @@ struct Command
   {
     switch(rhs.type)
     {
+      case CommandType::BEGIN:
+      {
+        data.begin = rhs.data.begin;
+        break;
+      }
       case CommandType::BEGIN_RENDER_PASS:
       {
         new(&data.beginRenderPass) CommandData::BeginRenderPassDescriptor(rhs.data.beginRenderPass);
@@ -374,6 +381,10 @@ struct Command
         data.depth.writeEnabled = rhs.data.depth.writeEnabled;
         break;
       }
+      default:
+      {
+        break;
+      }
     }
     type = rhs.type;
   }
@@ -386,6 +397,11 @@ struct Command
   {
     switch(rhs.type)
     {
+      case CommandType::BEGIN:
+      {
+        data.begin = rhs.data.begin;
+        break;
+      }
       case CommandType::BEGIN_RENDER_PASS:
       {
         new(&data.beginRenderPass) CommandData::BeginRenderPassDescriptor(std::move(rhs.data.beginRenderPass));
@@ -533,6 +549,10 @@ struct Command
         data.depth.writeEnabled = rhs.data.depth.writeEnabled;
         break;
       }
+      default:
+      {
+        break;
+      }
     }
     type = rhs.type;
   }
@@ -551,6 +571,11 @@ struct Command
 
     struct
     {
+      Graphics::CommandBufferBeginInfo info;
+    } begin;
+
+    struct
+    {
       std::vector<Graphics::TextureBinding> textureBindings;
     } bindTextures{};
 
@@ -666,6 +691,22 @@ public:
   {
   }
 
+  void Begin(const Graphics::CommandBufferBeginInfo& info) override
+  {
+    mCommands.emplace_back();
+    mCommands.back().type            = CommandType::BEGIN;
+    mCommands.back().data.begin.info = info;
+
+    TraceCallStack::NamedParams namedParams;
+    namedParams["usage"] << std::hex << info.usage;
+    mCallStack.PushCall("Begin", namedParams.str(), namedParams);
+  }
+  void End() override
+  {
+    mCommands.emplace_back(CommandType::END);
+    mCallStack.PushCall("End", "");
+  }
+
   void BindVertexBuffers(uint32_t                                    firstBinding,
                          const std::vector<const Graphics::Buffer*>& buffers,
                          const std::vector<uint32_t>&                offsets) override
@@ -1094,4 +1135,4 @@ private:
 
 } // namespace Dali
 
-#endif //DALI_TEST_GRAPHICS_COMMAND_BUFFER_H
+#endif // DALI_TEST_GRAPHICS_COMMAND_BUFFER_H
index 24bb51e..f0bc4d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@ struct TestGraphicsDeleter
   }
 };
 
-} //namespace
+} // namespace
 
 std::ostream& operator<<(std::ostream& o, const Graphics::BufferCreateInfo& bufferCreateInfo)
 {
@@ -635,14 +635,38 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com
   bool                     scissorEnabled = false;
   TestGraphicsFramebuffer* currentFramebuffer{nullptr};
   TestGraphicsPipeline*    currentPipeline{nullptr};
+  bool                     recording = false;
+  bool                     recorded  = false;
 
   for(auto& cmd : commandBuffer.GetCommands())
   {
     // process command
     switch(cmd.type)
     {
+      case CommandType::BEGIN:
+      {
+        if(recording)
+        {
+          fprintf(stderr, "ERROR: Should only call Begin once per cmd buffer\n");
+        }
+        recording = true;
+        break;
+      }
+      case CommandType::END:
+      {
+        if(!recording)
+        {
+          fprintf(stderr, "ERROR: Should only call End following a Begin");
+        }
+        recorded  = true;
+        recording = false;
+        break;
+      }
       case CommandType::FLUSH:
       {
+        if(recording)
+        {
+        }
         // Nothing to do here
         break;
       }
@@ -1009,6 +1033,11 @@ void TestGraphicsController::ProcessCommandBuffer(TestGraphicsCommandBuffer& com
       }
     }
   }
+
+  if(!recorded)
+  {
+    fprintf(stderr, "ERROR: No command buffer was recorded");
+  }
 }
 
 void TestGraphicsController::BindPipeline(TestGraphicsPipeline* pipeline)
index 6066f67..e21c304 100644 (file)
@@ -442,9 +442,85 @@ public: // Test Functions
     mCustomUniforms = customUniforms;
   }
 
+  constexpr std::pair<uint32_t, uint32_t> GetUniformBufferArrayStrideAndTypeSize(TestGraphicsReflection::TestUniformInfo& uniformInfo, uint32_t requestedStride)
+  {
+    uint32_t dataTypeSize  = 0;
+    uint32_t elementStride = 0;
+    switch(uniformInfo.type)
+    {
+      case Property::FLOAT:
+      case Property::INTEGER:
+      case Property::BOOLEAN:
+      {
+        dataTypeSize = sizeof(float);
+        break;
+      }
+      case Property::MATRIX:
+      {
+        dataTypeSize = sizeof(float) * 16;
+        break;
+      }
+      case Property::MATRIX3:
+      {
+        dataTypeSize = sizeof(float) * 9;
+        break;
+      }
+      case Property::VECTOR2:
+      {
+        dataTypeSize = sizeof(float) * 2;
+        break;
+      }
+      case Property::VECTOR3:
+      {
+        dataTypeSize = sizeof(float) * 3;
+        break;
+      }
+      case Property::VECTOR4:
+      {
+        dataTypeSize = sizeof(float) * 4;
+        break;
+      }
+      default:
+      {
+      }
+    }
+    if(uniformInfo.elementStride)
+    {
+      bool roundUp  = (dataTypeSize % uniformInfo.elementStride);
+      elementStride = (dataTypeSize / uniformInfo.elementStride) * uniformInfo.elementStride + (roundUp ? uniformInfo.elementStride : 0);
+    }
+    return std::make_pair(dataTypeSize, elementStride);
+  }
+
+  void AddMemberToUniformBlock(TestGraphicsReflection::TestUniformBlockInfo& blockInfo,
+                               std::string                                   name,
+                               Property::Type                                type,
+                               uint32_t                                      elementCount,
+                               uint32_t                                      elementStrideInBytes)
+  {
+    TestGraphicsReflection::TestUniformInfo info;
+    info.name          = std::move(name);
+    info.type          = type;
+    info.uniformClass  = Graphics::UniformClass::UNIFORM;
+    info.numElements   = elementCount;
+    info.locations     = {0};
+    info.bufferIndex   = 0;                    // this will update when AddCustomUniformBlock called
+
+    auto retval= GetUniformBufferArrayStrideAndTypeSize(info, elementStrideInBytes);
+    info.elementStride = std::max(retval.first, retval.second);
+    info.offsets       = {blockInfo.size};
+    blockInfo.size += (elementCount == 0 ? 1 : elementCount) * std::max(retval.first, retval.second);
+    blockInfo.members.emplace_back(info);
+  }
+
   void AddCustomUniformBlock(const TestGraphicsReflection::TestUniformBlockInfo& blockInfo)
   {
     mCustomUniformBlocks.push_back(blockInfo);
+    auto& info = mCustomUniformBlocks.back();
+    for(auto& member : info.members)
+    {
+      member.bufferIndex = mCustomUniformBlocks.size();
+    }
   }
 
   void ClearSubmitStack()
index 22d09e1..2d507f7 100644 (file)
@@ -325,13 +325,14 @@ bool TestGraphicsReflection::GetUniformBlock(uint32_t index, Dali::Graphics::Uni
   out.size = block.size;
   for(auto i = 0u; i < out.members.size(); ++i)
   {
-    const auto& memberUniform   = block.members[i];
-    out.members[i].name         = memberUniform.name;
-    out.members[i].binding      = block.binding;
-    out.members[i].uniformClass = Graphics::UniformClass::UNIFORM;
-    out.members[i].offset       = memberUniform.offsets[0];
-    out.members[i].location     = memberUniform.locations[0];
-    out.members[i].elementCount = memberUniform.numElements;
+    const auto& memberUniform    = block.members[i];
+    out.members[i].name          = memberUniform.name;
+    out.members[i].binding       = block.binding;
+    out.members[i].uniformClass  = Graphics::UniformClass::UNIFORM;
+    out.members[i].offset        = memberUniform.offsets[0];
+    out.members[i].location      = memberUniform.locations[0];
+    out.members[i].elementCount  = memberUniform.numElements;
+    out.members[i].elementStride = memberUniform.elementStride;
   }
 
   return true;
index b598435..3521ed0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_GRAPHICS_REFLECTION_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -60,6 +60,7 @@ public: // Test methods
     std::vector<uint32_t>  locations{};
     uint32_t               numElements{0u}; // 0 elements means this isn't an array; 1 element means this is an array of size 1
     Property::Type         type;
+    uint32_t               elementStride{0u}; // array element stride, 0 - tightly packed
   };
 
   struct TestUniformBlockInfo
index 290f663..fa5ac89 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -84,6 +84,13 @@ bool TestPlatformAbstraction::LoadShaderBinaryFile(const std::string& filename,
   return mLoadFileResult.loadResult;
 }
 
+bool TestPlatformAbstraction::SaveShaderBinaryFile(const std::string& filename, const unsigned char* buffer, unsigned int numBytes) const
+{
+  mTrace.PushCall("SaveShaderBinaryFile", "");
+
+  return mSaveFileResult;
+}
+
 /** Call this every test */
 void TestPlatformAbstraction::Initialize()
 {
index 3146e2d..707e2fe 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TEST_PLATFORM_ABSTRACTION_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -85,10 +85,7 @@ public:
   /**
    * @copydoc PlatformAbstraction::SaveShaderBinaryFile()
    */
-  virtual bool SaveShaderBinaryFile(const std::string& filename, const unsigned char* buffer, unsigned int numBytes) const override
-  {
-    return true;
-  }
+  bool SaveShaderBinaryFile(const std::string& filename, const unsigned char* buffer, unsigned int numBytes) const override;
 
   /**
    * @copydoc PlatformAbstraction::StartTimer()
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-render-surface.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-render-surface.cpp
new file mode 100644 (file)
index 0000000..60a0ad0
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test-render-surface.h"
+
+namespace Dali
+{
+TestRenderSurface::TestRenderSurface(Dali::PositionSize positionSize)
+: mPositionSize(positionSize),
+  mBackgroundColor()
+{
+}
+
+TestRenderSurface::~TestRenderSurface()
+{
+}
+
+Dali::PositionSize TestRenderSurface::GetPositionSize() const
+{
+  return mPositionSize;
+};
+
+void TestRenderSurface::GetDpi(unsigned int& dpiHorizontal, unsigned int& dpiVertical)
+{
+  dpiHorizontal = dpiVertical = 96;
+};
+
+int TestRenderSurface::GetSurfaceOrientation() const
+{
+  return 0;
+}
+
+int TestRenderSurface::GetScreenOrientation() const
+{
+  return 0;
+}
+
+void TestRenderSurface::InitializeGraphics()
+{
+}
+
+void TestRenderSurface::CreateSurface()
+{
+}
+
+void TestRenderSurface::DestroySurface()
+{
+}
+
+bool TestRenderSurface::ReplaceGraphicsSurface()
+{
+  return false;
+}
+
+void TestRenderSurface::MoveResize(Dali::PositionSize positionSize)
+{
+  mPositionSize = positionSize;
+}
+
+void TestRenderSurface::StartRender()
+{
+}
+
+bool TestRenderSurface::PreRender(bool resizingSurface, const std::vector<Rect<int>>& damageRects, Rect<int>& clippingRect)
+{
+  return true;
+}
+
+void TestRenderSurface::PostRender()
+{
+}
+
+void TestRenderSurface::StopRender()
+{
+}
+
+void TestRenderSurface::ReleaseLock()
+{
+}
+
+void TestRenderSurface::SetThreadSynchronization(ThreadSynchronizationInterface& threadSynchronization)
+{
+}
+
+Dali::Integration::RenderSurfaceInterface::Type TestRenderSurface::GetSurfaceType()
+{
+  return WINDOW_RENDER_SURFACE;
+}
+
+void TestRenderSurface::MakeContextCurrent()
+{
+}
+
+Integration::DepthBufferAvailable TestRenderSurface::GetDepthBufferRequired()
+{
+  return Integration::DepthBufferAvailable::TRUE;
+}
+
+Integration::StencilBufferAvailable TestRenderSurface::GetStencilBufferRequired()
+{
+  return Integration::StencilBufferAvailable::TRUE;
+}
+
+} // namespace Dali
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-render-surface.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-render-surface.h
new file mode 100644 (file)
index 0000000..b0f7a94
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef TEST_RENDER_SURFACE_H
+#define TEST_RENDER_SURFACE_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "render-surface-interface.h"
+
+namespace Dali
+{
+/**
+ * Concrete implementation of the RenderSurface class.
+ */
+class TestRenderSurface : public Dali::Integration::RenderSurfaceInterface
+{
+public:
+  /**
+   * @copydoc Dali::Integration::RenderSurface::RenderSurface
+   */
+  TestRenderSurface(Dali::PositionSize positionSize);
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::~RenderSurface
+   */
+  ~TestRenderSurface() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetPositionSize
+   */
+  Dali::PositionSize GetPositionSize() const override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetDpi
+   */
+  void GetDpi(unsigned int& dpiHorizontal, unsigned int& dpiVertical) override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetSurfaceOrientation
+   */
+  int GetSurfaceOrientation() const override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetScreenOrientation
+   */
+  int GetScreenOrientation() const override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::InitializeGraphics
+   */
+  void InitializeGraphics() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::CreateSurface
+   */
+  void CreateSurface() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::DestroySurface
+   */
+  void DestroySurface() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::ReplaceGraphicsSurface
+   */
+  bool ReplaceGraphicsSurface() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::MoveResize
+   */
+  void MoveResize(Dali::PositionSize positionSize) override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::StartRender
+   */
+  void StartRender() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::PreRender
+   */
+  bool PreRender(bool resizingSurface, const std::vector<Rect<int>>& damageRects, Rect<int>& clippingRect) override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::PostRender
+   */
+  void PostRender() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::StopRender
+   */
+  void StopRender() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::ReleaseLock
+   */
+  void ReleaseLock() override;
+
+  void SetThreadSynchronization(ThreadSynchronizationInterface& threadSynchronization) override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetSurfaceType
+   */
+  Dali::Integration::RenderSurfaceInterface::Type GetSurfaceType() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::MakeContextCurrent
+   */
+  void MakeContextCurrent() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetDepthBufferRequired
+   */
+  Integration::DepthBufferAvailable GetDepthBufferRequired() override;
+
+  /**
+   * @copydoc Dali::Integration::RenderSurface::GetStencilBufferRequired
+   */
+  Integration::StencilBufferAvailable GetStencilBufferRequired() override;
+
+private:
+  /**
+   * @brief Undefined copy constructor. RenderSurface cannot be copied
+   */
+  TestRenderSurface(const TestRenderSurface& rhs);
+
+  /**
+   * @brief Undefined assignment operator. RenderSurface cannot be copied
+   */
+  TestRenderSurface& operator=(const TestRenderSurface& rhs);
+
+private:
+  Dali::PositionSize mPositionSize;
+  Vector4            mBackgroundColor; ///< The background color of the surface
+};
+
+} // namespace Dali
+
+#endif // TEST_RENDER_SURFACE_H
index 6a69f93..f4a61f8 100644 (file)
@@ -75,9 +75,9 @@ public:
 
   static Integration::Scene GetScene(Dali::Window window);
 
-  Dali::RenderSurfaceInterface& GetSurface();
-  Dali::WindowContainer         GetWindows();
-  Dali::SceneHolderList         GetSceneHolders();
+  Dali::Integration::RenderSurfaceInterface& GetSurface();
+  Dali::WindowContainer                      GetWindows();
+  Dali::SceneHolderList                      GetSceneHolders();
 
   Dali::Internal::Adaptor::SceneHolder* GetWindow(Dali::Actor& actor);
   void                                  AddWindow(Internal::Adaptor::SceneHolder* window);
@@ -85,6 +85,8 @@ public:
 
   void RegisterProcessor(Integration::Processor& processor, bool postProcessor = false);
   void UnregisterProcessor(Integration::Processor& processor, bool postProcessor = false);
+  void RegisterProcessorOnce(Integration::Processor& processor, bool postProcessor = false);
+  void UnregisterProcessorOnce(Integration::Processor& processor, bool postProcessor = false);
 
   void SetApplication(Dali::TestApplication& testApplication);
 
index 40c73ae..488d2d6 100644 (file)
@@ -114,8 +114,12 @@ bool Adaptor::AddIdle(CallbackBase* callback, bool hasReturnValue)
 
 void Adaptor::RemoveIdle(CallbackBase* callback)
 {
-  mCallbacks.Erase(std::remove_if(mCallbacks.Begin(), mCallbacks.End(), [&callback](CallbackBase* current) { return callback == current; }), mCallbacks.End());
-  mReturnCallbacks.Erase(std::remove_if(mReturnCallbacks.Begin(), mReturnCallbacks.End(), [&callback](CallbackBase* current) { return callback == current; }), mReturnCallbacks.End());
+  mCallbacks.Erase(std::remove_if(mCallbacks.Begin(), mCallbacks.End(), [&callback](CallbackBase* current)
+                                  { return callback == current; }),
+                   mCallbacks.End());
+  mReturnCallbacks.Erase(std::remove_if(mReturnCallbacks.Begin(), mReturnCallbacks.End(), [&callback](CallbackBase* current)
+                                        { return callback == current; }),
+                         mReturnCallbacks.End());
 }
 
 void Adaptor::RunIdles()
@@ -152,11 +156,11 @@ void Adaptor::RequestUpdateOnce()
   }
 }
 
-Dali::RenderSurfaceInterface& Adaptor::GetSurface()
+Dali::Integration::RenderSurfaceInterface& Adaptor::GetSurface()
 {
   DALI_ASSERT_ALWAYS(!mWindows.empty());
 
-  return reinterpret_cast<Dali::RenderSurfaceInterface&>(mWindows.front()->GetRenderSurface());
+  return reinterpret_cast<Dali::Integration::RenderSurfaceInterface&>(mWindows.front()->GetRenderSurface());
 }
 
 Dali::WindowContainer Adaptor::GetWindows()
@@ -237,6 +241,20 @@ void Adaptor::UnregisterProcessor(Integration::Processor& processor, bool postPr
   core.UnregisterProcessor(processor, postProcessor);
 }
 
+void Adaptor::RegisterProcessorOnce(Integration::Processor& processor, bool postProcessor)
+{
+  Integration::Core& core = mTestApplication->GetCore();
+  tet_printf("Adaptor::RegisterProcessorOnce : %s\n", processor.GetProcessorName().data());
+  core.RegisterProcessorOnce(processor, postProcessor);
+}
+
+void Adaptor::UnregisterProcessorOnce(Integration::Processor& processor, bool postProcessor)
+{
+  Integration::Core& core = mTestApplication->GetCore();
+  tet_printf("Adaptor::UnregisterProcessorOnce : %s\n", processor.GetProcessorName().data());
+  core.UnregisterProcessorOnce(processor, postProcessor);
+}
+
 void Adaptor::SetApplication(Dali::TestApplication& testApplication)
 {
   mTestApplication = &testApplication;
@@ -304,11 +322,11 @@ void Adaptor::RemoveIdle(CallbackBase* callback)
   mImpl->RemoveIdle(callback);
 }
 
-void Adaptor::ReplaceSurface(Window window, Dali::RenderSurfaceInterface& surface)
+void Adaptor::ReplaceSurface(Window window, Dali::Integration::RenderSurfaceInterface& surface)
 {
 }
 
-void Adaptor::ReplaceSurface(Dali::Integration::SceneHolder window, Dali::RenderSurfaceInterface& surface)
+void Adaptor::ReplaceSurface(Dali::Integration::SceneHolder window, Dali::Integration::RenderSurfaceInterface& surface)
 {
 }
 
@@ -327,7 +345,7 @@ Adaptor::WindowCreatedSignalType& Adaptor::WindowCreatedSignal()
   return mImpl->WindowCreatedSignal();
 }
 
-Dali::RenderSurfaceInterface& Adaptor::GetSurface()
+Dali::Integration::RenderSurfaceInterface& Adaptor::GetSurface()
 {
   return mImpl->GetSurface();
 }
@@ -459,4 +477,14 @@ void Adaptor::UnregisterProcessor(Integration::Processor& processor, bool postPr
   mImpl->UnregisterProcessor(processor, postProcessor);
 }
 
+void Adaptor::RegisterProcessorOnce(Integration::Processor& processor, bool postProcessor)
+{
+  mImpl->RegisterProcessorOnce(processor, postProcessor);
+}
+
+void Adaptor::UnregisterProcessorOnce(Integration::Processor& processor, bool postProcessor)
+{
+  mImpl->UnregisterProcessorOnce(processor, postProcessor);
+}
+
 } // namespace Dali
index 3bd51bc..def6ba3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,12 +37,18 @@ const char* GetEnvironmentVariable(const char* variable)
   {
     return value->second.c_str();
   }
+  // Get http_proxy from real environment variables
+  if(std::string(variable) == std::string("http_proxy"))
+  {
+    auto* httpProxyResult = std::getenv("http_proxy");
+    return (gEnvironmentVariables[variable] = (httpProxyResult ? httpProxyResult : "")).c_str();
+  }
   return nullptr;
 }
 
 void SetTestEnvironmentVariable(const char* variable, const char* value)
 {
-  gEnvironmentVariables[variable] = value;
+  gEnvironmentVariables[variable] = value ? value : "";
 }
 
 } // namespace EnvironmentVariable
index 42050e4..e43b4be 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_SCENE_HOLDER_IMPL_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-#include <dali/integration-api/adaptor-framework/render-surface-interface.h>
-
 #include <dali/integration-api/scene.h>
 #include <dali/public-api/actors/layer.h>
 #include <dali/public-api/object/base-object.h>
+#include "test-render-surface.h"
 
-namespace Dali
-{
+#include <dali/public-api/events/hover-event.h>
+#include <dali/public-api/events/touch-event.h>
 
-class TestRenderSurface : public Dali::RenderSurfaceInterface
+namespace Dali
 {
-public:
-
-  TestRenderSurface( PositionSize positionSize ) : mPositionSize(positionSize) {};
-
-  PositionSize GetPositionSize() const override { return mPositionSize; };
-
-  virtual void GetDpi( unsigned int& dpiHorizontal, unsigned int& dpiVertical ) { dpiHorizontal = dpiVertical = 96; }
-
-  void InitializeGraphics() override {};
-
-  void CreateSurface() override {};
-
-  void DestroySurface() override {};
-
-  bool ReplaceGraphicsSurface() override { return false; };
-
-  void MoveResize( Dali::PositionSize positionSize ) override { mPositionSize = positionSize; };
-
-  void StartRender() override {};
-
-  bool PreRender( bool resizingSurface, const std::vector<Rect<int>>& damagedRects, Rect<int>& clippingRect  ) override { return false; };
-
-  void PostRender()
-  {
-  }
-
-  //void PostRender( bool renderToFbo, bool replacingSurface, bool resizingSurface, const std::vector<Rect<int>>& damagedRects ) override {};
-
-  void StopRender() override {};
-
-  void ReleaseLock() override {};
-
-  void SetThreadSynchronization( ThreadSynchronizationInterface& threadSynchronization ) override {};
-
-  RenderSurfaceInterface::Type GetSurfaceType() override { return RenderSurfaceInterface::WINDOW_RENDER_SURFACE; };
-
-  void MakeContextCurrent() override {};
-
-  Integration::DepthBufferAvailable GetDepthBufferRequired() override { return Integration::DepthBufferAvailable::FALSE; };
-
-  Integration::StencilBufferAvailable GetStencilBufferRequired() override { return Integration::StencilBufferAvailable::FALSE; };
-
-  int GetSurfaceOrientation() const override {return 0;};
-
-  int GetScreenOrientation() const override {return 0;};
-
-  void SetBackgroundColor( Vector4 color ) {};
-
-  Vector4 GetBackgroundColor() { return Color::WHITE; };
-
-private:
-  PositionSize mPositionSize;
-};
-
 namespace Internal
 {
-
 namespace Adaptor
 {
-
 class SceneHolder : public Dali::BaseObject
 {
 public:
-
-  SceneHolder( const Dali::Rect<int>& positionSize );
+  SceneHolder(const Dali::Rect<int>& positionSize);
 
   virtual ~SceneHolder();
 
-  void Add( Dali::Actor actor );
+  void Add(Dali::Actor actor);
 
-  void Remove( Dali::Actor actor );
+  void Remove(Dali::Actor actor);
 
   Dali::Layer GetRootLayer() const;
 
-  void SetBackgroundColor( Vector4 color );
+  void SetBackgroundColor(Vector4 color);
 
   Vector4 GetBackgroundColor() const;
 
-  void FeedTouchPoint( Dali::TouchPoint& point, int timeStamp );
+  void FeedTouchPoint(Dali::TouchPoint& point, int timeStamp);
 
-  void FeedWheelEvent( Dali::WheelEvent& wheelEvent );
+  void FeedWheelEvent(Dali::WheelEvent& wheelEvent);
 
-  void FeedKeyEvent( Dali::KeyEvent& keyEvent );
+  void FeedKeyEvent(Dali::KeyEvent& keyEvent);
 
   Dali::Integration::SceneHolder::KeyEventSignalType& KeyEventSignal();
 
@@ -125,32 +67,38 @@ public:
 
   Integration::Scene GetScene();
 
-  Dali::RenderSurfaceInterface& GetRenderSurface();
+  Dali::Integration::RenderSurfaceInterface& GetRenderSurface();
 
   Dali::RenderTaskList GetRenderTaskList();
 
-protected:
+  class SceneHolderLifeCycleObserver;
+  std::unique_ptr<SceneHolderLifeCycleObserver> mLifeCycleObserver; ///< The adaptor life cycle observer
+  Dali::TouchEvent                              mLastTouchEvent;
+  Dali::HoverEvent                              mLastHoverEvent;
+
+  uint32_t                 mId;             ///< A unique ID to identify the SceneHolder starting from 0
+  Dali::Integration::Scene mScene{nullptr}; ///< The Scene
+  std::string              mName;           ///< The name of the SceneHolder
 
-  TestRenderSurface mRenderSurface;
-  Integration::Scene mScene;
+  TestRenderSurface* mRenderSurface;
 };
 
 } // namespace Adaptor
 
 } // namespace Internal
 
-inline Internal::Adaptor::SceneHolder& GetImplementation( Dali::Integration::SceneHolder& sceneHolder )
+inline Internal::Adaptor::SceneHolder& GetImplementation(Dali::Integration::SceneHolder& sceneHolder)
 {
-  DALI_ASSERT_ALWAYS( sceneHolder && "SceneHolder handle is empty" );
+  DALI_ASSERT_ALWAYS(sceneHolder && "SceneHolder handle is empty");
   BaseObject& object = sceneHolder.GetBaseObject();
-  return static_cast<Internal::Adaptor::SceneHolder&>( object );
+  return static_cast<Internal::Adaptor::SceneHolder&>(object);
 }
 
-inline const Internal::Adaptor::SceneHolder& GetImplementation( const Dali::Integration::SceneHolder& sceneHolder )
+inline const Internal::Adaptor::SceneHolder& GetImplementation(const Dali::Integration::SceneHolder& sceneHolder)
 {
-  DALI_ASSERT_ALWAYS( sceneHolder && "SceneHolder handle is empty" );
+  DALI_ASSERT_ALWAYS(sceneHolder && "SceneHolder handle is empty");
   const BaseObject& object = sceneHolder.GetBaseObject();
-  return static_cast<const Internal::Adaptor::SceneHolder&>( object );
+  return static_cast<const Internal::Adaptor::SceneHolder&>(object);
 }
 
 } // namespace Dali
index 22a027b..8f4f8b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/public-api/render-tasks/render-task-list.h>
 #include <toolkit-adaptor-impl.h>
+#include "test-render-surface.h"
 
 using AdaptorImpl = Dali::Internal::Adaptor::Adaptor;
 
@@ -42,9 +43,41 @@ namespace Internal
 {
 namespace Adaptor
 {
+class SceneHolder::SceneHolderLifeCycleObserver
+{
+public:
+  SceneHolderLifeCycleObserver(Adaptor*& adaptor, bool& adaptorStarted)
+  : mAdaptor(adaptor),
+    mAdaptorStarted(adaptorStarted)
+  {
+  }
+
+private: // Adaptor::LifeCycleObserver interface
+  virtual void OnStart()
+  {
+    mAdaptorStarted = true;
+  };
+  virtual void OnPause(){};
+  virtual void OnResume(){};
+  virtual void OnStop()
+  {
+    // Mark adaptor as stopped;
+    mAdaptorStarted = false;
+  };
+  virtual void OnDestroy()
+  {
+    mAdaptor = nullptr;
+  };
+
+private:
+  Adaptor*& mAdaptor;
+  bool&     mAdaptorStarted;
+};
+
 SceneHolder::SceneHolder(const Dali::Rect<int>& positionSize)
-: mRenderSurface(positionSize),
-  mScene(Dali::Integration::Scene::New(Dali::Size(static_cast<float>(positionSize.width), static_cast<float>(positionSize.height))))
+: mId(0),
+  mScene(Dali::Integration::Scene::New(Dali::Size(static_cast<float>(positionSize.width), static_cast<float>(positionSize.height)))),
+  mRenderSurface(new TestRenderSurface(positionSize))
 {
 }
 
@@ -123,9 +156,9 @@ Integration::Scene SceneHolder::GetScene()
   return mScene;
 }
 
-Dali::RenderSurfaceInterface& SceneHolder::GetRenderSurface()
+Dali::Integration::RenderSurfaceInterface& SceneHolder::GetRenderSurface()
 {
-  return mRenderSurface;
+  return *mRenderSurface;
 }
 
 Dali::RenderTaskList SceneHolder::GetRenderTaskList()
index 840cc13..48091cd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -54,7 +54,6 @@ ToolkitTestApplication::ToolkitTestApplication(size_t surfaceWidth, size_t surfa
 
   // Core needs to be initialized next before we start the adaptor
   InitializeCore();
-  Accessibility::Accessible::SetObjectRegistry(mCore->GetObjectRegistry());
 
   // This will also emit the window created signals
   AdaptorImpl::GetImpl(*mAdaptor).Start(mMainWindow);
@@ -76,6 +75,12 @@ ToolkitTestApplication::~ToolkitTestApplication()
   Dali::LifecycleController lifecycleController = Dali::LifecycleController::Get();
   lifecycleController.TerminateSignal().Emit();
 
+  // Stop adaptor after terminate signal emit
+  if(Dali::Adaptor::IsAvailable() && mAdaptor)
+  {
+    mAdaptor->Stop();
+  }
+
   // Need to delete core before we delete the adaptor.
   delete mCore;
   mCore = NULL;
index 57a438e..e9a1212 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -277,7 +277,7 @@ public:
     }
   }
 
-  Length Shape(unsigned int const* text, unsigned int numChars, unsigned int fontId, Script script)
+  Length Shape(TextAbstraction::FontClient& fontClient, unsigned int const* text, unsigned int numChars, unsigned int fontId, Script script)
   {
     mText     = new unsigned char[numChars];
     mNumChars = numChars;
@@ -640,9 +640,9 @@ void Shaping::GetGlyphs(GlyphInfo* glyphStore, unsigned int* mappingTable)
   GetImplementation(*this).GetGlyphs(glyphStore, mappingTable);
 }
 
-Length Shaping::Shape(unsigned int const* text, unsigned int numChars, unsigned int fontId, Script script)
+Length Shaping::Shape(TextAbstraction::FontClient& fontClient, unsigned int const* text, unsigned int numChars, unsigned int fontId, Script script)
 {
-  return GetImplementation(*this).Shape(text, numChars, fontId, script);
+  return GetImplementation(*this).Shape(fontClient, text, numChars, fontId, script);
 }
 
 } // namespace TextAbstraction
index 9909164..899c098 100755 (executable)
@@ -129,6 +129,16 @@ public:
 
   }
 
+  void SceneConnection()
+  {
+
+  }
+
+  void SceneDisconnection()
+  {
+
+  }
+
 public:
 
   std::string mUrl;
@@ -365,5 +375,15 @@ void VideoPlayer::LowerToBottom()
   Internal::Adaptor::GetImplementation( *this ).LowerToBottom();
 }
 
+void VideoPlayer::SceneConnection()
+{
+  Internal::Adaptor::GetImplementation( *this ).SceneConnection();
+}
+
+void VideoPlayer::SceneDisconnection()
+{
+  Internal::Adaptor::GetImplementation( *this ).SceneDisconnection();
+}
+
 } // namespace Dali;
 
index 19f32b3..71436e8 100644 (file)
@@ -509,6 +509,16 @@ public:
   {
     return true;
   }
+
+  int GetPolicyDecisionError() const override
+  {
+    return 1;
+  }
+
+  bool SuspendPolicyDecision() const override
+  {
+    return true;
+  }
 };
 
 class MockWebEngineHttpAuthHandler : public Dali::WebEngineHttpAuthHandler
@@ -1181,6 +1191,9 @@ public:
   {
     return nullptr;
   }
+  void ChangeOrientation(int orientation) override
+  {
+  }
   std::string GetUrl() const override
   {
     return std::string();
@@ -1276,6 +1289,9 @@ public:
   void AddJavaScriptMessageHandler(const std::string& exposedObjectName, JavaScriptMessageHandlerCallback handler) override
   {
   }
+  void AddJavaScriptEntireMessageHandler(const std::string& exposedObjectName, JavaScriptEntireMessageHandlerCallback handler) override
+  {
+  }
   void RegisterJavaScriptAlertCallback(JavaScriptAlertCallback callback) override
   {
   }
@@ -1423,6 +1439,9 @@ public:
   {
     return false;
   }
+  void ExitFullscreen() override
+  {
+  }
   void RegisterFrameRenderedCallback(WebEngineFrameRenderedCallback callback) override
   {
   }
@@ -1456,6 +1475,9 @@ public:
   void RegisterNavigationPolicyDecidedCallback(WebEngineNavigationPolicyDecidedCallback callback) override
   {
   }
+  void RegisterNewWindowPolicyDecidedCallback(WebEngineNewWindowPolicyDecidedCallback callback) override
+  {
+  }
   void RegisterNewWindowCreatedCallback(WebEngineNewWindowCreatedCallback callback) override
   {
   }
@@ -1474,6 +1496,15 @@ public:
   void RegisterContextMenuHiddenCallback(WebEngineContextMenuHiddenCallback callback) override
   {
   }
+  void RegisterFullscreenEnteredCallback(WebEngineFullscreenEnteredCallback callback) override
+  {
+  }
+  void RegisterFullscreenExitedCallback(WebEngineFullscreenExitedCallback callback) override
+  {
+  }
+  void RegisterTextFoundCallback(WebEngineTextFoundCallback callback) override
+  {
+  }
   void GetPlainTextAsynchronously(PlainTextReceivedCallback callback) override
   {
   }
@@ -1604,6 +1635,7 @@ public:
   void LoadUrl(const std::string& url)
   {
     mUrl = url;
+    SetAccessibilityAddress();
     ConnectToGlobalSignal(&OnLoadUrl);
   }
 
@@ -1806,7 +1838,12 @@ public:
 
   Dali::Accessibility::Address GetAccessibilityAddress()
   {
-    return {":9.99", "root"};
+    return mAccessibilityAddress;
+  }
+
+  void SetAccessibilityAddress()
+  {
+    mAccessibilityAddress = {":9.99", "root"};
   }
 
   Dali::PixelData GetScreenshot(Dali::Rect<int32_t> viewArea, float scaleFactor)
@@ -1940,6 +1977,26 @@ public:
     }
   }
 
+  void RegisterNewWindowPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback callback)
+  {
+    mNewWindowPolicyDecidedCallback = callback;
+  }
+
+  void RegisterFullscreenEnteredCallback(Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback callback)
+  {
+    mFullscreenEnteredCallback = callback;
+  }
+
+  void RegisterFullscreenExitedCallback(Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback callback)
+  {
+    mFullscreenExitedCallback = callback;
+  }
+
+  void RegisterTextFoundCallback(Dali::WebEnginePlugin::WebEngineTextFoundCallback callback)
+  {
+    mTextFoundCallback = callback;
+  }
+
   std::string              mUrl;
   std::vector<std::string> mHistory;
   size_t                   mCurrentPlusOnePos;
@@ -1956,6 +2013,7 @@ public:
   Dali::Vector2             mContentSize;
   WebEngineBackForwardList* mockWebEngineBackForwardList;
   WebEngineSettings*        mockWebEngineSettings;
+  Dali::Accessibility::Address mAccessibilityAddress{};
 
   std::vector<Dali::WebEnginePlugin::JavaScriptMessageHandlerCallback> mResultCallbacks;
 
@@ -1984,6 +2042,10 @@ public:
   Dali::WebEnginePlugin::GeolocationPermissionCallback            mGeolocationPermissionCallback;
   Dali::WebEnginePlugin::WebEngineHitTestCreatedCallback          mHitTestCreatedCallback;
   Dali::WebEnginePlugin::PlainTextReceivedCallback                mPlainTextReceivedCallback;
+  Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback  mNewWindowPolicyDecidedCallback;
+  Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback       mFullscreenEnteredCallback;
+  Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback        mFullscreenExitedCallback;
+  Dali::WebEnginePlugin::WebEngineTextFoundCallback               mTextFoundCallback;
 };
 
 namespace
@@ -2097,6 +2159,23 @@ bool OnLoadUrl()
       std::unique_ptr<Dali::WebEngineContextMenu> hiddenMenu(new MockWebEngineContextMenu());
       gInstance->mContextMenuHiddenCallback(std::move(hiddenMenu));
     }
+    if(gInstance->mNewWindowPolicyDecidedCallback)
+    {
+      std::unique_ptr<Dali::WebEnginePolicyDecision> policyDecision(new MockWebEnginePolicyDecision());
+      gInstance->mNewWindowPolicyDecidedCallback(std::move(policyDecision));
+    }
+    if(gInstance->mFullscreenEnteredCallback)
+    {
+      gInstance->mFullscreenEnteredCallback();
+    }
+    if(gInstance->mFullscreenExitedCallback)
+    {
+      gInstance->mFullscreenExitedCallback();
+    }
+    if(gInstance->mTextFoundCallback)
+    {
+      gInstance->mTextFoundCallback(1);
+    }
   }
   return false;
 }
@@ -2428,6 +2507,10 @@ NativeImageSourcePtr WebEngine::GetNativeImageSource()
   return sourcePtr;
 }
 
+void WebEngine::ChangeOrientation(int orientation)
+{
+}
+
 void WebEngine::LoadHtmlString(const std::string& htmlString)
 {
 }
@@ -2520,6 +2603,10 @@ void WebEngine::AddJavaScriptMessageHandler(const std::string& exposedObjectName
 {
 }
 
+void WebEngine::AddJavaScriptEntireMessageHandler(const std::string& exposedObjectName, std::function<void(const std::string&, const std::string&)> handler)
+{
+}
+
 void WebEngine::RegisterJavaScriptAlertCallback(Dali::WebEnginePlugin::JavaScriptAlertCallback callback)
 {
   Internal::Adaptor::GetImplementation(*this).RegisterJavaScriptAlertCallback(callback);
@@ -2704,6 +2791,10 @@ bool WebEngine::SendWheelEvent(const WheelEvent& event)
   return true;
 }
 
+void WebEngine::ExitFullscreen()
+{
+}
+
 void WebEngine::SetFocus(bool focused)
 {
 }
@@ -2839,4 +2930,25 @@ void WebEngine::GetPlainTextAsynchronously(Dali::WebEnginePlugin::PlainTextRecei
   Internal::Adaptor::GetImplementation(*this).GetPlainTextAsynchronously(callback);
 }
 
+void WebEngine::RegisterNewWindowPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback callback)
+{
+  Internal::Adaptor::GetImplementation(*this).RegisterNewWindowPolicyDecidedCallback(callback);
+}
+
+void WebEngine::RegisterFullscreenEnteredCallback(Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback callback)
+{
+  Internal::Adaptor::GetImplementation(*this).RegisterFullscreenEnteredCallback(callback);
+}
+
+void WebEngine::RegisterFullscreenExitedCallback(Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback callback)
+{
+  Internal::Adaptor::GetImplementation(*this).RegisterFullscreenExitedCallback(callback);
+}
+
+void WebEngine::RegisterTextFoundCallback(Dali::WebEnginePlugin::WebEngineTextFoundCallback callback)
+{
+  Internal::Adaptor::GetImplementation(*this).RegisterTextFoundCallback(callback);
+}
+
+
 } // namespace Dali
index b770e14..d19da07 100644 (file)
@@ -59,26 +59,26 @@ Window* Window::New(const PositionSize& positionSize, const std::string& name, c
 
 Dali::Window::WindowPosition Window::GetPosition() const
 {
-  PositionSize positionSize = mRenderSurface.GetPositionSize();
+  PositionSize positionSize = mRenderSurface->GetPositionSize();
 
   return Dali::Window::WindowPosition(positionSize.x, positionSize.y);
 }
 
 PositionSize Window::GetPositionSize() const
 {
-  return mRenderSurface.GetPositionSize();
+  return mRenderSurface->GetPositionSize();
 }
 
 Dali::Window::WindowSize Window::GetSize() const
 {
-  PositionSize positionSize = mRenderSurface.GetPositionSize();
+  PositionSize positionSize = mRenderSurface->GetPositionSize();
 
   return Dali::Window::WindowSize(positionSize.width, positionSize.height);
 }
 
 void Window::SetPositionSize(PositionSize positionSize)
 {
-  mRenderSurface.MoveResize(positionSize);
+  mRenderSurface->MoveResize(positionSize);
 
   Uint16Pair   newSize(positionSize.width, positionSize.height);
   Dali::Window handle(this);
@@ -155,7 +155,7 @@ Integration::Scene Window::GetScene()
   return GetImplementation(*this).GetScene();
 }
 
-Dali::RenderSurfaceInterface& Window::GetRenderSurface()
+Dali::Integration::RenderSurfaceInterface& Window::GetRenderSurface()
 {
   return GetImplementation(*this).GetRenderSurface();
 }
@@ -193,12 +193,14 @@ void Window::Raise()
 void Window::Show()
 {
   GetImplementation(*this).mVisible = true;
+  GetImplementation(*this).GetScene().Show();
   GetImplementation(*this).mVisibilityChangedSignal.Emit(*this, true);
 }
 
 void Window::Hide()
 {
   GetImplementation(*this).mVisible = false;
+  GetImplementation(*this).GetScene().Hide();
   GetImplementation(*this).mVisibilityChangedSignal.Emit(*this, false);
 }
 
index 2c3eb28..d4901f8 100644 (file)
@@ -18,7 +18,7 @@
  *
  */
 
-//EXTERNAL INCLUDES
+// EXTERNAL INCLUDES
 #include <dali/integration-api/scene.h>
 #include <dali/public-api/math/int-pair.h>
 #include <dali/public-api/math/rect.h>
@@ -71,22 +71,22 @@ public:
   Window&       operator=(Window&& rhs);
   static Window DownCast(BaseHandle handle);
 
-  Integration::Scene            GetScene();
-  Dali::RenderSurfaceInterface& GetRenderSurface();
-  void                          Add(Dali::Actor actor);
-  void                          Remove(Dali::Actor actor);
-  Dali::Layer                   GetRootLayer() const;
-  void                          SetBackgroundColor(const Vector4& color);
-  Vector4                       GetBackgroundColor() const;
-  void                          Raise();
-  void                          Show();
-  void                          Hide();
-  bool                          IsVisible() const;
-  FocusChangeSignalType&        FocusChangeSignal();
-  KeyEventSignalType&           KeyEventSignal();
-  TouchEventSignalType&         TouchedSignal();
-  ResizeSignalType&             ResizeSignal();
-  Dali::RenderTaskList          GetRenderTaskList();
+  Integration::Scene                         GetScene();
+  Dali::Integration::RenderSurfaceInterface& GetRenderSurface();
+  void                                       Add(Dali::Actor actor);
+  void                                       Remove(Dali::Actor actor);
+  Dali::Layer                                GetRootLayer() const;
+  void                                       SetBackgroundColor(const Vector4& color);
+  Vector4                                    GetBackgroundColor() const;
+  void                                       Raise();
+  void                                       Show();
+  void                                       Hide();
+  bool                                       IsVisible() const;
+  FocusChangeSignalType&                     FocusChangeSignal();
+  KeyEventSignalType&                        KeyEventSignal();
+  TouchEventSignalType&                      TouchedSignal();
+  ResizeSignalType&                          ResizeSignal();
+  Dali::RenderTaskList                       GetRenderTaskList();
 
 public:
   explicit Window(Internal::Adaptor::Window* window);
index ba02bef..26e4a94 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -51,6 +51,11 @@ const char* TEST_IMAGE_FILE_NAME      = TEST_RESOURCE_DIR "/application-icon-%02
 const char* TEST_GIF_FILE_NAME        = TEST_RESOURCE_DIR "/anim.gif";
 const char* TEST_MASK_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/mask.png";
 const char* TEST_WEBP_FILE_NAME       = TEST_RESOURCE_DIR "/dali-logo.webp";
+
+const char* TEST_N_PATCH_IMAGE_FILE_NAME         = TEST_RESOURCE_DIR "/heartsframe.9.png";
+const char* TEST_SVG_FILE_NAME                   = TEST_RESOURCE_DIR "/svg1.svg";
+const char* TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/insta_camera.json";
+
 } // namespace
 
 void CopyUrlsIntoArray(Property::Array& urls, int startIndex = 0)
@@ -93,7 +98,8 @@ int UtcDaliAnimatedImageVisualGetPropertyMap01(void)
       .Add(DevelVisual::Property::CORNER_RADIUS_POLICY, Visual::Transform::Policy::ABSOLUTE)
       .Add(DevelVisual::Property::BORDERLINE_WIDTH, 33.3f)
       .Add(DevelVisual::Property::BORDERLINE_COLOR, Color::RED)
-      .Add(DevelVisual::Property::BORDERLINE_OFFSET, 0.3f));
+      .Add(DevelVisual::Property::BORDERLINE_OFFSET, 0.3f)
+      .Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 2.0f));
 
   Property::Map resultMap;
   animatedImageVisual.CreatePropertyMap(resultMap);
@@ -167,6 +173,10 @@ int UtcDaliAnimatedImageVisualGetPropertyMap01(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<int>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
 
+  value = resultMap.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR, Property::FLOAT);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_EQUALS(value->Get<float>(), 2.0f, TEST_LOCATION);
+
   // Natural size getted as desired size
   Vector2 naturalSize;
   animatedImageVisual.GetNaturalSize(naturalSize);
@@ -225,7 +235,8 @@ int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
       .Add("cornerRadiusPolicy", Visual::Transform::Policy::RELATIVE)
       .Add("borderlineWidth", 20.0f)
       .Add("borderlineColor", Vector4())
-      .Add("borderlineOffset", -1.0f));
+      .Add("borderlineOffset", -1.0f)
+      .Add("frameSpeedFactor", 0.5f));
 
   Property::Map resultMap;
   animatedImageVisual.CreatePropertyMap(resultMap);
@@ -321,6 +332,10 @@ int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<int>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
 
+  value = resultMap.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR, Property::FLOAT);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.5f, TEST_LOCATION);
+
   END_TEST;
 }
 
@@ -545,6 +560,112 @@ int UtcDaliAnimatedImageVisualImageLoadingFail01(void)
   END_TEST;
 }
 
+int UtcDaliAnimatedImageVisualImageLoadingFail02(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline("Test with non-animated single image. We should show broken image than.");
+
+  for(int isSynchronousLoading = 0; isSynchronousLoading < 2; ++isSynchronousLoading)
+  {
+    tet_printf("Test to load non-animatable image %s\n", (isSynchronousLoading == 1) ? "Synchronously" : "Asynchronously");
+
+    Property::Map propertyMap;
+    propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+    propertyMap.Insert(ImageVisual::Property::URL, "dummy");
+    propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, (isSynchronousLoading == 1));
+
+    VisualFactory factory = VisualFactory::Get();
+    Visual::Base  visual  = factory.CreateVisual(propertyMap);
+
+    DummyControl        dummyControl = DummyControl::New(true);
+    Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+    dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+    dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+
+    DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 0u, TEST_LOCATION);
+
+    application.GetScene().Add(dummyControl);
+
+    application.SendNotification();
+    application.Render(20);
+
+    if(!(isSynchronousLoading == 1))
+    {
+      DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+    }
+
+    application.SendNotification();
+    application.Render(20);
+
+    // Check broken image uploaded.
+    DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 1u, TEST_LOCATION);
+
+    dummyControl.Unparent();
+
+    // Remove cached image at TextureManager.
+    application.SendNotification();
+    application.Render(20);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliAnimatedImageVisualImageLoadingFail03(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline("Test with invalid image that suffix is .gif, and AnimatedImageLoading not supported. We should show broken image than.");
+
+  for(int isSynchronousLoading = 0; isSynchronousLoading < 2; ++isSynchronousLoading)
+  {
+    tet_printf("Test to load non-animatable image %s\n", (isSynchronousLoading == 1) ? "Synchronously" : "Asynchronously");
+
+    Property::Map propertyMap;
+    propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+    propertyMap.Insert(ImageVisual::Property::URL, "dummy.Gif"); ///< Suffix is gif so visual become AnimatedImageVisual. But AnimatedImageLoading become null.
+    propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, (isSynchronousLoading == 1));
+
+    VisualFactory factory = VisualFactory::Get();
+    Visual::Base  visual  = factory.CreateVisual(propertyMap);
+
+    DummyControl        dummyControl = DummyControl::New(true);
+    Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+    dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+    dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+
+    DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 0u, TEST_LOCATION);
+
+    application.GetScene().Add(dummyControl);
+
+    application.SendNotification();
+    application.Render(20);
+
+    // TODO : Since fixed-image-cache didn't support synchronous loading now, we need to wait for a while.
+    // We have to remove it in future!
+    //if(!(isSynchronousLoading == 1))
+    {
+      DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+    }
+
+    application.SendNotification();
+    application.Render(20);
+
+    // Check broken image uploaded.
+    DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 1u, TEST_LOCATION);
+
+    dummyControl.Unparent();
+
+    // Remove cached image at TextureManager.
+    application.SendNotification();
+    application.Render(20);
+  }
+
+  END_TEST;
+}
+
 int UtcDaliAnimatedImageVisualSynchronousLoading(void)
 {
   ToolkitTestApplication application;
@@ -775,7 +896,7 @@ int UtcDaliAnimatedImageVisualJumpToAction(void)
 
     DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedImageVisual::Action::JUMP_TO, 6);
 
-    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(6), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(4), true, TEST_LOCATION);
     DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 4, TEST_LOCATION);
 
     dummyControl.Unparent();
@@ -938,6 +1059,7 @@ int UtcDaliAnimatedImageVisualAnimatedImage01(void)
     propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 2);
     propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 4);
     propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 20);
+    propertyMap.Insert(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 1.5f);
 
     VisualFactory factory = VisualFactory::Get();
     Visual::Base  visual  = factory.CreateVisual(propertyMap);
@@ -1190,6 +1312,7 @@ int UtcDaliAnimatedImageVisualMultiImage01(void)
     propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 4);
     propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 8);
     propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 100);
+    propertyMap.Insert(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 1.5f);
 
     VisualFactory factory = VisualFactory::Get();
     Visual::Base  visual  = factory.CreateVisual(propertyMap);
@@ -1643,6 +1766,8 @@ int UtcDaliAnimatedImageVisualMultiImage05(void)
   END_TEST;
 }
 
+namespace
+{
 void TestLoopCount(ToolkitTestApplication& application, DummyControl& dummyControl, uint16_t frameCount, uint16_t loopCount, const char* location)
 {
   TestGlAbstraction& gl           = application.GetGlAbstraction();
@@ -1693,6 +1818,7 @@ void TestLoopCount(ToolkitTestApplication& application, DummyControl& dummyContr
 
   dummyControl.Unparent();
 }
+} // namespace
 
 int UtcDaliAnimatedImageVisualLoopCount(void)
 {
@@ -1937,6 +2063,10 @@ int UtcDaliAnimatedImageVisualDesiredSize(void)
   int desiredWidth  = 15;
   int desiredHeight = 20;
 
+  // texture size have to keep it's ratio. So, the size of texture should be 20x20.
+  const int resultWidth  = 20;
+  const int resultHeight = 20;
+
   Visual::Base visual = VisualFactory::Get().CreateVisual(TEST_GIF_FILE_NAME, ImageDimensions(desiredWidth, desiredHeight));
   DALI_TEST_CHECK(visual);
 
@@ -1960,7 +2090,7 @@ int UtcDaliAnimatedImageVisualDesiredSize(void)
 
   {
     std::stringstream out;
-    out << GL_TEXTURE_2D << ", " << 0u << ", " << desiredWidth << ", " << desiredHeight;
+    out << GL_TEXTURE_2D << ", " << 0u << ", " << resultWidth << ", " << resultHeight;
     DALI_TEST_CHECK(textureTrace.FindMethodAndParams("TexImage2D", out.str().c_str()));
   }
 
@@ -1987,8 +2117,8 @@ int UtcDaliAnimatedImageVisualDesiredSize(void)
 
   {
     std::stringstream out;
-    out << GL_TEXTURE_2D << ", " << 0u << ", " << desiredWidth << ", " << desiredHeight;
-    DALI_TEST_CHECK(textureTrace.FindMethodAndParams("TexImage2D", out.str().c_str())); // The size should not be changed
+    out << GL_TEXTURE_2D << ", " << 0u << ", " << resultWidth << ", " << resultHeight;
+    DALI_TEST_CHECK(textureTrace.FindMethodAndParams("TexImage2D", out.str().c_str())); // The size should be changed for keeping the aspect ratio.
   }
 
   END_TEST;
@@ -2072,10 +2202,10 @@ int UtcDaliAnimatedImageVisualControlVisibilityChanged(void)
   END_TEST;
 }
 
-int UtcDaliAnimatedImageVisualWindowVisibilityChanged(void)
+int UtcDaliAnimatedImageVisualInheritedVisibilityChanged(void)
 {
   ToolkitTestApplication application;
-  tet_infoline("UtcDaliAnimatedImageVisualWindowVisibilityChanged");
+  tet_infoline("UtcDaliAnimatedImageVisualInheritedVisibilityChanged");
 
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_IMAGE)
@@ -2199,3 +2329,37 @@ int UtcDaliAnimatedImageVisualFrameCountBeforeLoadingFinished(void)
 
   END_TEST;
 }
+
+int UtcDaliAnimatedImageVisualLoadNonRegularImage(void)
+{
+  ToolkitTestApplication application;
+
+  std::vector<std::pair<std::string, DevelVisual::Type>> urlAndExpectVisualTypes = {
+    {TEST_MASK_IMAGE_FILE_NAME, DevelVisual::IMAGE},
+    {TEST_N_PATCH_IMAGE_FILE_NAME, DevelVisual::N_PATCH},
+    {TEST_SVG_FILE_NAME, DevelVisual::SVG},
+    {TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME, DevelVisual::ANIMATED_VECTOR_IMAGE},
+  };
+
+  for(const auto& urlAndExpectVisualTypePair : urlAndExpectVisualTypes)
+  {
+    tet_printf("Test AnimatedImageVisual with url: %s\n", urlAndExpectVisualTypePair.first.c_str());
+    Property::Map propertyMap;
+    propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+    propertyMap.Insert(ImageVisual::Property::URL, urlAndExpectVisualTypePair.first);
+    propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+
+    Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    Property::Map resultMap;
+    visual.CreatePropertyMap(resultMap);
+
+    Property::Value* typeValue = resultMap.Find(Toolkit::Visual::Property::TYPE, Property::INTEGER);
+    DALI_TEST_CHECK(typeValue);
+    int type = typeValue->Get<int>();
+    DALI_TEST_EQUALS(type, static_cast<int>(urlAndExpectVisualTypePair.second), TEST_LOCATION);
+  }
+
+  END_TEST;
+}
index 55891b8..9ee13ac 100644 (file)
@@ -249,6 +249,7 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual03(void)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
     .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
     .Add(DevelImageVisual::Property::PLAY_RANGE, playRange)
+    .Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 2.0f)
     .Add(DevelVisual::Property::CORNER_RADIUS, 50.0f)
     .Add(DevelVisual::Property::BORDERLINE_WIDTH, 20.0f)
     .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
@@ -313,6 +314,7 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
     .Add("redrawInScalingDown", false)
     .Add("enableFrameCache", false)
     .Add("notifyAfterRasterization", false)
+    .Add("frameSpeedFactor", 0.5f)
     .Add("cornerRadius", cornerRadius)
     .Add("borderlineWidth", borderlineWidth)
     .Add("borderlineColor", borderlineColor)
@@ -391,6 +393,10 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<bool>() == false);
 
+  value = resultMap.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR, Property::FLOAT);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_CHECK(value->Get<float>() == 0.5f);
+
   value = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
   DALI_TEST_CHECK(value);
   DALI_TEST_EQUALS(value->Get<Vector4>(), Vector4(cornerRadius, cornerRadius, cornerRadius, cornerRadius), TEST_LOCATION);
@@ -1920,6 +1926,119 @@ int UtcDaliAnimatedVectorImageVisualLoopingMode(void)
   END_TEST;
 }
 
+int UtcDaliAnimatedVectorImageVisualFrameSpeedFactor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliAnimatedVectorImageVisualFrameSpeedFactor");
+
+  Property::Map propertyMap;
+  propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
+    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
+    .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
+
+  Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New(true);
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+  Vector2 controlSize(20.f, 30.f);
+  actor.SetProperty(Actor::Property::SIZE, controlSize);
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  Property::Value* value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 1.0f, TEST_LOCATION); // Check default value is 1.0f
+
+  Property::Map attributes;
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 0.5f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.5f, TEST_LOCATION);
+
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 8.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 8.0f, TEST_LOCATION);
+
+  // TODO : Below logic might be changed in future.
+
+  // Clampled by maximum frame speed factor.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 100.0f + 1.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 100.0f, TEST_LOCATION);
+
+  // Clampled by minimum frame speed factor.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, 0.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.01f, TEST_LOCATION);
+
+  // Clampled by minimum frame speed factor 2.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, -1.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.01f, TEST_LOCATION);
+
+  // Clampled by minimum frame speed factor 3.
+  attributes.Clear();
+  attributes.Add(DevelImageVisual::Property::FRAME_SPEED_FACTOR, -100.0f - 1.0f);
+
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+  application.SendNotification();
+  application.Render();
+
+  map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+  value = map.Find(DevelImageVisual::Property::FRAME_SPEED_FACTOR);
+  DALI_TEST_EQUALS(value->Get<float>(), 0.01f, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliAnimatedVectorImageVisualPropertyNotification(void)
 {
   ToolkitTestApplication application;
@@ -2147,10 +2266,10 @@ int UtcDaliAnimatedVectorImageVisualControlVisibilityChanged(void)
   END_TEST;
 }
 
-int UtcDaliAnimatedVectorImageVisualWindowVisibilityChanged(void)
+int UtcDaliAnimatedVectorImageVisualInheritedVisibilityChanged(void)
 {
   ToolkitTestApplication application;
-  tet_infoline("UtcDaliAnimatedVectorImageVisualWindowVisibilityChanged");
+  tet_infoline("UtcDaliAnimatedVectorImageVisualInheritedVisibilityChanged");
 
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
@@ -2724,4 +2843,58 @@ int UtcDaliAnimatedVectorImageNativeTextureChangeShader(void)
   Test::VectorAnimationRenderer::UseNativeImageTexture(false);
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliAnimatedVectorImageVisualDestroyApplicationWhenFrameDropped(void)
+{
+  try
+  {
+    {
+      ToolkitTestApplication application;
+      tet_infoline("UtcDaliAnimatedVectorImageVisualDestroyApplicationWhenFrameDropped");
+
+      Property::Map propertyMap;
+      propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
+        .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME_FRAME_DROP)
+        .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
+
+      Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
+      DALI_TEST_CHECK(visual);
+
+      DummyControl      actor     = DummyControl::New(true);
+      DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+      dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+      Vector2 controlSize(20.f, 30.f);
+      actor.SetProperty(Actor::Property::SIZE, controlSize);
+
+      application.GetScene().Add(actor);
+
+      application.SendNotification();
+      application.Render();
+
+      // Trigger count is 2 - load, render the first frame
+      DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+      Property::Map attributes;
+      DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
+
+      // Make delay to drop frames
+      Test::VectorAnimationRenderer::DelayRendering(500); // block Rasterize thread near 500 ms
+
+      // Request 1 frame rendering.
+      application.SendNotification();
+      application.Render();
+
+      // Destroy applicatoin immediately.
+    }
+
+    tet_result(TET_PASS);
+  }
+  catch(...)
+  {
+    tet_result(TET_FAIL);
+  }
+
+  END_TEST;
+}
index 6d29bf5..febdf97 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,8 +30,8 @@
 #include <dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-task.h>
 #include <dali/devel-api/adaptor-framework/canvas-renderer/canvas-renderer-shape.h>
 #include <dali/devel-api/adaptor-framework/canvas-renderer/canvas-renderer.h>
-#include <dali/public-api/images/pixel-data.h>
 #include <dali/public-api/adaptor-framework/async-task-manager.h>
+#include <dali/public-api/images/pixel-data.h>
 
 using namespace Dali;
 using namespace Toolkit;
@@ -357,7 +357,7 @@ int UtcDaliCanvasViewRasterizeThreadRasterizationCompletedSignalP(void)
 
   gRasterizationCompletedSignal = false;
 
-  Dali::Toolkit::CanvasView            canvasView              = Dali::Toolkit::CanvasView::New(Vector2(100, 100));
+  Dali::Toolkit::CanvasView canvasView = Dali::Toolkit::CanvasView::New(Vector2(100, 100));
 
   Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
   DALI_TEST_CHECK(dummyCanvasRenderer);
@@ -503,3 +503,85 @@ int UtcDaliCanvasViewSychronousLoading(void)
 
   END_TEST;
 }
+
+int UtcDaliCanvasViewRasterizationRequestManually(void)
+{
+  ToolkitTestApplication application;
+
+  CanvasView canvasView = CanvasView::New(Vector2(300, 300));
+  DALI_TEST_CHECK(canvasView);
+
+  application.GetScene().Add(canvasView);
+
+  canvasView.SetProperty(Actor::Property::SIZE, Vector2(300, 300));
+  canvasView.SetProperty(Toolkit::CanvasView::Property::SYNCHRONOUS_LOADING, false);
+
+  Dali::CanvasRenderer::Shape shape = Dali::CanvasRenderer::Shape::New();
+
+  shape.AddRect(Rect<float>(10, 10, 10, 10), Vector2(0, 0));
+
+  canvasView.AddDrawable(shape);
+
+  application.SendNotification();
+  application.Render();
+
+  // Rasterization occured
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  bool isRasterizationManually = canvasView.GetProperty(Toolkit::CanvasView::Property::RASTERIZATION_REQUEST_MANUALLY).Get<bool>();
+  DALI_TEST_EQUALS(isRasterizationManually, false, TEST_LOCATION);
+
+  shape.AddRect(Rect<float>(10, 10, 10, 10), Vector2(0, 0));
+  application.SendNotification();
+  application.Render();
+
+  shape.AddRect(Rect<float>(10, 10, 10, 10), Vector2(0, 0));
+  application.SendNotification();
+  application.Render();
+
+  // Check if the canvasView is rasterized.
+  // Rasterization occured
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  canvasView.SetProperty(Toolkit::CanvasView::Property::RASTERIZATION_REQUEST_MANUALLY, true);
+
+  application.SendNotification();
+  application.Render();
+
+  // Rasterization occured
+  // (Note that we cannot 'cancel' the latest rasterization request even if we set RASTERIZATION_REQUEST_MANUALLY to true)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  isRasterizationManually = canvasView.GetProperty(Toolkit::CanvasView::Property::RASTERIZATION_REQUEST_MANUALLY).Get<bool>();
+  DALI_TEST_EQUALS(isRasterizationManually, true, TEST_LOCATION);
+
+  shape.AddRect(Rect<float>(10, 10, 10, 10), Vector2(0, 0));
+  application.SendNotification();
+  application.Render();
+
+  // Check if the canvasView is not rasterized.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 0), false, TEST_LOCATION);
+
+  Dali::CanvasRenderer::Shape shape2 = Dali::CanvasRenderer::Shape::New();
+
+  shape2.AddRect(Rect<float>(10, 10, 10, 10), Vector2(0, 0));
+
+  canvasView.AddDrawable(shape2);
+
+  application.SendNotification();
+  application.Render();
+
+  // Check whether the canvasView is not rasterized even if we add drawables.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 0), false, TEST_LOCATION);
+
+  // Request rasterize manually
+  canvasView.RequestRasterization();
+
+  application.SendNotification();
+  application.Render();
+
+  // Check if the canvasView is rasterized.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  END_TEST;
+}
index 3d21701..089fd8f 100644 (file)
@@ -655,7 +655,7 @@ int UtcDaliControlImplOnAccessibilityActivatedP(void)
   DALI_TEST_CHECK(handle);
 
   Property::Map attributes;
-  DALI_TEST_EQUALS(false, handle.DoAction("accessibilityActivated", attributes), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, handle.DoAction("activate", attributes), TEST_LOCATION);
 
   END_TEST;
 }
index 41393d8..fee0114 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -99,6 +99,10 @@ int UtcDaliGaussianBlurViewNew(void)
 
   Toolkit::GaussianBlurView view2 = Toolkit::GaussianBlurView::New(5, 1.5f, Pixel::RGB888, 0.5f, 0.5f, false);
   DALI_TEST_CHECK(view2);
+
+  // Don't assert even if we give big number.
+  Toolkit::GaussianBlurView view3 = Toolkit::GaussianBlurView::New(2147483647, 1.5f, Pixel::RGB888, 0.5f, 0.5f, false);
+  DALI_TEST_CHECK(view3);
   END_TEST;
 }
 
@@ -210,21 +214,21 @@ int UtcDaliGaussianBlurActivateDeactivateRepeat(void)
   application.SendNotification();
   application.Render(20);
 
-  DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 3, TEST_LOCATION);
+  DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 4, TEST_LOCATION);
 
   view.Deactivate();
 
   application.SendNotification();
   application.Render(20);
 
-  DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 3, TEST_LOCATION);
+  DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 4, TEST_LOCATION);
 
   view.Activate();
 
   application.SendNotification();
   application.Render(20);
 
-  DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 6, TEST_LOCATION);
+  DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 8, TEST_LOCATION);
   END_TEST;
 }
 
@@ -313,14 +317,14 @@ int UtcDaliGaussianBlurActivateOnce2(void)
   application.SendNotification();
   application.Render(20);
 
-  DALI_TEST_CHECK(gl.GetLastGenTextureId() == 3);
+  DALI_TEST_CHECK(gl.GetLastGenTextureId() == 4);
 
   view.ActivateOnce();
 
   application.SendNotification();
   application.Render(20);
 
-  DALI_TEST_CHECK(gl.GetLastGenTextureId() == 6);
+  DALI_TEST_CHECK(gl.GetLastGenTextureId() == 8);
 
   END_TEST;
 }
index efc772d..c60d4ee 100644 (file)
@@ -1184,17 +1184,18 @@ int UtcDaliImageViewSetImageUrl(void)
   END_TEST;
 }
 
-bool    gResourceReadySignalFired = false;
-Vector3 gNaturalSize;
+namespace
+{
+bool gResourceReadySignalFired = false;
 
 void ResourceReadySignal(Control control)
 {
   gResourceReadySignalFired = true;
 }
 
-void OnResourceReadySignalSVG(Control control)
+void OnResourceReadySyncSVGLoading02(Control control)
 {
-  // Check whether Image Visual transforms on ImageVieiw::OnRelayout()
+  // Check whether Image Visual transforms on ImageView::OnRelayout()
   Toolkit::Internal::Control& controlImpl = Toolkit::Internal::GetImplementation(control);
   Toolkit::Visual::Base       imageVisual = DevelControl::GetVisual(controlImpl, ImageView::Property::IMAGE);
   Property::Map               resultMap;
@@ -1205,10 +1206,14 @@ void OnResourceReadySignalSVG(Control control)
   Property::Map* retMap = transformValue->GetMap();
   DALI_TEST_CHECK(retMap);
 
-  // Fitting mode should not be applied at this point
-  DALI_TEST_EQUALS(retMap->Find(Visual::Transform::Property::SIZE)->Get<Vector2>(), Vector2::ZERO, TEST_LOCATION);
+  auto size = retMap->Find(Visual::Transform::Property::SIZE)->Get<Vector2>();
+
+  // Fitting mode is applied at this point. because we do FittingMode in control
+  DALI_TEST_EQUALS(size, Vector2(100.0f, 100.0f), TEST_LOCATION);
 }
 
+} // namespace
+
 int UtcDaliImageViewCheckResourceReady(void)
 {
   ToolkitTestApplication application;
@@ -1499,10 +1504,15 @@ int UtcDaliImageViewReloadAlphaMaskImage(void)
   END_TEST;
 }
 
+namespace
+{
+Vector3 gNaturalSize;
+
 void OnRelayoutOverride(Size size)
 {
   gNaturalSize = size; // Size Relayout is using
 }
+} // namespace
 
 int UtcDaliImageViewReplaceImageAndGetNaturalSize(void)
 {
@@ -2922,8 +2932,17 @@ int UtcDaliImageViewLoadRemoteSVG(void)
     application.Render();
 
     DALI_TEST_EQUALS(imageView.GetRendererCount(), 1u, TEST_LOCATION);
+
+    imageView.Unparent();
   }
 
+  // Insure to Remove svg cache.
+  application.SendNotification();
+  application.Render();
+  application.RunIdles();
+  application.SendNotification();
+  application.Render();
+
   // Without size set
   {
     Toolkit::ImageView imageView;
@@ -2948,6 +2967,8 @@ int UtcDaliImageViewLoadRemoteSVG(void)
     application.Render();
 
     DALI_TEST_EQUALS(imageView.GetRendererCount(), 1u, TEST_LOCATION);
+
+    imageView.Unparent();
   }
 
   END_TEST;
@@ -3015,6 +3036,7 @@ int UtcDaliImageViewSyncSVGLoading02(void)
 
   tet_infoline("ImageView Testing SVG image sync loading");
 
+  for(int testCase = 0; testCase < 3; ++testCase)
   {
     ImageView imageView = ImageView::New();
 
@@ -3025,7 +3047,28 @@ int UtcDaliImageViewSyncSVGLoading02(void)
     syncLoadingMap.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, true);
     syncLoadingMap.Insert(DevelVisual::Property::VISUAL_FITTING_MODE, Toolkit::DevelVisual::FIT_KEEP_ASPECT_RATIO);
     imageView.SetProperty(ImageView::Property::IMAGE, syncLoadingMap);
-    imageView.ResourceReadySignal().Connect(&OnResourceReadySignalSVG);
+    switch(testCase)
+    {
+      case 0:
+      default:
+      {
+        tet_printf("Case 0 : Do not set size (size is 0, 0)\n");
+        break;
+      }
+      case 1:
+      {
+        tet_printf("Case 1 : Width is bigger than height (size is 200, 100)\n");
+        imageView.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 100.0f));
+        break;
+      }
+      case 2:
+      {
+        tet_printf("Case 2 : Height is bigger than width (size is 100, 200)\n");
+        imageView.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 200.0f));
+        break;
+      }
+    }
+    imageView.ResourceReadySignal().Connect(&OnResourceReadySyncSVGLoading02);
 
     application.GetScene().Add(imageView);
     DALI_TEST_CHECK(imageView);
@@ -3050,6 +3093,14 @@ int UtcDaliImageViewSyncSVGLoading02(void)
     Vector3 naturalSize = imageView.GetNaturalSize();
     DALI_TEST_EQUALS(naturalSize.width, 100.0f, TEST_LOCATION);
     DALI_TEST_EQUALS(naturalSize.height, 100.0f, TEST_LOCATION);
+
+    imageView.Unparent();
+
+    // Ensure remove cache.
+    application.SendNotification();
+    application.Render();
+    application.SendNotification();
+    application.Render();
   }
   END_TEST;
 }
@@ -3087,6 +3138,57 @@ int UtcDaliImageViewAsyncSVGLoading(void)
   END_TEST;
 }
 
+int UtcDaliImageViewAsyncSVGLoading02(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline("ImageView Testing SVG image async loading and the loaded result check cached");
+
+  {
+    ImageView imageView = ImageView::New();
+    DALI_TEST_CHECK(imageView);
+
+    // Async loading is used - default value of SYNCHRONOUS_LOADING is false.
+    Property::Map propertyMap;
+    propertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
+    propertyMap.Insert(Toolkit::ImageVisual::Property::URL, TEST_RESOURCE_DIR "/svg1.svg");
+    imageView.SetProperty(ImageView::Property::IMAGE, propertyMap);
+
+    application.GetScene().Add(imageView);
+
+    // Check that natural size return invalid values now
+    // Note : This logic might be changed if we decide to decode the svg synchronously.
+    Vector3 naturalSize = imageView.GetNaturalSize();
+    DALI_TEST_NOT_EQUALS(naturalSize.width, 100.0f, 0.01f, TEST_LOCATION);
+    DALI_TEST_NOT_EQUALS(naturalSize.height, 100.0f, 0.01f, TEST_LOCATION);
+
+    application.SendNotification();
+
+    // Wait for loading
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render(16);
+
+    naturalSize = imageView.GetNaturalSize();
+    DALI_TEST_EQUALS(naturalSize.width, 100.0f, TEST_LOCATION);
+    DALI_TEST_EQUALS(naturalSize.height, 100.0f, TEST_LOCATION);
+
+    // Test that imageView2 use cached natrual size.
+    ImageView imageView2 = ImageView::New();
+    DALI_TEST_CHECK(imageView2);
+
+    // Use same property map with imageView
+    imageView2.SetProperty(ImageView::Property::IMAGE, propertyMap);
+
+    // Check whether natural size is same as cached image size.
+    naturalSize = imageView2.GetNaturalSize();
+    DALI_TEST_EQUALS(naturalSize.width, 100.0f, TEST_LOCATION);
+    DALI_TEST_EQUALS(naturalSize.height, 100.0f, TEST_LOCATION);
+  }
+  END_TEST;
+}
+
 int UtcDaliImageViewSVGLoadingSyncSetInvalidValue(void)
 {
   ToolkitTestApplication application;
@@ -3412,9 +3514,12 @@ int UtcDaliImageViewSvgAtlasing(void)
   propertyMap["url"]      = TEST_SVG_FILE_NAME;
   propertyMap["atlasing"] = true;
 
+  gResourceReadySignalFired = false;
+
   ImageView imageView = ImageView::New();
   imageView.SetProperty(ImageView::Property::IMAGE, propertyMap);
   imageView.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f));
+  imageView.ResourceReadySignal().Connect(&ResourceReadySignal);
   application.GetScene().Add(imageView);
 
   application.SendNotification();
@@ -3422,6 +3527,8 @@ int UtcDaliImageViewSvgAtlasing(void)
   // Wait for loading & rasterization
   DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
 
+  DALI_TEST_CHECK(gResourceReadySignalFired);
+
   application.SendNotification();
   application.Render(16);
 
@@ -3431,15 +3538,70 @@ int UtcDaliImageViewSvgAtlasing(void)
   params1["height"] << 100;
   DALI_TEST_EQUALS(callStack.FindMethodAndParams("TexSubImage2D", params1), true, TEST_LOCATION);
 
-  imageView.SetProperty(Actor::Property::SIZE, Vector2(600.f, 600.f));
+  callStack.Reset();
+
+  gResourceReadySignalFired = false;
+
+  // Also use new image view with atlas.
+  ImageView imageView2 = ImageView::New();
+  imageView2.SetProperty(ImageView::Property::IMAGE, propertyMap);
+  imageView2.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f));
+  imageView2.ResourceReadySignal().Connect(&ResourceReadySignal);
+  application.GetScene().Add(imageView2);
 
   application.SendNotification();
 
-  // Wait for rasterization
+  // Let we check that we use cached image, and cached texture.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 0), false, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gResourceReadySignalFired);
+
+  application.SendNotification();
+  application.Render(16);
+
+  // Check there is no newly generated texture
+  DALI_TEST_EQUALS(callStack.CountMethod("GenTextures"), 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(callStack.FindMethodAndParams("TexSubImage2D", params1), false, TEST_LOCATION);
+
+  callStack.Reset();
+
+  gResourceReadySignalFired = false;
+
+  // Also use new image view 'without'' atlas.
+  propertyMap["atlasing"] = false;
+  ImageView imageView3    = ImageView::New();
+  imageView3.SetProperty(ImageView::Property::IMAGE, propertyMap);
+  imageView3.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f));
+  imageView3.ResourceReadySignal().Connect(&ResourceReadySignal);
+  application.GetScene().Add(imageView3);
+
+  application.SendNotification();
+
+  // Let we check that we use cached image, but not cached texture.
+  // Wait rasterize.
   DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
+  DALI_TEST_CHECK(gResourceReadySignalFired);
+
+  application.SendNotification();
+  application.Render(16);
+
+  // Check that we generate new texture
+  DALI_TEST_EQUALS(callStack.CountMethod("GenTextures"), 1, TEST_LOCATION);
+  DALI_TEST_EQUALS(callStack.FindMethodAndParams("TexImage2D", params1), true, TEST_LOCATION);
+
   callStack.Reset();
 
+  gResourceReadySignalFired = false;
+
+  // Try to atlas over the size.
+  imageView.SetProperty(Actor::Property::SIZE, Vector2(600.f, 600.f));
+
+  application.SendNotification();
+
+  // Wait for rasterization
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
   application.SendNotification();
   application.Render(16);
 
@@ -3449,6 +3611,24 @@ int UtcDaliImageViewSvgAtlasing(void)
   params2["height"] << 600;
   DALI_TEST_EQUALS(callStack.FindMethodAndParams("TexImage2D", params2), true, TEST_LOCATION);
 
+  callStack.Reset();
+
+  // Try to load over the size.
+  // Note that imageView3's atlas attempt is false.
+  imageView3.SetProperty(Actor::Property::SIZE, Vector2(600.f, 600.f));
+
+  // Let we check atlas cached.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 0), false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  // Check there is no newly generated texture
+  DALI_TEST_EQUALS(callStack.CountMethod("GenTextures"), 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(callStack.FindMethodAndParams("TexSubImage2D", params2), false, TEST_LOCATION);
+
+  gResourceReadySignalFired = false;
+
   END_TEST;
 }
 
@@ -3993,6 +4173,22 @@ void OnResourceReadySignal11(Control control)
   }
 }
 
+void OnResourceReadySignal12(Control control)
+{
+  gResourceReadySignalCounter++;
+
+  if(gImageView1)
+  {
+    gImageView1.Unparent();
+    gImageView1.Reset(); // Destroy visual
+  }
+  if(gImageView2)
+  {
+    gImageView2.Unparent();
+    gImageView2.Reset(); // Destroy visual
+  }
+}
+
 } // namespace
 
 int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
@@ -4999,6 +5195,87 @@ int UtcDaliImageViewSetImageOnResourceReadySignal11(void)
   END_TEST;
 }
 
+int UtcDaliImageViewSetImageOnResourceReadySignal12(void)
+{
+  tet_infoline("Test ResourceReady Add AnimatedImageVisual with invalid url, and then Remove immediately.");
+
+  ToolkitTestApplication application;
+
+  gResourceReadySignalCounter = 0;
+
+  // Clear image view for clear test
+
+  if(gImageView1)
+  {
+    gImageView1.Reset();
+  }
+  if(gImageView2)
+  {
+    gImageView2.Reset();
+  }
+  if(gImageView3)
+  {
+    gImageView3.Reset();
+  }
+
+  try
+  {
+    gImageView1 = ImageView::New();
+    gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, "invalid.gif");
+    gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal12);
+    application.GetScene().Add(gImageView1); // It will call resourceReady signal 1 time.
+
+    gImageView2 = ImageView::New();
+    gImageView2.SetProperty(Toolkit::ImageView::Property::IMAGE, "invalid.gif");
+    gImageView2.ResourceReadySignal().Connect(&OnResourceReadySignal12);
+    application.GetScene().Add(gImageView2); // It will call resourceReady signal 1 time.
+
+    tet_printf("ResourceReady called %d times\n", gResourceReadySignalCounter);
+
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 0, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    // Load gImageView1
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+    tet_printf("ResourceReady called %d times\n", gResourceReadySignalCounter);
+
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, TEST_LOCATION);
+
+    DALI_TEST_CHECK(true);
+  }
+  catch(...)
+  {
+    // Exception should not happened
+    DALI_TEST_CHECK(false);
+  }
+
+  // Clear cache.
+  application.SendNotification();
+  application.Render();
+
+  gResourceReadySignalCounter = 0;
+
+  // Clear image view for clear test
+
+  if(gImageView1)
+  {
+    gImageView1.Reset();
+  }
+  if(gImageView2)
+  {
+    gImageView2.Reset();
+  }
+  if(gImageView3)
+  {
+    gImageView3.Reset();
+  }
+
+  END_TEST;
+}
+
 int UtcDaliImageViewUseSameUrlWithAnimatedImageVisual(void)
 {
   tet_infoline("Test multiple views with same image in animated image visual");
@@ -5042,6 +5319,9 @@ int UtcDaliImageViewNpatchImageCacheTest01(void)
     // Ensure remove npatch cache if required.
     application.SendNotification();
     application.Render();
+    application.RunIdles();
+    application.SendNotification();
+    application.Render();
 
     imageView[index] = ImageView::New(nPatchImageUrl);
     imageView[index].SetProperty(Actor::Property::SIZE, Vector2(100.0f, 200.0f));
@@ -5080,6 +5360,9 @@ int UtcDaliImageViewNpatchImageCacheTest01(void)
 
   application.SendNotification();
   application.Render();
+  application.RunIdles();
+  application.SendNotification();
+  application.Render();
 
   textureCallStack.Reset();
   // Let we use deference textures
@@ -5106,6 +5389,9 @@ int UtcDaliImageViewNpatchImageCacheTest01(void)
 
   application.SendNotification();
   application.Render();
+  application.RunIdles();
+  application.SendNotification();
+  application.Render();
 
   // Check memory leak
   DALI_TEST_EQUALS(textureCallStack.CountMethod("GenTextures"), textureCallStack.CountMethod("DeleteTextures"), TEST_LOCATION);
@@ -5586,11 +5872,11 @@ int UtcDaliImageViewTransitionEffect05(void)
   ToolkitTestApplication application;
 
   Property::Map map;
-  map["target"]      = "image";
-  map["property"]    = "opacity";
+  map["target"]       = "image";
+  map["property"]     = "opacity";
   map["initialValue"] = 0.2f;
-  map["targetValue"] = 1.0f;
-  map["animator"]    = Property::Map()
+  map["targetValue"]  = 1.0f;
+  map["animator"]     = Property::Map()
                       .Add("alphaFunction", "EASE_IN_OUT")
                       .Add("timePeriod", Property::Map().Add("delay", 0.0f).Add("duration", 2.0f))
                       .Add("animationType", "BETWEEN");
@@ -5625,11 +5911,11 @@ int UtcDaliImageViewTransitionEffect06(void)
   ToolkitTestApplication application;
 
   Property::Map map;
-  map["target"]      = "image";
-  map["property"]    = "opacity";
+  map["target"]       = "image";
+  map["property"]     = "opacity";
   map["initialValue"] = 0.2f;
-  map["targetValue"] = 1.0f;
-  map["animator"]    = Property::Map()
+  map["targetValue"]  = 1.0f;
+  map["animator"]     = Property::Map()
                       .Add("alphaFunction", "EASE_IN_OUT")
                       .Add("timePeriod", Property::Map().Add("delay", 0.0f).Add("duration", 2.0f))
                       .Add("animationType", "TO");
@@ -5658,8 +5944,6 @@ int UtcDaliImageViewTransitionEffect06(void)
   END_TEST;
 }
 
-
-
 int UtcDaliImageViewImageLoadFailureAndReload01(void)
 {
   tet_infoline("Try to load invalid image first, and then reload after that image valid.");
@@ -5823,4 +6107,222 @@ int UtcDaliImageViewImageLoadSuccessAndReload01(void)
   gResourceReadySignalFired = false;
 
   END_TEST;
+}
+
+namespace
+{
+int  gSvgReRasterizeDuringResourceReadyOrderType = 0;
+void OnResourceReadyReRasterize01(Control control)
+{
+  if(gResourceReadySignalCounter == 0u)
+  {
+    auto parent = gImageView1.GetParent();
+    DALI_TEST_CHECK(parent);
+
+    tet_printf("Request gImageView2 and gImageView3 as 300x300. gImageView4 as 400x400\n");
+
+    gImageView2 = ImageView::New(TEST_SVG_FILE_NAME);
+    gImageView2.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+    gImageView2.SetProperty(Actor::Property::SIZE, Vector2(300.f, 300.f));
+    parent.Add(gImageView2);
+    gImageView3 = ImageView::New(TEST_SVG_FILE_NAME);
+    gImageView3.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+    gImageView3.SetProperty(Actor::Property::SIZE, Vector2(300.f, 300.f));
+    parent.Add(gImageView3);
+    gImageView4 = ImageView::New(TEST_SVG_FILE_NAME);
+    gImageView4.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+    gImageView4.SetProperty(Actor::Property::SIZE, Vector2(400.f, 400.f));
+
+    Property::Map map;
+    map.Add(Visual::Property::TYPE, Visual::IMAGE);
+    map.Add(ImageVisual::Property::URL, TEST_SVG_FILE_NAME);
+    map.Add(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+
+    gImageView1.Unparent();
+
+    gImageView1.SetProperty(Actor::Property::SIZE, Vector2(400.f, 400.f));
+    gImageView1.SetProperty(ImageView::Property::IMAGE, map);
+
+    if((gSvgReRasterizeDuringResourceReadyOrderType & 2) == 0)
+    {
+      tet_printf("Make 400x400 image raterized as synchronously\n");
+      // Add gImageView1 now.
+      parent.Add(gImageView1);
+    }
+    else
+    {
+      tet_printf("Make 400x400 image raterized as synchronously, but after some async rasterization request first.\n");
+    }
+
+    if((gSvgReRasterizeDuringResourceReadyOrderType & 1) == 0)
+    {
+      // Add gImageView4 now
+      parent.Add(gImageView4);
+    }
+  }
+  else if(gResourceReadySignalCounter > 0 && ((gSvgReRasterizeDuringResourceReadyOrderType & 1) == 1))
+  {
+    auto parent = gImageView2.GetParent();
+    DALI_TEST_CHECK(parent);
+
+    // Add gImageView4 after second phase of gImageView1 load done.
+    parent.Add(gImageView4);
+  }
+  ++gResourceReadySignalCounter;
+}
+
+} // namespace
+
+int UtcDaliImageViewSvgReRasterizeDuringResourceReady01(void)
+{
+  for(int useInvalidSvg = 0; useInvalidSvg < 2; ++useInvalidSvg)
+  {
+    for(gSvgReRasterizeDuringResourceReadyOrderType = 0; gSvgReRasterizeDuringResourceReadyOrderType < 4; ++gSvgReRasterizeDuringResourceReadyOrderType)
+    {
+      ToolkitTestApplication application;
+
+      tet_infoline("Test SVG image rasterize and re-rasterize at ResourceReady callback.\n");
+      if(useInvalidSvg == 1)
+      {
+        tet_infoline("But in this case, we will use invalid svg file. So broken image used.\n");
+      }
+      tet_printf("order type : %d\n", gSvgReRasterizeDuringResourceReadyOrderType);
+
+      TestGlAbstraction& gl           = application.GetGlAbstraction();
+      TraceCallStack&    textureTrace = gl.GetTextureTrace();
+      textureTrace.Enable(true);
+
+      // Clear image view for clear test
+      if(gImageView1)
+      {
+        gImageView1.Reset();
+      }
+      if(gImageView2)
+      {
+        gImageView2.Reset();
+      }
+      if(gImageView3)
+      {
+        gImageView3.Reset();
+      }
+      if(gImageView4)
+      {
+        gImageView4.Reset();
+      }
+      gResourceReadySignalCounter = 0u;
+
+      gImageView1 = ImageView::New(useInvalidSvg == 1 ? TEST_RESOURCE_DIR "/invalid.svg" : TEST_SVG_FILE_NAME);
+      gImageView1.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+      gImageView1.ResourceReadySignal().Connect(&OnResourceReadyReRasterize01);
+      application.GetScene().Add(gImageView1);
+
+      DALI_TEST_EQUALS(gResourceReadySignalCounter, 0u, TEST_LOCATION);
+
+      application.SendNotification();
+      application.Render(16);
+
+      if(useInvalidSvg == 1)
+      {
+        // load invalid svg file. It will emit ResourceReady for gImageView1
+        DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+        DALI_TEST_EQUALS(gResourceReadySignalCounter, 1u, TEST_LOCATION);
+      }
+      else
+      {
+        // load svg file. It will not emit ResourceReady yet
+        DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+        DALI_TEST_EQUALS(gResourceReadySignalCounter, 0u, TEST_LOCATION);
+
+        application.SendNotification();
+        application.Render(16);
+        DALI_TEST_EQUALS(gResourceReadySignalCounter, 0u, TEST_LOCATION);
+
+        // rasterize svg 200x200. Now, ResourceReady signal will be emitted for gImageView1
+        DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+        DALI_TEST_EQUALS(gResourceReadySignalCounter, 1u, TEST_LOCATION);
+      }
+
+      application.SendNotification();
+      application.Render(16);
+      if((gSvgReRasterizeDuringResourceReadyOrderType & 2) == 2)
+      {
+        // Add gImageView1 now, to avoid sync load & rasterize request always before async.
+        application.GetScene().Add(gImageView1);
+
+        application.SendNotification();
+        application.Render(16);
+      }
+      // ResourceReady signal will be emitted for gImageView1 (sync load case), or gImageView4 (if gImageView1 rasterize first.)
+      // Note : We cannot assume that gImageView1 rasterize requested before gImageView4.
+      //        So, we cannot ensure that gResourceReadySignalCounter is which 2 or 3.
+      DALI_TEST_GREATER(gResourceReadySignalCounter, 1, TEST_LOCATION);
+      DALI_TEST_GREATER(4, gResourceReadySignalCounter, TEST_LOCATION);
+
+      application.SendNotification();
+      application.Render(16);
+
+      {
+        TraceCallStack::NamedParams params;
+        params["width"] << 200;
+        params["height"] << 200;
+        DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), useInvalidSvg == 0, TEST_LOCATION);
+        params.mParams.clear();
+        params["width"] << 300;
+        params["height"] << 300;
+        DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), false, TEST_LOCATION);
+        params.mParams.clear();
+        params["width"] << 400;
+        params["height"] << 400;
+        DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), true, TEST_LOCATION);
+      }
+      textureTrace.Reset();
+
+      // Total event trigger count is 2 or 1 : rasterize svg 300x300 + (rasterize svg 400x400 if required) + (load valid svg file if required)
+      // Now, ResourceReady signal will be emitted for gImageView2 and gImageView3 (and gImageView4 if required).
+      // Note : We cannot assume that which signal will be come first / rasterization 300x300 vs 400x400.
+      uint32_t eventTriggerRequiredCount = gResourceReadySignalCounter == 2 ? (gSvgReRasterizeDuringResourceReadyOrderType == 3 ? 3 : 2) : 1; // Check whether we need to wait 400x400 + 300x300, or only 300x300.
+
+      DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(eventTriggerRequiredCount), true, TEST_LOCATION);
+      DALI_TEST_EQUALS(gResourceReadySignalCounter, 5u, TEST_LOCATION);
+
+      application.SendNotification();
+      application.Render(16);
+
+      {
+        TraceCallStack::NamedParams params;
+        params["width"] << 200;
+        params["height"] << 200;
+        DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), false, TEST_LOCATION);
+        params.mParams.clear();
+        params["width"] << 300;
+        params["height"] << 300;
+        DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), true, TEST_LOCATION);
+        params.mParams.clear();
+        params["width"] << 400;
+        params["height"] << 400;
+        DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), false, TEST_LOCATION);
+      }
+
+      // Clear image view for clear test
+      if(gImageView1)
+      {
+        gImageView1.Reset();
+      }
+      if(gImageView2)
+      {
+        gImageView2.Reset();
+      }
+      if(gImageView3)
+      {
+        gImageView3.Reset();
+      }
+      if(gImageView4)
+      {
+        gImageView4.Reset();
+      }
+      gResourceReadySignalCounter = 0u;
+    }
+  }
+
+  END_TEST;
 }
\ No newline at end of file
index 319d4d4..3053c7f 100644 (file)
@@ -232,10 +232,13 @@ int UtcDaliImageVisualPropertyMap(void)
   application.Render();
 
   DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
-  auto renderer           = actor.GetRendererAt(0);
-  auto preMultipliedIndex = renderer.GetPropertyIndex("preMultipliedAlpha");
+  auto renderer = actor.GetRendererAt(0);
+
+  // Note : renderer don't have "premultipliedAlpha" at pre multiplied alpha enabled. (Since shader hold it.)
+  auto shader             = renderer.GetShader();
+  auto preMultipliedIndex = shader.GetPropertyIndex("premultipliedAlpha");
   DALI_TEST_CHECK(preMultipliedIndex != Property::INVALID_INDEX);
-  auto preMultipliedAlpha  = renderer.GetProperty<float>(preMultipliedIndex);
+  auto preMultipliedAlpha  = shader.GetProperty<float>(preMultipliedIndex);
   auto preMultipliedAlpha2 = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
   DALI_TEST_EQUALS(preMultipliedAlpha, 1.0f, TEST_LOCATION);
   DALI_TEST_EQUALS(preMultipliedAlpha2, true, TEST_LOCATION);
@@ -287,13 +290,15 @@ int UtcDaliImageVisualNoPremultipliedAlpha01(void)
   application.Render();
 
   DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
-  auto renderer           = actor.GetRendererAt(0);
-  auto preMultipliedIndex = renderer.GetPropertyIndex("preMultipliedAlpha");
+  auto renderer = actor.GetRendererAt(0);
+
+  // Note : renderer now have "premultipliedAlpha" since pre multiplied alpha disabled.
+  auto preMultipliedIndex = renderer.GetPropertyIndex("premultipliedAlpha");
   DALI_TEST_CHECK(preMultipliedIndex != Property::INVALID_INDEX);
-  auto preMultipliedAlpha  = renderer.GetProperty<bool>(preMultipliedIndex);
+  auto preMultipliedAlpha  = renderer.GetProperty<float>(preMultipliedIndex);
   auto preMultipliedAlpha2 = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
 
-  DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(preMultipliedAlpha, 0.0f, TEST_LOCATION);
   DALI_TEST_EQUALS(preMultipliedAlpha2, false, TEST_LOCATION);
 
   DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
@@ -343,13 +348,15 @@ int UtcDaliImageVisualNoPremultipliedAlpha02(void)
   application.Render();
 
   DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
-  auto renderer           = actor.GetRendererAt(0);
-  auto preMultipliedIndex = renderer.GetPropertyIndex("preMultipliedAlpha");
+  auto renderer = actor.GetRendererAt(0);
+
+  // Note : renderer now have "premultipliedAlpha" since pre multiplied alpha disabled.
+  auto preMultipliedIndex = renderer.GetPropertyIndex("premultipliedAlpha");
   DALI_TEST_CHECK(preMultipliedIndex != Property::INVALID_INDEX);
-  auto preMultipliedAlpha  = renderer.GetProperty<bool>(preMultipliedIndex);
+  auto preMultipliedAlpha  = renderer.GetProperty<float>(preMultipliedIndex);
   auto preMultipliedAlpha2 = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
 
-  DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(preMultipliedAlpha, 0.0f, TEST_LOCATION);
   DALI_TEST_EQUALS(preMultipliedAlpha2, false, TEST_LOCATION);
 
   DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
@@ -386,12 +393,12 @@ int UtcDaliImageVisualNoPremultipliedAlpha02(void)
 
   DALI_TEST_EQUALS(newActor.GetRendererCount(), 1u, TEST_LOCATION);
   auto newRenderer   = newActor.GetRendererAt(0);
-  preMultipliedIndex = newRenderer.GetPropertyIndex("preMultipliedAlpha");
+  preMultipliedIndex = newRenderer.GetPropertyIndex("premultipliedAlpha");
   DALI_TEST_CHECK(preMultipliedIndex != Property::INVALID_INDEX);
-  preMultipliedAlpha  = newRenderer.GetProperty<bool>(preMultipliedIndex);
+  preMultipliedAlpha  = newRenderer.GetProperty<float>(preMultipliedIndex);
   preMultipliedAlpha2 = newRenderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
 
-  DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(preMultipliedAlpha, 0.0f, TEST_LOCATION);
   DALI_TEST_EQUALS(preMultipliedAlpha2, false, TEST_LOCATION);
 
   srcFactorRgb    = newRenderer.GetProperty<int>(Renderer::Property::BLEND_FACTOR_SRC_RGB);
@@ -1384,7 +1391,6 @@ int UtcDaliImageVisualCustomWrapModePixelArea(void)
   END_TEST;
 }
 
-
 int UtcDaliImageVisualCustomWrapModePixelArea02(void)
 {
   ToolkitTestApplication application;
@@ -1561,14 +1567,6 @@ int UtcDaliImageVisualAnimateMixColor(void)
   ToolkitTestApplication application;
   tet_infoline("Animate mix color");
 
-  static std::vector<UniformData> customUniforms =
-    {
-      UniformData("mixColor", Property::Type::VECTOR3),
-    };
-
-  TestGraphicsController& graphics = application.GetGraphicsController();
-  graphics.AddCustomUniforms(customUniforms);
-
   application.GetPlatform().SetClosestImageSize(Vector2(100, 100));
 
   VisualFactory factory = VisualFactory::Get();
@@ -1625,11 +1623,10 @@ int UtcDaliImageVisualAnimateMixColor(void)
   application.SendNotification();
   application.Render(0);     // Ensure animation starts
   application.Render(2000u); // Halfway point
-  Vector3 testColor(1.0f, 0.0f, 0.5f);
+  Vector4 testColor(1.0f, 0.0f, 0.5f, 0.75f);
 
-  // uColor.a should be actor's alpha * mixColor.a.
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(0.5f, 0.5f, 0.5f, 0.75f)), true, TEST_LOCATION);
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", testColor), true, TEST_LOCATION);
+  // uColor should be actor's color * mixColor.
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(0.5f, 0.5f, 0.5f, 1.0f) * testColor), true, TEST_LOCATION);
 
   DALI_TEST_CHECK(glEnableStack.FindMethodAndParams("Enable", blendStr.str()));
 
@@ -1643,8 +1640,7 @@ int UtcDaliImageVisualAnimateMixColor(void)
   application.SendNotification();
 
   DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), Color::WHITE, TEST_LOCATION);
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, 0.5f)), true, TEST_LOCATION);
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(TARGET_MIX_COLOR)), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Color::WHITE * TARGET_MIX_COLOR), true, TEST_LOCATION);
 
   // (Don't test for caching of capabilities, toolkit uses Test graphics backend, not actual backend)
 
@@ -3404,15 +3400,31 @@ int UtcDaliImageVisualOrientationCorrection(void)
   Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
   application.GetScene().Add(actor);
 
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap(resultMap);
+
+  // check the Property::ORIENTATION_CORRECTION value from the returned map
+  Property::Value* typeValue = resultMap.Find(ImageVisual::Property::ORIENTATION_CORRECTION, Property::BOOLEAN);
+  DALI_TEST_CHECK(typeValue);
+  DALI_TEST_EQUALS(typeValue->Get<bool>(), false, TEST_LOCATION);
+
+  Vector2 originalImageSize;
+  tet_infoline("Get size of original visual before load image");
+  imageVisual.GetNaturalSize(originalImageSize);
   dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  DALI_TEST_GREATER(originalImageSize.width, originalImageSize.height, TEST_LOCATION); // Width and Height must be different for this test.
+
   // Wait for image to load
   DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
-  Vector2 originalImageSize;
+  Vector2 nonRotatedImageSize;
   tet_infoline("Get size of original visual to compare later with rotated image");
-  imageVisual.GetNaturalSize(originalImageSize);
-  DALI_TEST_GREATER(originalImageSize.width, originalImageSize.height, TEST_LOCATION); // Width and Height must be different for this test.
-  imageVisual.Reset();                                                                 // remove handle so can unregister it and remove from cache
+  imageVisual.GetNaturalSize(nonRotatedImageSize);
+  DALI_TEST_GREATER(nonRotatedImageSize.width, nonRotatedImageSize.height, TEST_LOCATION); // Width and Height must be different for this test.
+
+  // Note : The size of original image is bigger than MAX_TEXTURE_SIZE. origianImageSize might not be same as nonRotatedImageSize.
+  // TODO : Shouldn't we need to fix it?
+
   dummyImpl.UnregisterVisual(DummyControl::Property::TEST_VISUAL);
   application.SendNotification();
   application.Render();
@@ -3424,26 +3436,83 @@ int UtcDaliImageVisualOrientationCorrection(void)
   propertyMap.Insert(ImageVisual::Property::ORIENTATION_CORRECTION, true);
   imageVisual = factory.CreateVisual(propertyMap);
 
+  Vector2 rotatedImageSize;
+  imageVisual.GetNaturalSize(rotatedImageSize);
+  tet_infoline("Confirm that visual has rotated, even if ");
+  DALI_TEST_EQUALS(originalImageSize.width, rotatedImageSize.height, TEST_LOCATION);
+  DALI_TEST_EQUALS(originalImageSize.height, rotatedImageSize.width, TEST_LOCATION);
+
   dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
   // Wait for image to load
   DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
-  Vector2 rotatedImageSize;
   imageVisual.GetNaturalSize(rotatedImageSize);
   tet_infoline("Confirm that visual has rotated");
-  DALI_TEST_EQUALS(originalImageSize.width, rotatedImageSize.height, TEST_LOCATION);
-  DALI_TEST_EQUALS(originalImageSize.height, rotatedImageSize.width, TEST_LOCATION);
+  DALI_TEST_EQUALS(nonRotatedImageSize.width, rotatedImageSize.height, TEST_LOCATION);
+  DALI_TEST_EQUALS(nonRotatedImageSize.height, rotatedImageSize.width, TEST_LOCATION);
 
-  Property::Map resultMap;
   imageVisual.CreatePropertyMap(resultMap);
 
   // check the Property::ORIENTATION_CORRECTION value from the returned map
-  Property::Value* typeValue = resultMap.Find(ImageVisual::Property::ORIENTATION_CORRECTION, Property::BOOLEAN);
+  typeValue = resultMap.Find(ImageVisual::Property::ORIENTATION_CORRECTION, Property::BOOLEAN);
+  DALI_TEST_CHECK(typeValue);
   DALI_TEST_EQUALS(typeValue->Get<bool>(), true, TEST_LOCATION);
 
   END_TEST;
 }
 
+int UtcDaliImageVisualOrientationCorrectionCache(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageVisualOrientationCorrectionCache Check orientation correction value give effort to cache hit");
+
+  VisualFactory factory = VisualFactory::Get();
+  tet_infoline("Create visual with Orientation correction set OFF");
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_ROTATED_IMAGE);
+  propertyMap.Insert("orientationCorrection", false);
+  Visual::Base imageVisual1 = factory.CreateVisual(propertyMap);
+
+  tet_infoline("Create control for visual, need to loaded it");
+  DummyControl        actor1     = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl1 = static_cast<Impl::DummyControl&>(actor1.GetImplementation());
+  dummyImpl1.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual1);
+  application.GetScene().Add(actor1);
+
+  // Wait for image to load
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  tet_infoline("Create visual with Orientation correction set ON ");
+  propertyMap.Clear();
+  propertyMap.Insert(Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_ROTATED_IMAGE);
+  propertyMap.Insert(ImageVisual::Property::ORIENTATION_CORRECTION, true);
+  Visual::Base imageVisual2 = factory.CreateVisual(propertyMap);
+
+  tet_infoline("Create control for visual2, need to loaded it");
+  DummyControl        actor2     = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl2 = static_cast<Impl::DummyControl&>(actor2.GetImplementation());
+  dummyImpl2.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual2);
+  application.GetScene().Add(actor2);
+
+  // Wait for image to load. Check whether each correction and non-correction image have difference size.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  Vector2 visual1NaturalSize;
+  imageVisual1.GetNaturalSize(visual1NaturalSize);
+  Vector2 visual2NaturalSize;
+  imageVisual2.GetNaturalSize(visual2NaturalSize);
+
+  DALI_TEST_NOT_EQUALS(visual1NaturalSize.width, visual1NaturalSize.height, 0.01f, TEST_LOCATION); // Width and Height must be different for this test.
+
+  tet_infoline("Confirm that visual has rotated");
+  DALI_TEST_EQUALS(visual1NaturalSize.width, visual2NaturalSize.height, TEST_LOCATION);
+  DALI_TEST_EQUALS(visual1NaturalSize.height, visual2NaturalSize.width, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliImageVisualCustomShader(void)
 {
   ToolkitTestApplication application;
@@ -4227,4 +4296,213 @@ int UtcDaliImageVisualDebugImageVisualShaderN2(void)
   DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 1, TEST_LOCATION);
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliImageVisualSynchronousSizing01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageVisualSynchronousSizing01");
+
+  Vector2  size   = Vector2(64.0f, 64.0f);
+  uint32_t width  = 64;
+  uint32_t height = 64;
+
+  Property::Map imagePropertyMap;
+  imagePropertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
+  imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME);
+  imagePropertyMap.Insert(Toolkit::DevelImageVisual::Property::SYNCHRONOUS_SIZING, true);
+
+  Toolkit::ImageView imageView = Toolkit::ImageView::New();
+  imageView.SetProperty(Toolkit::ImageView::Property::IMAGE, imagePropertyMap);
+  imageView.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  imageView.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  imageView.SetProperty(Actor::Property::SIZE, size);
+
+  DALI_TEST_EQUALS(imageView.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(imageView);
+
+  // load image as size 0x0 (Since we cannot ensure the size of actor yet)
+  // (Texture size is its original size, not the actor size.)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check (current image texture size != actor size)
+  Renderer   renderer = imageView.GetRendererAt(0);
+  TextureSet textures = renderer.GetTextures();
+  DALI_TEST_EQUALS(textures.GetTextureCount(), 1u, TEST_LOCATION);
+
+  Texture texture = textures.GetTexture(0);
+  DALI_TEST_EQUALS(texture.GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(texture.GetHeight(), 128u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(imageView.GetRendererCount(), 1u, TEST_LOCATION);
+
+  // load image as size 200x200 (Now we can ensure the size of actor is 200x200)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Set size again
+  imageView.SetProperty(Actor::Property::SIZE, size);
+
+  // Check (reloaded image texture size == actor size)
+  renderer = imageView.GetRendererAt(0);
+  textures = renderer.GetTextures();
+  DALI_TEST_EQUALS(textures.GetTextureCount(), 1u, TEST_LOCATION);
+
+  texture = textures.GetTexture(0);
+  DALI_TEST_EQUALS(texture.GetWidth(), width, TEST_LOCATION);
+  DALI_TEST_EQUALS(texture.GetHeight(), height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualSynchronousSizing02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageVisualSynchronousSizing02");
+
+  Property::Map imagePropertyMap;
+  imagePropertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
+  imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME);
+  imagePropertyMap.Insert(Toolkit::DevelImageVisual::Property::SYNCHRONOUS_SIZING, true);
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Visual::Base visual = factory.CreateVisual(imagePropertyMap);
+
+  Vector2 size;
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f)); // set size(1), no renderer yet
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), false, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  // load image as size 0x0 (Since we cannot ensure the size of actor yet)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification(); // require to load size(1)
+
+  // load image as size 200x200 (Now we can ensure the size of actor is 200x200)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  actor.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f)); // set size(2), no renderer yet
+  visual.GetNaturalSize(size);                                     // get size(1)
+  DALI_TEST_EQUALS(size, Vector2(200.0f, 200.0f), 0.001f, TEST_LOCATION);
+
+  application.SendNotification(); // require to load size(2)
+  application.Render();
+
+  // reload image
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  visual.GetNaturalSize(size); // get size(2)
+  DALI_TEST_EQUALS(size, Vector2(100.0f, 100.0f), 0.001f, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualSynchronousSizing03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageVisualSynchronousSizing03");
+
+  Property::Map imagePropertyMap;
+  imagePropertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
+  imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME);
+  imagePropertyMap.Insert("synchronousSizing", true);
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Visual::Base visual1 = factory.CreateVisual(imagePropertyMap); // Create duplicated visuals, to check whether cache system works well.
+  Visual::Base visual2 = factory.CreateVisual(imagePropertyMap);
+  Visual::Base visual3 = factory.CreateVisual(imagePropertyMap);
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual1);
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 2, visual2);
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 3, visual3);
+
+  actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f)); // set size(1), no renderer yet
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), false, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  // load image as size 0x0 (Since we cannot ensure the size of actor yet)
+  // NOTE : This behavior might be changed in future.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 3u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+  application.SendNotification(); // require to load size(1)
+
+  // load image as size 200x200 (Now we can ensure the size of actor is 200x200)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 3u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 2, TEST_LOCATION);
+
+  textureTrace.Reset();
+
+  // Unparent and Add again. Check whether the texture is cached.
+  actor.Unparent();
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 3u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+  textureTrace.Reset();
+
+  actor.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f)); // set size(2)
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 3u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+  application.SendNotification(); // require to load size(2)
+
+  // load image as size 100x100 (Now we can ensure the size of actor is 100x100)
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 1, TEST_LOCATION);
+
+  END_TEST;
+}
index 4d0d54e..216b419 100644 (file)
@@ -473,11 +473,13 @@ int UtcDaliKeyboardFocusManagerMoveFocus(void)
   tableView.AddChild(third, TableView::CellPosition(1, 0));
   tableView.AddChild(fourth, TableView::CellPosition(1, 1));
 
+  focusChangedCallback.Reset();
+
   // Set the focus to the first actor
   DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true);
   DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
   DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
-  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == second);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
   DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first);
   focusChangedCallback.Reset();
 
@@ -1435,11 +1437,13 @@ int UtcDaliKeyboardFocusManagerMoveFocusTestStateChange(void)
   tableView.AddChild(third, TableView::CellPosition(1, 0));
   tableView.AddChild(fourth, TableView::CellPosition(1, 1));
 
+  focusChangedCallback.Reset();
+
   // Set the focus to the first actor
   DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true);
   DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
   DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
-  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == second);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
   DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first);
 
   DALI_TEST_EQUALS(first.GetProperty<int>(DevelControl::Property::STATE), (int)DevelControl::FOCUSED, TEST_LOCATION);
@@ -2455,4 +2459,57 @@ int UtcDaliKeyboardFocusManagerKeyEventOtherWindow(void)
   DALI_TEST_CHECK(manager.GetCurrentFocusActor() == button1);
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliKeyboardFocusManagerRemoveScene(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" UtcDaliKeyboardFocusManagerRemoveScene");
+
+  // Register Type
+  TypeInfo type;
+  type = TypeRegistry::Get().GetTypeInfo("KeyboardFocusManager");
+  DALI_TEST_CHECK(type);
+  BaseHandle handle = type.CreateInstance();
+  DALI_TEST_CHECK(handle);
+
+  KeyboardFocusManager manager = KeyboardFocusManager::Get();
+  DALI_TEST_CHECK(manager);
+
+  bool                 focusChangedSignalVerified = false;
+  FocusChangedCallback focusChangedCallback(focusChangedSignalVerified);
+  manager.FocusChangedSignal().Connect(&focusChangedCallback, &FocusChangedCallback::Callback);
+
+  // Create the first actor and add it to the stage
+  Actor first = Actor::New();
+  first.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
+  application.GetScene().Add(first);
+
+  // Check that the focus is set on the first actor
+  DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first);
+  focusChangedCallback.Reset();
+
+  // Remove actor from scene
+  application.GetScene().Remove(first);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor());
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == first);
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == Actor());
+  focusChangedCallback.Reset();
+
+  // Add actor to scene
+  application.GetScene().Add(first);
+  DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
+  DALI_TEST_CHECK(focusChangedCallback.mSignalVerified);
+  DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor());
+  DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first);
+  focusChangedCallback.Reset();
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp b/automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp
new file mode 100644 (file)
index 0000000..8e95732
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+int UtcDaliRenderEffectNewP(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectNewP");
+
+  BackgroundBlurEffect blurEffect = BackgroundBlurEffect::New();
+  DALI_TEST_CHECK(blurEffect);
+
+  BackgroundBlurEffect blurEffect2 = BackgroundBlurEffect::New(0.5f, 10);
+  DALI_TEST_CHECK(blurEffect2);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectNewN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectNewN");
+
+  tet_printf("Check some invalid parameters clamp internally\n");
+
+  BackgroundBlurEffect blurEffect  = BackgroundBlurEffect::New(-0.5f, 10);
+  BackgroundBlurEffect blurEffect2 = BackgroundBlurEffect::New(10.0f, 10);
+  BackgroundBlurEffect blurEffect3 = BackgroundBlurEffect::New(0.5f, 0);
+  BackgroundBlurEffect blurEffect4 = BackgroundBlurEffect::New(0.5f, 2147483647);
+  DALI_TEST_CHECK(blurEffect);
+  DALI_TEST_CHECK(blurEffect2);
+  DALI_TEST_CHECK(blurEffect3);
+  DALI_TEST_CHECK(blurEffect4);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectActivateP01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectActivateP01");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  Control childControl = Control::New();
+  childControl.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  scene.Add(control);
+  control.Add(childControl);
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  childControl.SetRenderEffect(BackgroundBlurEffect::New());
+
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectActivateP02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectActivateP02");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  BackgroundBlurEffect blurEffect = BackgroundBlurEffect::New();
+  control.SetRenderEffect(blurEffect);
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  Control control2 = Control::New();
+  control2.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control2.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control2);
+
+  control2.SetRenderEffect(blurEffect);
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectDeactivateP(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectDeactivateP");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  uint32_t count = control.GetRendererCount();
+  control.SetRenderEffect(BackgroundBlurEffect::New());
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectDeactivateN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectDeactivateN");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  control.ClearRenderEffect(); // Nothing happens
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectActivateDeactivateInplace(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectActivateDeactivateInplace");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  BackgroundBlurEffect blurEffect = BackgroundBlurEffect::New();
+  control.SetRenderEffect(blurEffect);
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  control.SetRenderEffect(blurEffect);
+  control.ClearRenderEffect();
+  control.SetRenderEffect(blurEffect);
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectReassign(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectReassign");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  BackgroundBlurEffect blurEffect = BackgroundBlurEffect::New();
+  control.SetRenderEffect(blurEffect); // Duplicate actions will be ignored
+  control.SetRenderEffect(blurEffect); // Duplicate actions will be ignored
+  control.SetRenderEffect(blurEffect); // Duplicate actions will be ignored
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectResize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectResize");
+
+  Integration::Scene scene   = application.GetScene();
+  Control            control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  scene.Add(control);
+  control.SetRenderEffect(BackgroundBlurEffect::New());
+
+  application.SendNotification();
+  application.Render();
+
+  control.SetProperty(Actor::Property::SIZE, Vector2(30.0f, 30.0f));
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_WIDTH), 30.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_HEIGHT), 30.0f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectSynchronizeBackgroundCornerRadius(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectSynchronizeBackgroundCornerRadius");
+
+  Integration::Scene scene = application.GetScene();
+
+  Property::Map blackDimmerMap;
+  blackDimmerMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::COLOR);
+  blackDimmerMap.Insert(Toolkit::Visual::Property::MIX_COLOR, Color::BLACK);
+  blackDimmerMap.Insert(Toolkit::Visual::Property::OPACITY, 0.2f);
+  blackDimmerMap.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, 30.0f);
+
+  RenderEffect effect = BackgroundBlurEffect::New(0.4f, 40);
+
+  Control control = Control::New();
+  DALI_TEST_CHECK(control.GetRendererCount() == 0u);
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  control.SetProperty(Toolkit::Control::Property::BACKGROUND, blackDimmerMap);
+  DALI_TEST_CHECK(control.GetRendererCount() == 1u);
+  control.SetRenderEffect(effect);
+  DALI_TEST_CHECK(control.GetRendererCount() == 2u);
+
+  Renderer renderer = control.GetRendererAt(1u);
+  Vector4  radius   = Vector4::ZERO;
+  renderer.GetProperty(renderer.GetPropertyIndex(std::string("uCornerRadius"))).Get(radius);
+
+  Toolkit::Visual::Transform::Policy::Type policy;
+  renderer.GetProperty(renderer.GetPropertyIndex(std::string("uCornerRadiusPolicy"))).Get(policy);
+  DALI_TEST_CHECK(policy == 1);
+
+  DALI_TEST_CHECK(radius.x == 30.0f);
+  DALI_TEST_CHECK(radius.y == 30.0f);
+  DALI_TEST_CHECK(radius.z == 30.0f);
+  DALI_TEST_CHECK(radius.w == 30.0f);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectInvalidTargetSize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectInvalidTargetSize");
+
+  Integration::Scene scene          = application.GetScene();
+  const uint32_t     maxTextureSize = Dali::GetMaxTextureSize();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE_WIDTH, maxTextureSize + 1000.0f);
+  control.SetProperty(Actor::Property::SIZE_HEIGHT, maxTextureSize + 1000.0f);
+  scene.Add(control);
+  control.SetRenderEffect(BackgroundBlurEffect::New(0.4f, 40));
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(true); // no error
+
+  control.SetProperty(Actor::Property::SIZE_WIDTH, -10.0f);
+  control.SetProperty(Actor::Property::SIZE_HEIGHT, -10.0f);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(true); // no error
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectControlSceneOnAndSceneOff01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectControlSceneOnAndSceneOff01");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  uint32_t count = control.GetRendererCount();
+
+  // Add render effect during scene off.
+  control.SetRenderEffect(BackgroundBlurEffect::New());
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+
+  // Still render effect is not activated.
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  scene.Add(control);
+  // Render effect activated.
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.Unparent();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  scene.Add(control);
+  // Render effect activated.
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.Unparent();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  scene.Add(control);
+  // Render effect not activated.
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectControlSceneOnAndSceneOff02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectControlSceneOnAndSceneOff02");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  uint32_t count = control.GetRendererCount();
+  scene.Add(control);
+
+  // Add render effect during scene on.
+  control.SetRenderEffect(BackgroundBlurEffect::New());
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+
+  // Render effect activated.
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.Unparent();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  scene.Add(control);
+  // Render effect activated.
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.Unparent();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  scene.Add(control);
+  // Render effect not activated.
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectControlVisiblityChanged01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectControlVisiblityChanged01");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  uint32_t count = control.GetRendererCount();
+  scene.Add(control);
+
+  // Add render effect during invisible.
+  control.SetProperty(Actor::Property::VISIBLE, false);
+  control.SetRenderEffect(BackgroundBlurEffect::New());
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+
+  // Still render effect is not activated.
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect activated.
+  control.SetProperty(Actor::Property::VISIBLE, true);
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.SetProperty(Actor::Property::VISIBLE, false);
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.Unparent();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect still deactivated.
+  control.SetProperty(Actor::Property::VISIBLE, true);
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect activated.
+  scene.Add(control);
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.SetProperty(Actor::Property::VISIBLE, false);
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.SetProperty(Actor::Property::VISIBLE, true);
+  // Render effect not activated.
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliRenderEffectControlVisiblityChanged02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectControlVisiblityChanged02");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  uint32_t count = control.GetRendererCount();
+  scene.Add(control);
+
+  // Add render effect during scene on.
+  control.SetRenderEffect(BackgroundBlurEffect::New());
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+
+  // Render effect activated.
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.SetProperty(Actor::Property::VISIBLE, false);
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.SetProperty(Actor::Property::VISIBLE, true);
+  // Render effect activated.
+  DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count + 1, control.GetRendererCount(), TEST_LOCATION);
+
+  // Render effect deactivated.
+  control.SetProperty(Actor::Property::VISIBLE, false);
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  control.SetProperty(Actor::Property::VISIBLE, true);
+  // Render effect not activated.
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+
+  END_TEST;
+}
index 48fd4ce..2bd9077 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -88,3 +88,93 @@ int UtcDaliSvgVisualChageSize(void)
 
   END_TEST;
 }
+
+int UtcDaliSvgVisualSvgCacheFileAndRasterizedTexture(void)
+{
+  tet_infoline("Test rasterized texture cached");
+
+  ToolkitTestApplication application;
+
+  TraceCallStack& textureTrace = application.GetGlAbstraction().GetTextureTrace();
+  textureTrace.Enable(true);
+
+  Visual::Base visual1 = VisualFactory::Get().CreateVisual(Property::Map().Add(ImageVisual::Property::URL, TEST_SVG_FILE_NAME));
+  DALI_TEST_CHECK(visual1);
+  Visual::Base visual2 = VisualFactory::Get().CreateVisual(Property::Map().Add(ImageVisual::Property::URL, TEST_SVG_FILE_NAME));
+  DALI_TEST_CHECK(visual2);
+  Visual::Base visual3 = VisualFactory::Get().CreateVisual(Property::Map().Add(ImageVisual::Property::URL, TEST_SVG_FILE_NAME));
+  DALI_TEST_CHECK(visual3);
+
+  DummyControl      control1   = DummyControl::New();
+  DummyControlImpl& dummyImpl1 = static_cast<DummyControlImpl&>(control1.GetImplementation());
+  dummyImpl1.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual1);
+
+  DummyControl      control2   = DummyControl::New();
+  DummyControlImpl& dummyImpl2 = static_cast<DummyControlImpl&>(control2.GetImplementation());
+  dummyImpl2.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual2);
+
+  DummyControl      control3   = DummyControl::New();
+  DummyControlImpl& dummyImpl3 = static_cast<DummyControlImpl&>(control3.GetImplementation());
+  dummyImpl3.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual3);
+
+  application.SendNotification();
+
+  // Wait for loading only one time
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  Vector2 size1(100.0f, 100.0f);
+  Vector2 size2(300.0f, 300.0f);
+
+  tet_printf("Rasterize control1 and control3 as 100x100, control2 as 300x300\n");
+
+  control1.SetProperty(Actor::Property::SIZE, size1);
+  application.GetScene().Add(control1);
+  control2.SetProperty(Actor::Property::SIZE, size2);
+  application.GetScene().Add(control2);
+  control3.SetProperty(Actor::Property::SIZE, size1);
+  application.GetScene().Add(control3);
+
+  visual1.SetTransformAndSize(Property::Map(), size1);
+  visual2.SetTransformAndSize(Property::Map(), size2);
+  visual3.SetTransformAndSize(Property::Map(), size1);
+
+  // Wait for rasterization
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Check we upload only 2 textures.
+  TraceCallStack::NamedParams params;
+  params["width"] << 100;
+  params["height"] << 100;
+  DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), true, TEST_LOCATION);
+  params.mParams.clear();
+  params["width"] << 300;
+  params["height"] << 300;
+  DALI_TEST_EQUALS(textureTrace.FindMethodAndParams("TexImage2D", params), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  textureTrace.Reset();
+
+  tet_printf("Change control3 size from 100x100, to 300x300\n");
+
+  control3.SetProperty(Actor::Property::SIZE, size2);
+  visual3.SetTransformAndSize(Property::Map(), size2);
+
+  application.SendNotification();
+  application.Render();
+
+  // Check we don't doing any additional rasterization
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 0), false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Check we don't upload any additional texture upload
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index 6a5745c..690fd02 100644 (file)
@@ -1273,13 +1273,13 @@ int UtcDaliTextEditorSetPropertyP(void)
   DALI_TEST_EQUALS(editor.GetProperty<float>(DevelTextEditor::Property::MIN_LINE_SIZE), 50.0f, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
 
   // Check Remove Front/Back Inset Property
-  DALI_TEST_CHECK(editor.GetProperty<bool>(DevelTextEditor::Property::REMOVE_FRONT_INSET));
-  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, false);
   DALI_TEST_CHECK(!editor.GetProperty<bool>(DevelTextEditor::Property::REMOVE_FRONT_INSET));
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  DALI_TEST_CHECK(editor.GetProperty<bool>(DevelTextEditor::Property::REMOVE_FRONT_INSET));
 
-  DALI_TEST_CHECK(editor.GetProperty<bool>(DevelTextEditor::Property::REMOVE_BACK_INSET));
-  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, false);
   DALI_TEST_CHECK(!editor.GetProperty<bool>(DevelTextEditor::Property::REMOVE_BACK_INSET));
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
+  DALI_TEST_CHECK(editor.GetProperty<bool>(DevelTextEditor::Property::REMOVE_BACK_INSET));
 
   application.SendNotification();
   application.Render();
@@ -1476,6 +1476,53 @@ int utcDaliTextEditorTextChangedWithInputMethodContext(void)
   END_TEST;
 }
 
+int utcDaliTextEditorFocusWithInputMethodContext(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorFocusWithInputMethodContext");
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK(editor);
+
+  application.GetScene().Add(editor);
+  editor.SetProperty(DevelTextEditor::Property::ENABLE_EDITING, true);
+  application.SendNotification();
+  application.Render();
+
+  // get InputMethodContext
+  InputMethodContext inputMethodContext = DevelTextEditor::GetInputMethodContext(editor);
+  DALI_TEST_CHECK(inputMethodContext);
+
+  // connect StatusChangedSignal
+  editor.SetKeyInputFocus();
+
+  // keyboard shown
+  inputMethodContext.StatusChangedSignal().Emit(true);
+  application.SendNotification();
+  application.Render();
+
+  // keyboard hidden
+  inputMethodContext.StatusChangedSignal().Emit(false);
+  application.SendNotification();
+  application.Render();
+
+  // set focus and keyboard shown
+  editor.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
+  KeyboardFocusManager::Get().SetCurrentFocusActor(editor);
+
+  inputMethodContext.StatusChangedSignal().Emit(true);
+  application.SendNotification();
+  application.Render();
+
+  // keyboard hidden, focus should remain
+  inputMethodContext.StatusChangedSignal().Emit(false);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(editor, KeyboardFocusManager::Get().GetCurrentFocusActor(), TEST_LOCATION);
+
+  END_TEST;
+}
+
 int utcDaliTextEditorInputStyleChanged01(void)
 {
   // The text-editor emits signals when the input style changes. These changes of style are
@@ -4464,6 +4511,8 @@ int UtcDaliTextEditorHyphenWrapMode(void)
   TextEditor textEditor = TextEditor::New();
 
   textEditor.SetProperty(Actor::Property::SIZE, Vector2(150.0f, 300.f));
+  textEditor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  textEditor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
 
   application.GetScene().Add(textEditor);
   application.SendNotification();
@@ -5063,6 +5112,8 @@ int utcDaliTextEditorGeometryEllipsisStart(void)
   editor.SetProperty(DevelTextEditor::Property::ENABLE_SCROLL_BAR, false);
   editor.SetProperty(DevelTextEditor::Property::ELLIPSIS, true);
   editor.SetProperty(DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4");
 
   // Avoid a crash when core load gl resources.
@@ -5114,6 +5165,8 @@ int utcDaliTextEditorGeometryEllipsisMiddle(void)
   editor.SetProperty(DevelTextEditor::Property::ENABLE_SCROLL_BAR, false);
   editor.SetProperty(DevelTextEditor::Property::ELLIPSIS, true);
   editor.SetProperty(DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::MIDDLE);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4");
 
   // Avoid a crash when core load gl resources.
@@ -5165,6 +5218,8 @@ int utcDaliTextEditorGeometryEllipsisEnd(void)
   editor.SetProperty(DevelTextEditor::Property::ENABLE_SCROLL_BAR, false);
   editor.SetProperty(DevelTextEditor::Property::ELLIPSIS, true);
   editor.SetProperty(DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4");
 
   // Avoid a crash when core load gl resources.
@@ -5213,6 +5268,8 @@ int utcDaliTextEditorGeometryRTL(void)
   editor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
   editor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
   editor.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "line1 \nline2\nline 3\nالاخيرالسطر");
 
   // Avoid a crash when core load gl resources.
@@ -5312,6 +5369,8 @@ int utcDaliTextEditorGeometryOneGlyph(void)
   label.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
   label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
   label.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+  label.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextEditor::Property::TEXT, "H");
 
   // Avoid a crash when core load gl resources.
@@ -6653,11 +6712,15 @@ int utcDaliTextEditorRemoveFrontInset(void)
   tet_infoline(" utcDaliTextEditorRemoveFrontInset");
   TextEditor editor = TextEditor::New();
   DALI_TEST_CHECK(editor);
+
   application.GetScene().Add(editor);
   application.SendNotification();
   application.Render();
-  DevelTextEditor::SetRemoveFrontInset(editor, false);
-  DALI_TEST_CHECK(!DevelTextEditor::IsRemoveFrontInset(editor));
+
+  DALI_TEST_CHECK(!DevelTextEditor::IsRemoveFrontInset(editor)); // default value is false.
+  DevelTextEditor::SetRemoveFrontInset(editor, true);
+  DALI_TEST_CHECK(DevelTextEditor::IsRemoveFrontInset(editor));
+
   END_TEST;
 }
 int utcDaliTextEditorRemoveBackInset(void)
@@ -6666,10 +6729,14 @@ int utcDaliTextEditorRemoveBackInset(void)
   tet_infoline(" utcDaliTextEditorRemoveBackInset");
   TextEditor editor = TextEditor::New();
   DALI_TEST_CHECK(editor);
+
   application.GetScene().Add(editor);
   application.SendNotification();
   application.Render();
-  DevelTextEditor::SetRemoveBackInset(editor, false);
-  DALI_TEST_CHECK(!DevelTextEditor::IsRemoveBackInset(editor));
+
+  DALI_TEST_CHECK(!DevelTextEditor::IsRemoveBackInset(editor)); // default value is false.
+  DevelTextEditor::SetRemoveBackInset(editor, true);
+  DALI_TEST_CHECK(DevelTextEditor::IsRemoveBackInset(editor));
+
   END_TEST;
 }
index d098f99..e7d79b6 100644 (file)
@@ -1275,13 +1275,13 @@ int UtcDaliTextFieldSetPropertyP(void)
   application.Render();
 
   // Check Remove Front/Back Inset Property
-  DALI_TEST_CHECK(field.GetProperty<bool>(DevelTextField::Property::REMOVE_FRONT_INSET));
-  field.SetProperty(DevelTextField::Property::REMOVE_FRONT_INSET, false);
   DALI_TEST_CHECK(!field.GetProperty<bool>(DevelTextField::Property::REMOVE_FRONT_INSET));
+  field.SetProperty(DevelTextField::Property::REMOVE_FRONT_INSET, true);
+  DALI_TEST_CHECK(field.GetProperty<bool>(DevelTextField::Property::REMOVE_FRONT_INSET));
 
-  DALI_TEST_CHECK(field.GetProperty<bool>(DevelTextField::Property::REMOVE_BACK_INSET));
-  field.SetProperty(DevelTextField::Property::REMOVE_BACK_INSET, false);
   DALI_TEST_CHECK(!field.GetProperty<bool>(DevelTextField::Property::REMOVE_BACK_INSET));
+  field.SetProperty(DevelTextField::Property::REMOVE_BACK_INSET, true);
+  DALI_TEST_CHECK(field.GetProperty<bool>(DevelTextField::Property::REMOVE_BACK_INSET));
 
   application.SendNotification();
   application.Render();
@@ -1857,6 +1857,53 @@ int utcDaliTextFieldInputFilterWithInputMethodContext(void)
   END_TEST;
 }
 
+int utcDaliTextFieldFocusWithInputMethodContext(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldFocusWithInputMethodContext");
+  TextField field = TextField::New();
+  DALI_TEST_CHECK(field);
+
+  application.GetScene().Add(field);
+  field.SetProperty(DevelTextField::Property::ENABLE_EDITING, true);
+  application.SendNotification();
+  application.Render();
+
+  // get InputMethodContext
+  InputMethodContext inputMethodContext = DevelTextField::GetInputMethodContext(field);
+  DALI_TEST_CHECK(inputMethodContext);
+
+  // connect StatusChangedSignal
+  field.SetKeyInputFocus();
+
+  // keyboard shown
+  inputMethodContext.StatusChangedSignal().Emit(true);
+  application.SendNotification();
+  application.Render();
+
+  // keyboard hidden
+  inputMethodContext.StatusChangedSignal().Emit(false);
+  application.SendNotification();
+  application.Render();
+
+  // set focus and keyboard shown
+  field.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
+  KeyboardFocusManager::Get().SetCurrentFocusActor(field);
+
+  inputMethodContext.StatusChangedSignal().Emit(true);
+  application.SendNotification();
+  application.Render();
+
+  // keyboard hidden, focus should remain
+  inputMethodContext.StatusChangedSignal().Emit(false);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(field, KeyboardFocusManager::Get().GetCurrentFocusActor(), TEST_LOCATION);
+
+  END_TEST;
+}
+
 // Negative test for the textChanged signal.
 int utcDaliTextFieldTextChangedN(void)
 {
@@ -5035,6 +5082,8 @@ int utcDaliTextFieldGeometryEllipsisStart(void)
   field.SetProperty(TextField::Property::ENABLE_MARKUP, true);
   field.SetProperty(DevelTextField::Property::ELLIPSIS, true);
   field.SetProperty(DevelTextField::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START);
+  field.SetProperty(DevelTextField::Property::REMOVE_FRONT_INSET, true);
+  field.SetProperty(DevelTextField::Property::REMOVE_BACK_INSET, true);
   field.SetProperty(TextField::Property::TEXT, "Hello World");
 
   // Avoid a crash when core load gl resources.
@@ -5082,6 +5131,8 @@ int utcDaliTextFieldGeometryEllipsisEnd(void)
   field.SetProperty(TextField::Property::ENABLE_MARKUP, true);
   field.SetProperty(DevelTextField::Property::ELLIPSIS, true);
   field.SetProperty(DevelTextField::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END);
+  field.SetProperty(DevelTextField::Property::REMOVE_FRONT_INSET, true);
+  field.SetProperty(DevelTextField::Property::REMOVE_BACK_INSET, true);
   field.SetProperty(TextField::Property::TEXT, "Hello World");
 
   // Avoid a crash when core load gl resources.
@@ -6166,8 +6217,9 @@ int utcDaliTextFieldRemoveFrontInset(void)
   application.SendNotification();
   application.Render();
 
-  DevelTextField::SetRemoveFrontInset(field, false);
-  DALI_TEST_CHECK(!DevelTextField::IsRemoveFrontInset(field));
+  DALI_TEST_CHECK(!DevelTextField::IsRemoveFrontInset(field)); // default value is false.
+  DevelTextField::SetRemoveFrontInset(field, true);
+  DALI_TEST_CHECK(DevelTextField::IsRemoveFrontInset(field));
 
   END_TEST;
 }
@@ -6184,8 +6236,9 @@ int utcDaliTextFieldRemoveBackInset(void)
   application.SendNotification();
   application.Render();
 
-  DevelTextField::SetRemoveBackInset(field, false);
-  DALI_TEST_CHECK(!DevelTextField::IsRemoveBackInset(field));
+  DALI_TEST_CHECK(!DevelTextField::IsRemoveBackInset(field)); // default value is false.
+  DevelTextField::SetRemoveBackInset(field, true);
+  DALI_TEST_CHECK(DevelTextField::IsRemoveBackInset(field));
 
   END_TEST;
 }
index 13e8dee..6ce2fb2 100644 (file)
@@ -63,6 +63,8 @@ int UtcDaliTextGeometryGetLineBoundingRectangleLabel(void)
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(DevelTextLabel::Property::LINE_SPACING, lineSpacing);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -89,6 +91,8 @@ int UtcDaliTextGeometryGetLineBoundingRectangleEditor(void)
 
   editor.SetProperty(Actor::Property::SIZE, Vector2(160.0f, 250.f));
   editor.SetProperty(TextEditor::Property::POINT_SIZE, 10.f);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -116,6 +120,8 @@ int UtcDaliTextGeometryGetLineBoundingRectangleField(void)
   field.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
   field.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 350.f));
   field.SetProperty(TextField::Property::POINT_SIZE, 10.f);
+  field.SetProperty(DevelTextField::Property::REMOVE_FRONT_INSET, true);
+  field.SetProperty(DevelTextField::Property::REMOVE_BACK_INSET, true);
   field.SetProperty(TextField::Property::TEXT, "مرحبا بالعالم");
 
   application.SendNotification();
@@ -172,6 +178,8 @@ int UtcDaliTextGeometryLineSpacingPositiveGetLineBoundingRectangleLabel(void)
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(DevelTextLabel::Property::LINE_SPACING, lineSpacing);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -200,6 +208,8 @@ int UtcDaliTextGeometryWithVerticalLineAlignmentTopGetLineBoundingRectangleLabel
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "TOP");
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -228,6 +238,8 @@ int UtcDaliTextGeometryWithVerticalLineAlignmentBottomGetLineBoundingRectangleLa
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "BOTTOM");
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -256,6 +268,8 @@ int UtcDaliTextGeometryWithEllipsisMiddleGetLineBoundingRectangleLabel(void)
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(TextLabel::Property::ELLIPSIS, true);
   label.SetProperty(DevelTextLabel::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::MIDDLE);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -285,6 +299,8 @@ int UtcDaliTextGeometryWithEllipsisStartGetLineBoundingRectangleLabel(void)
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(TextLabel::Property::ELLIPSIS, true);
   label.SetProperty(DevelTextLabel::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -314,6 +330,8 @@ int UtcDaliTextGeometryWithEllipsisEndGetLineBoundingRectangleLabel(void)
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(TextLabel::Property::ELLIPSIS, true);
   label.SetProperty(DevelTextLabel::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -344,6 +362,8 @@ int UtcDaliTextGeometryGetCharacterBoundingRectangleLabel(void)
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(DevelTextLabel::Property::LINE_SPACING, lineSpacing);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -374,6 +394,8 @@ int UtcDaliTextGeometryGetCharacterBoundingRectangleEditor(void)
 
   editor.SetProperty(Actor::Property::SIZE, Vector2(160.0f, 250.f));
   editor.SetProperty(TextEditor::Property::POINT_SIZE, 10.f);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -458,6 +480,8 @@ int UtcDaliTextGeometryLineSpacingPositiveGetCharacterBoundingRectangleLabel(voi
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(DevelTextLabel::Property::LINE_SPACING, lineSpacing);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -486,6 +510,8 @@ int UtcDaliTextGeometryWithVerticalLineAlignmentTopGetCharacterBoundingRectangle
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "TOP");
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -514,6 +540,8 @@ int UtcDaliTextGeometryWithVerticalLineAlignmentBottomGetCharacterBoundingRectan
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
   label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "BOTTOM");
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, \n consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
 
   application.SendNotification();
@@ -566,6 +594,8 @@ int UtcDaliTextGeometryGetCharacterIndexAtPositionTextEditor(void)
 
   editor.SetProperty(Actor::Property::SIZE, Vector2(160.0f, 250.f));
   editor.SetProperty(TextEditor::Property::POINT_SIZE, 10.f);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_FRONT_INSET, true);
+  editor.SetProperty(DevelTextEditor::Property::REMOVE_BACK_INSET, true);
   editor.SetProperty(TextEditor::Property::TEXT, "Hello everyone.");
 
   application.SendNotification();
@@ -645,6 +675,8 @@ int UtcDaliTextGeometryGetCharacterIndexAtPositionLastCharacter(void)
 
   label.SetProperty(Actor::Property::SIZE, Vector2(450.0f, 300.f));
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Hello");
 
   application.SendNotification();
diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel-Async.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel-Async.cpp
new file mode 100644 (file)
index 0000000..4165dfb
--- /dev/null
@@ -0,0 +1,2757 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <toolkit-event-thread-callback.h>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
+#include <dali-toolkit/devel-api/controls/text-controls/text-style-properties-devel.h>
+#include <dali-toolkit/devel-api/text/bitmap-font.h>
+#include <dali-toolkit/devel-api/text/rendering-backend.h>
+#include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali-toolkit/devel-api/text/text-utils-devel.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/text-abstraction/bitmap-font.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include "test-text-geometry-utils.h"
+
+using namespace Dali;
+using namespace Toolkit;
+
+void dali_textlabel_async_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void dali_textlabel_async_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+static int   ASYNC_TEXT_THREAD_TIMEOUT = 5;
+
+static bool  gAsyncTextRenderedCalled;
+static float gAsyncTextRenderedWidth;
+static float gAsyncTextRenderedHeight;
+
+static bool  gAsyncSizeComputedCalled;
+static float gAsyncSizeComputedWidth;
+static float gAsyncSizeComputedHeight;
+
+static bool  gTextFitChangedCalled;
+
+float ConvertToEven(float value)
+{
+  int intValue(static_cast<int>(value));
+  return static_cast<float>(intValue + (intValue & 1));
+}
+
+struct CallbackFunctor
+{
+  CallbackFunctor(bool* callbackFlag)
+  : mCallbackFlag(callbackFlag)
+  {
+  }
+
+  void operator()()
+  {
+    *mCallbackFlag = true;
+  }
+  bool* mCallbackFlag;
+};
+
+static void TestAsyncTextRendered(TextLabel control, float width, float height)
+{
+  tet_infoline(" TestAsyncTextRendered");
+  gAsyncTextRenderedCalled = true;
+  gAsyncTextRenderedWidth  = width;
+  gAsyncTextRenderedHeight = height;
+}
+
+static void TestAsyncSizeComputed(TextLabel control, float width, float height)
+{
+  tet_infoline(" TestAsyncSizeComputed");
+  gAsyncSizeComputedCalled = true;
+  gAsyncSizeComputedWidth  = width;
+  gAsyncSizeComputedHeight = height;
+}
+
+static void TestTextFitChanged(TextLabel control)
+{
+  tet_infoline(" TestTextFitChanged");
+  gTextFitChangedCalled = true;
+}
+
+} // namespace
+
+int UtcDaliToolkitTextLabelAsyncRender01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRender01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(false, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRender02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRender02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  Property::Map underline;
+  underline.Clear();
+  underline.Insert("enable", true);
+  underline.Insert("color", Color::RED);
+  underline.Insert("height", 1);
+  label.SetProperty(TextLabel::Property::UNDERLINE, underline);
+
+  Property::Map strikethrough;
+  strikethrough.Clear();
+  strikethrough.Insert("enable", true);
+  strikethrough.Insert("color", Color::BLUE);
+  strikethrough.Insert("height", 2.0f);
+  label.SetProperty(DevelTextLabel::Property::STRIKETHROUGH, strikethrough);
+
+  Property::Map outline;
+  outline["color"] = Color::GREEN;
+  outline["width"] = 2.0f;
+  outline["offset"] = Vector2(2.0f, 2.0f);
+  outline["blurRadius"] = 3.0f;
+  label.SetProperty(TextLabel::Property::OUTLINE, outline);
+
+  Property::Map shadow;
+  shadow.Insert("color", Color::BLACK);
+  shadow.Insert("offset", Vector2(1.0f, 1.0f));
+  label.SetProperty(TextLabel::Property::SHADOW, shadow);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(false, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+  expectedWidth            = 150.0f;
+
+  label.SetProperty(Actor::Property::SIZE_WIDTH, expectedWidth);
+  label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
+  label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(false, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+  expectedHeight           = 150.0f;
+
+  label.SetProperty(Actor::Property::SIZE_HEIGHT, expectedHeight);
+  label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, "BEGIN");
+  label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "BOTTOM");
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(false, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedSize01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedSize01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 200.0f;
+  float expectedHeight = 200.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  // RTL test.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  label.SetProperty(TextLabel::Property::TEXT, "Update paragraphs with different directions. Update middle paragraphs. مرحبا بالعالم שלום עולם مرحبا بالعالم Hello world.");
+  label.SetProperty(Actor::Property::LAYOUT_DIRECTION, LayoutDirection::RIGHT_TO_LEFT);
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedSize02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedSize02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the async natural size computed signal.
+  ConnectionTracker* testSizeTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncNaturalSizeComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testSizeTracker, "asyncNaturalSizeComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request render, but the request will cancelled due to being in sync mode.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, labelWidth, labelHeight);
+
+  // Async size computation also works in sync mode.
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(!gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(!asyncTextRendered);
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth  = label.GetNaturalSize().width;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncSizeComputedWidth  = ConvertToEven(gAsyncSizeComputedWidth);
+  gAsyncSizeComputedHeight = ConvertToEven(gAsyncSizeComputedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedWidth01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedWidth01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, 300.0f, std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = label.GetHeightForWidth(expectedWidth);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // RTL test.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  label.SetProperty(TextLabel::Property::TEXT, "Update paragraphs with different directions. Update middle paragraphs. مرحبا بالعالم שלום עולם مرحبا بالعالم Hello world.");
+  label.SetProperty(Actor::Property::LAYOUT_DIRECTION, LayoutDirection::RIGHT_TO_LEFT);
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, 300.0f, std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  expectedHeight = label.GetHeightForWidth(expectedWidth);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedWidth02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncRenderWithFixedWidth02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the async natural size computed signal.
+  ConnectionTracker* testSizeTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncNaturalSizeComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testSizeTracker, "asyncNaturalSizeComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request render, but the request will cancelled due to being in sync mode.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, labelWidth, std::numeric_limits<float>::infinity());
+
+  // Async size computation also works in sync mode.
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(!gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(!asyncTextRendered);
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth  = label.GetNaturalSize().width;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncSizeComputedWidth  = ConvertToEven(gAsyncSizeComputedWidth);
+  gAsyncSizeComputedHeight = ConvertToEven(gAsyncSizeComputedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncRenderWithConstraint01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncRenderWithConstraint01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, false);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  float expectedWidth  = label.GetNaturalSize().width;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncTextRenderedWidth  = ConvertToEven(gAsyncTextRenderedWidth);
+  gAsyncTextRenderedHeight = ConvertToEven(gAsyncTextRenderedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // RTL test.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  label.SetProperty(TextLabel::Property::TEXT, "Update paragraphs with different directions. Update middle paragraphs. مرحبا بالعالم שלום עולם مرحبا بالعالم Hello world.");
+  label.SetProperty(Actor::Property::LAYOUT_DIRECTION, LayoutDirection::RIGHT_TO_LEFT);
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  expectedWidth  = label.GetNaturalSize().width;
+  expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncTextRenderedWidth  = ConvertToEven(gAsyncTextRenderedWidth);
+  gAsyncTextRenderedHeight = ConvertToEven(gAsyncTextRenderedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Constraint test.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  label.SetProperty(TextLabel::Property::TEXT, "Update paragraphs with different directions. Update middle paragraphs. مرحبا بالعالم שלום עולם مرحبا بالعالم Hello world.");
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 30);
+  label.SetProperty(Actor::Property::LAYOUT_DIRECTION, LayoutDirection::LEFT_TO_RIGHT);
+
+  expectedWidth  = 50.0f;
+  expectedHeight = label.GetNaturalSize().height;
+
+  // Request render with small constraint
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncRenderWithConstraint02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncRenderWithConstraint02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the async natural size computed signal.
+  ConnectionTracker* testSizeTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncNaturalSizeComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testSizeTracker, "asyncNaturalSizeComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request render, but the request will cancelled due to being in sync mode.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
+
+  // Async size computation also works in sync mode.
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(!gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(!asyncTextRendered);
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth  = label.GetNaturalSize().width;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncSizeComputedWidth  = ConvertToEven(gAsyncSizeComputedWidth);
+  gAsyncSizeComputedHeight = ConvertToEven(gAsyncSizeComputedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncNaturalSize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncNaturalSize");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  // Connect to the async natural size computed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncNaturalSizeComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testTracker, "asyncNaturalSizeComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request size computation.
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth  = label.GetNaturalSize().width;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncSizeComputedWidth  = ConvertToEven(gAsyncSizeComputedWidth);
+  gAsyncSizeComputedHeight = ConvertToEven(gAsyncSizeComputedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // RTL test.
+  label.SetProperty(TextLabel::Property::TEXT, "Update paragraphs with different directions. Update middle paragraphs. مرحبا بالعالم שלום עולם مرحبا بالعالم Hello world.");
+  label.SetProperty(Actor::Property::LAYOUT_DIRECTION, LayoutDirection::RIGHT_TO_LEFT);
+
+  asyncSizeComputed        = false;
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request size computation.
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  expectedWidth  = label.GetNaturalSize().width;
+  expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncSizeComputedWidth  = ConvertToEven(gAsyncSizeComputedWidth);
+  gAsyncSizeComputedHeight = ConvertToEven(gAsyncSizeComputedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncHeightForWidth(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncHeightForWidth");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  // Connect to the async height for width computed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncHeightForWidthComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testTracker, "asyncHeightForWidthComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request size computation.
+  DevelTextLabel::RequestAsyncHeightForWidth(label, 300.0f);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth     = 300.0f;
+  float expectedHeight    = label.GetHeightForWidth(expectedWidth);
+  int   expectedLineCount = label.GetProperty<int>(TextLabel::Property::LINE_COUNT);
+  int   asyncLineCount    = label.GetProperty<int>(DevelTextLabel::Property::ASYNC_LINE_COUNT);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedLineCount, asyncLineCount, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // RTL test.
+  label.SetProperty(TextLabel::Property::TEXT, "Update paragraphs with different directions. Update middle paragraphs. مرحبا بالعالم שלום עולם مرحبا بالعالم Hello world.");
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 14);
+  label.SetProperty(Actor::Property::LAYOUT_DIRECTION, LayoutDirection::RIGHT_TO_LEFT);
+
+  asyncSizeComputed        = false;
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  // Request size computation.
+  DevelTextLabel::RequestAsyncHeightForWidth(label, 300.0f);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  expectedWidth     = 300.0f;
+  expectedHeight    = label.GetHeightForWidth(expectedWidth);
+  expectedLineCount = label.GetProperty<int>(TextLabel::Property::LINE_COUNT);
+  asyncLineCount    = label.GetProperty<int>(DevelTextLabel::Property::ASYNC_LINE_COUNT);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedLineCount, asyncLineCount, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderTextFit01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderTextFit01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::MULTI_LINE, false);
+
+  // Text fit
+  Property::Map textFitMapSet;
+  textFitMapSet["enable"]       = true;
+  textFitMapSet["minSize"]      = 10.f;
+  textFitMapSet["maxSize"]      = 30.f;
+  textFitMapSet["stepSize"]     = 5.f;
+  textFitMapSet["fontSizeType"] = "pointSize";
+  label.SetProperty(Toolkit::DevelTextLabel::Property::TEXT_FIT, textFitMapSet);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the text fit changed signal.
+  ConnectionTracker* testFitTracker = new ConnectionTracker();
+  DevelTextLabel::TextFitChangedSignal(label).Connect(&TestTextFitChanged);
+
+  bool textFitChanged = false;
+  label.ConnectSignal(testFitTracker, "textFitChanged", CallbackFunctor(&textFitChanged));
+  gTextFitChangedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_CHECK(gTextFitChangedCalled);
+  DALI_TEST_CHECK(textFitChanged);
+
+  float textFitFontSize = (label.GetProperty(Dali::Toolkit::DevelTextLabel::Property::TEXT_FIT).Get<Property::Map>())["fontSize"].Get<float>();
+
+  // To measure expected size.
+  TextLabel sizeLabel = TextLabel::New();
+  DALI_TEST_CHECK(sizeLabel);
+
+  sizeLabel.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  sizeLabel.SetProperty(TextLabel::Property::TEXT, "Hello");
+  sizeLabel.SetProperty(TextLabel::Property::MULTI_LINE, false);
+  sizeLabel.SetProperty(TextLabel::Property::POINT_SIZE, textFitFontSize);
+
+  DALI_TEST_EQUALS(sizeLabel.GetNaturalSize(), label.GetNaturalSize(), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Text fit array, make sorted options.
+  std::vector<DevelTextLabel::FitOption> fitOptions;
+  fitOptions.push_back(DevelTextLabel::FitOption(10, 15));
+  fitOptions.push_back(DevelTextLabel::FitOption(15, 20));
+  fitOptions.push_back(DevelTextLabel::FitOption(20, 25));
+  fitOptions.push_back(DevelTextLabel::FitOption(25, 30));
+  fitOptions.push_back(DevelTextLabel::FitOption(30, 35));
+  DevelTextLabel::SetTextFitArray(label, true, fitOptions);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(sizeLabel.GetNaturalSize(), label.GetNaturalSize(), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Text fit array, empty fit options.
+  std::vector<DevelTextLabel::FitOption> emptyFitOptions;
+  DevelTextLabel::SetTextFitArray(label, false, emptyFitOptions);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  // Text fit array, empty fit options, negative.
+  DevelTextLabel::SetTextFitArray(label, true, emptyFitOptions);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  // Text fit array, make unsorted options.
+  std::vector<DevelTextLabel::FitOption> unsortedFitOptions;
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(10, 15));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(15, 10));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(20, 25));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(25, 30));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(30, 35));
+  DevelTextLabel::SetTextFitArray(label, true, unsortedFitOptions);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(sizeLabel.GetNaturalSize(), label.GetNaturalSize(), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderTextFit02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderTextFit02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12.0f);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, false);
+
+  // Text fit
+  Property::Map textFitMapSet;
+  textFitMapSet["enable"]       = true;
+  textFitMapSet["minSize"]      = 10.f;
+  textFitMapSet["maxSize"]      = 30.f;
+  textFitMapSet["stepSize"]     = 5.f;
+  textFitMapSet["fontSizeType"] = "pointSize";
+  label.SetProperty(Toolkit::DevelTextLabel::Property::TEXT_FIT, textFitMapSet);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the text fit changed signal.
+  ConnectionTracker* testFitTracker = new ConnectionTracker();
+  DevelTextLabel::TextFitChangedSignal(label).Connect(&TestTextFitChanged);
+
+  bool textFitChanged = false;
+  label.ConnectSignal(testFitTracker, "textFitChanged", CallbackFunctor(&textFitChanged));
+  gTextFitChangedCalled = false;
+
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // TextFit requires a fixed size. Render with natural size.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_CHECK(gTextFitChangedCalled);
+  DALI_TEST_CHECK(textFitChanged);
+
+  float textFitFontSize = (label.GetProperty(Dali::Toolkit::DevelTextLabel::Property::TEXT_FIT).Get<Property::Map>())["fontSize"].Get<float>();
+
+  // To measure expected size.
+  TextLabel sizeLabel = TextLabel::New();
+  DALI_TEST_CHECK(sizeLabel);
+
+  sizeLabel.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  sizeLabel.SetProperty(TextLabel::Property::TEXT, "Hello");
+  sizeLabel.SetProperty(TextLabel::Property::MULTI_LINE, false);
+  sizeLabel.SetProperty(TextLabel::Property::POINT_SIZE, textFitFontSize);
+
+  float minWidth  = sizeLabel.GetNaturalSize().width;
+  float minHeight = sizeLabel.GetNaturalSize().height;
+
+  gAsyncTextRenderedWidth  = ConvertToEven(gAsyncTextRenderedWidth);
+  gAsyncTextRenderedHeight = ConvertToEven(gAsyncTextRenderedHeight);
+
+  bool width  = gAsyncTextRenderedWidth  >= minWidth;
+  bool height = gAsyncTextRenderedHeight >= minHeight;
+
+  DALI_TEST_CHECK(width);
+  DALI_TEST_CHECK(height);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderTextFit03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderTextFit03");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12.0f);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, false);
+
+  std::string longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Vestibulum volutpat pretium libero. Vivamus at augue. In hac habitasse platea dictumst. Pellentesque eu metus. Etiam vitae tortor. Morbi vestibulum volutpat enim. Fusce vel dui. Sed vulputate odio vel purus. Aliquam at lorem. \U0001F31F";
+  label.SetProperty(TextLabel::Property::TEXT, longText);
+
+  // Text fit
+  Property::Map textFitMapSet;
+  textFitMapSet["enable"]       = true;
+  textFitMapSet["minSize"]      = 10.0f;
+  textFitMapSet["maxSize"]      = 30.0f;
+  textFitMapSet["stepSize"]     = 0.5f;
+  textFitMapSet["fontSizeType"] = "pointSize";
+  label.SetProperty(Toolkit::DevelTextLabel::Property::TEXT_FIT, textFitMapSet);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the text fit changed signal.
+  ConnectionTracker* testFitTracker = new ConnectionTracker();
+  DevelTextLabel::TextFitChangedSignal(label).Connect(&TestTextFitChanged);
+
+  bool textFitChanged = false;
+  label.ConnectSignal(testFitTracker, "textFitChanged", CallbackFunctor(&textFitChanged));
+  gTextFitChangedCalled = false;
+
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 200.0f;
+  float expectedHeight = 200.0f;
+
+  // TextFit requires a fixed size. Render with natural size.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_CHECK(gTextFitChangedCalled);
+  DALI_TEST_CHECK(textFitChanged);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Text fit array, make sorted options.
+  std::vector<DevelTextLabel::FitOption> fitOptions;
+  fitOptions.push_back(DevelTextLabel::FitOption(50, 60));
+  fitOptions.push_back(DevelTextLabel::FitOption(60, 70));
+  fitOptions.push_back(DevelTextLabel::FitOption(70, 80));
+  fitOptions.push_back(DevelTextLabel::FitOption(80, 90));
+  fitOptions.push_back(DevelTextLabel::FitOption(90, 100));
+  DevelTextLabel::SetTextFitArray(label, true, fitOptions);
+
+  // TextFit requires a fixed size. Render with natural size.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Text fit array, make unsorted options.
+  std::vector<DevelTextLabel::FitOption> unsortedFitOptions;
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(50, 80));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(60, 70));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(70, 80));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(80, 90));
+  unsortedFitOptions.push_back(DevelTextLabel::FitOption(90, 100));
+  DevelTextLabel::SetTextFitArray(label, true, unsortedFitOptions);
+
+  // TextFit requires a fixed size. Render with natural size.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderAutoScroll01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderAutoScroll01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, false);
+
+  // Auto scroll
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, true);
+  label.SetProperty(TextLabel::Property::AUTO_SCROLL_STOP_MODE, TextLabel::AutoScrollStopMode::IMMEDIATE);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+  DALI_TEST_CHECK(label.GetProperty<bool>(TextLabel::Property::ENABLE_AUTO_SCROLL));
+
+  // stop IMMEDIATE.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, false);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+  DALI_TEST_CHECK(!label.GetProperty<bool>(TextLabel::Property::ENABLE_AUTO_SCROLL));
+
+  // restart.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, true);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+  DALI_TEST_CHECK(label.GetProperty<bool>(TextLabel::Property::ENABLE_AUTO_SCROLL));
+
+  // stop FINISH_LOOP.
+  // Rendering should not be requested at this time, because the scroll does not end immediately,
+  label.SetProperty(TextLabel::Property::AUTO_SCROLL_STOP_MODE, TextLabel::AutoScrollStopMode::FINISH_LOOP);
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, false);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  // The finish loop has not ended yet.
+  DALI_TEST_CHECK(label.GetProperty<bool>(TextLabel::Property::ENABLE_AUTO_SCROLL));
+
+  // stop IMMEDIATE.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  label.SetProperty(TextLabel::Property::AUTO_SCROLL_STOP_MODE, TextLabel::AutoScrollStopMode::IMMEDIATE);
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, false);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+  DALI_TEST_CHECK(!label.GetProperty<bool>(TextLabel::Property::ENABLE_AUTO_SCROLL));
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderAutoScroll02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderAutoScroll02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 100);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, false);
+
+  std::string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper.";
+  label.SetProperty(TextLabel::Property::TEXT, text);
+
+  // Auto scroll
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, true);
+  label.SetProperty(TextLabel::Property::AUTO_SCROLL_STOP_MODE, TextLabel::AutoScrollStopMode::IMMEDIATE);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  // Request render auto scroll.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, labelWidth, labelHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  float expectedWidth  = labelWidth;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncTextRenderedHeight = ConvertToEven(gAsyncTextRenderedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // stop IMMEDIATE.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  label.SetProperty(TextLabel::Property::ENABLE_AUTO_SCROLL, false);
+
+  // Request render auto scroll.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, labelWidth, labelHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  expectedWidth  = labelWidth;
+  expectedHeight = labelHeight;
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderCutout(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderCutout");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Hello, World");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  // Cutout
+  label.SetProperty(DevelTextLabel::Property::CUTOUT, true);
+  label.SetProperty(TextLabel::Property::TEXT_COLOR, Color::BLUE);
+  label.SetProperty(Control::Property::BACKGROUND, Color::RED);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  label.SetProperty(DevelTextLabel::Property::CUTOUT, false);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  application.SendNotification();
+  application.Render();
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  label.SetProperty(DevelTextLabel::Property::CUTOUT, true);
+  label.SetProperty(Control::Property::BACKGROUND, Color::GREEN);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderHyphenation(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderCutout");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 150.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::TEXT, "Hi Experimen");
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 35);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  // HYPHENATION
+  // Hi Exp-
+  // erimen
+  label.SetProperty(TextLabel::Property::LINE_WRAP_MODE, DevelText::LineWrap::HYPHENATION);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  int expectedLineCount = label.GetProperty<int>(TextLabel::Property::LINE_COUNT);
+  int asyncLineCount    = label.GetProperty<int>(DevelTextLabel::Property::ASYNC_LINE_COUNT);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+  DALI_TEST_EQUALS(expectedLineCount, asyncLineCount, TEST_LOCATION);
+
+  // MIXED
+  // Hi
+  // Experi-
+  // men
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  label.SetProperty(TextLabel::Property::LINE_WRAP_MODE, DevelText::LineWrap::MIXED);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  expectedLineCount = label.GetProperty<int>(TextLabel::Property::LINE_COUNT);
+  asyncLineCount    = label.GetProperty<int>(DevelTextLabel::Property::ASYNC_LINE_COUNT);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+  DALI_TEST_EQUALS(expectedLineCount, asyncLineCount, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderMarkup01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderMarkup01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  label.SetProperty(TextLabel::Property::TEXT, "<color value='white'>Markup</color><color value='cyan'>Text</color>");
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  // Emoji
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  const std::string emojis = "<font family='BreezeColorEmoji' size='20'>\xF0\x9F\x98\x81 \xF0\x9F\x98\x82 \xF0\x9F\x98\x83 \xF0\x9F\x98\x84</font>";
+  label.SetProperty(TextLabel::Property::TEXT, emojis);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  // EMOJI Sequences case for coverage.
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  std::string emojiSequences =
+    "Glyphs not included in the font &#xf01a;&#xf01b;&#xf01c;&#xf01d;&#xf01e;&#xf01f;\n"   //case for coverage when glyph is not included in the font
+    "Text VS15 &#x262a;&#xfe0e;\n"                                                         //text presentation sequence and selector
+    "Color VS16 &#x262a;&#xfe0f;\n"                                                        //emoji presentation sequence and selector
+    "Default &#x262a; \n"                                                                  //default presentation
+    "FamilyManWomanGirlBoy &#x1F468;&#x200D;&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;\n" // emoji multi zwj sequence
+    "WomanScientist &#x1f469;&#x200d;&#x1f52c;\n"                                          // emoji zwj sequence
+    "WomanScientistLightSkinTone&#x1F469;&#x1F3FB;&#x200D;&#x1F52C; \n"                    // emoji modifier sequence: skin tone & JWZ
+    "LeftRightArrowText&#x2194;&#xfe0e;\n"                                                 // text presentation sequence and selector
+    "LeftRightArrowEmoji&#x2194;&#xfe0f;\n"                                                // emoji presentation sequence and selector
+    "SouthKoreaFlag&#x1f1f0;&#x1f1f7;\n"                                                   // emoji flag sequence
+    "JordanFlag&#x1f1ef;&#x1f1f4;\n"                                                       // emoji flag sequence
+    "EnglandFlag&#x1F3F4;&#xE0067;&#xE0062;&#xE0065;&#xE006E;&#xE0067;&#xE007F;\n"         // emoji tag sequence like England flag
+    "Runner &#x1f3c3;&#x200d;&#x27a1;&#xfe0f; \n"
+    "VictoryHandMediumLightSkinTone:&#x270C;&#xFE0F;&#x1F3FC;\n"                                                                // emoji modifier sequence: skin tone
+    "RainbowFlag:&#x1F3F3;&#xFE0F;&#x200D;&#x1F308; \n"                                                                         // emoji zwj sequence: Rainbow Flag
+    "keycap# &#x0023;&#xFE0F;&#x20E3; \n"                                                                                       // fully-qualified  emoji keycap sequence
+    "keycap#_text &#x0023;&#x20E3; \n"                                                                                          // unqualified emoji keycap sequence
+    "keycap3 &#x0033;&#xfe0f;&#x20e3; \n"                                                                                       // fully-qualified  emoji keycap sequence
+    "keycap3_text &#x0033;&#x20e3; \n"                                                                                          // unqualified emoji keycap sequence
+    "two adjacent glyphs &#x262a;&#xfe0f;&#xfe0f;&#xfe0f;&#x262a;&#xfe0f;\n"                                                    // This line should be rendered as two adjacent glyphs
+    "Digit 8&#xfe0f; 8&#xfe0e; 8\n"                                                                                             // should be rendered according to selector
+    "Surfing Medium Skin Female:  &#x1f3c4;&#x1f3fc;&#x200d;&#x2640;&#xfe0f;\n"                                                 // Person Surfing + Medium Skin Tone +? Zero Width Joiner + Female Sign
+    "SYMBOLS_NSLCL variation selector: &#x1f170;&#xfe0f;&#x1f171;&#xfe0f;&#x1f172;&#xfe0e;&#x1f173;&#xfe0e;&#x1f174;&#xfe0e;\n" // 1F170 ~ 1F174 with variation selector, text vs emoji
+    "SYMBOLS_NSLCL with VS15: &#x1f170;&#xfe0e;&#x1f171;&#xfe0e;&#x1f172;&#xfe0e;&#x1f173;&#xfe0e;&#x1f174;&#xfe0e;\n"          // 1F170 ~ 1F174 with VS15
+    "SYMBOLS_NSLCL with VS16: &#x1f170;&#xfe0f;&#x1f171;&#xfe0f;&#x1f172;&#xfe0f;&#x1f173;&#xfe0f;&#x1f174;&#xfe0f;\n"          // 1F170 ~ 1F174 with VS16
+    ;
+
+  label.SetProperty(TextLabel::Property::TEXT, emojiSequences);
+  label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+  label.SetProperty(TextLabel::Property::ELLIPSIS, false);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderMarkup02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderMarkup02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  float labelWidth  = 300.0f;
+  float labelHeight = 300.0f;
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+  label.SetProperty(Actor::Property::SIZE, Vector2(labelWidth, labelHeight));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testRenderTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testRenderTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // underline
+  std::string underlineText = "start<u height='5.0f' color='green' >underline<u color='blue'>markup text</u>CDE</u>end";
+  label.SetProperty(TextLabel::Property::TEXT, underlineText);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  // strikethrough
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  std::string strikethroughText = "start<s height='5.0f' color='green' >strikethrough<s color='blue' >markup text</s>CDE</s>end";
+  label.SetProperty(TextLabel::Property::TEXT, strikethroughText);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  // character spacing
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  std::string charspacingText = "start\n<char-spacing value='5.0f'>CHAR\n</char-spacing><char-spacing value='10.0f'>SPACING\n</char-spacing>end";
+  label.SetProperty(TextLabel::Property::TEXT, charspacingText);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  application.SendNotification();
+  application.Render();
+
+  // emoji
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+
+  std::string emojiText = "Color VS16 \u262a\ufe0f";
+  label.SetProperty(TextLabel::Property::TEXT, emojiText);
+
+  // Request render automatically.
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderTiling01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderTiling01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 100);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  std::string longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Vestibulum volutpat pretium libero. Vivamus at augue. In hac habitasse platea dictumst. Pellentesque eu metus. Etiam vitae tortor. Morbi vestibulum volutpat enim. Fusce vel dui. Sed vulputate odio vel purus. Aliquam at lorem. \U0001F31F";
+  label.SetProperty(TextLabel::Property::TEXT, longText);  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 100.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, expectedWidth, std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT * 2), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  float expectedHeight = label.GetHeightForWidth(expectedWidth);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  Property::Map underline;
+  underline.Clear();
+  underline.Insert("enable", true);
+  underline.Insert("color", Color::RED);
+  underline.Insert("height", 1);
+  label.SetProperty(TextLabel::Property::UNDERLINE, underline);
+
+  Property::Map strikethrough;
+  strikethrough.Clear();
+  strikethrough.Insert("enable", true);
+  strikethrough.Insert("color", Color::BLUE);
+  strikethrough.Insert("height", 2.0f);
+  label.SetProperty(DevelTextLabel::Property::STRIKETHROUGH, strikethrough);
+
+  Property::Map outline;
+  outline["color"] = Color::GREEN;
+  outline["width"] = 2.0f;
+  outline["offset"] = Vector2(2.0f, 2.0f);
+  outline["blurRadius"] = 3.0f;
+  label.SetProperty(TextLabel::Property::OUTLINE, outline);
+
+  Property::Map shadow;
+  shadow.Insert("color", Color::BLACK);
+  shadow.Insert("offset", Vector2(1.0f, 1.0f));
+  label.SetProperty(TextLabel::Property::SHADOW, shadow);
+
+  expectedWidth  = 100.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, expectedWidth, std::numeric_limits<float>::infinity());
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT * 2), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  expectedHeight = label.GetHeightForWidth(expectedWidth);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncComputation01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncComputation01");
+
+  // Cancel async render task.
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  // dummy text for test.
+  TextLabel dummy1 = TextLabel::New();
+  DALI_TEST_CHECK(dummy1);
+  dummy1.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy1.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy1.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy1.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy2 = TextLabel::New();
+  DALI_TEST_CHECK(dummy2);
+  dummy2.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy2.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy2.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy2.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 200.0f;
+  float expectedHeight = 200.0f;
+  float dummySize      = 100.0f;
+
+  std::string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+  dummy1.SetProperty(TextLabel::Property::TEXT, text);
+  dummy2.SetProperty(TextLabel::Property::TEXT, text);
+  label.SetProperty(TextLabel::Property::TEXT, text);
+
+  // Request size computation, due to dummy's requests, text manager's loader queue is full.
+  DevelTextLabel::RequestAsyncNaturalSize(dummy1);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy1, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy2);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy2, dummySize);
+
+  // Request render .. [Task 1]
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, expectedWidth, std::numeric_limits<float>::infinity());
+
+  // Request render .. [Task 2]
+  // The unfinished [Task 1] will be canceled.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(6, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncComputation02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncComputation02");
+
+  // Cancel async natural size computation task.
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async natural size computed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncNaturalSizeComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  // dummy text for test.
+  TextLabel dummy1 = TextLabel::New();
+  DALI_TEST_CHECK(dummy1);
+  dummy1.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy1.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy1.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy1.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy2 = TextLabel::New();
+  DALI_TEST_CHECK(dummy2);
+  dummy2.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy2.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy2.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy2.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testTracker, "asyncNaturalSizeComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  float dummySize = 100.0f;
+
+  std::string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+  dummy1.SetProperty(TextLabel::Property::TEXT, text);
+  dummy2.SetProperty(TextLabel::Property::TEXT, text);
+  label.SetProperty(TextLabel::Property::TEXT, text);
+
+  // Request size computation, due to dummy's requests, text manager's loader queue is full.
+  DevelTextLabel::RequestAsyncNaturalSize(dummy1);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy1, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy2);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy2, dummySize);
+
+  // Request compute .. [Task 1]
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  // Request compute .. [Task 2]
+  // The unfinished [Task 1] will be canceled.
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(6, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth  = label.GetNaturalSize().width;
+  float expectedHeight = label.GetNaturalSize().height;
+
+  gAsyncSizeComputedWidth  = ConvertToEven(gAsyncSizeComputedWidth);
+  gAsyncSizeComputedHeight = ConvertToEven(gAsyncSizeComputedHeight);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelRequestAsyncComputation03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelRequestAsyncComputation03");
+
+  // Cancel async height for width computation task.
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async height for width computed signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncHeightForWidthComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  // dummy text for test.
+  TextLabel dummy1 = TextLabel::New();
+  DALI_TEST_CHECK(dummy1);
+  dummy1.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy1.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy1.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy1.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy2 = TextLabel::New();
+  DALI_TEST_CHECK(dummy2);
+  dummy2.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy2.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy2.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy2.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  bool asyncSizeComputed = false;
+  label.ConnectSignal(testTracker, "asyncHeightForWidthComputed", CallbackFunctor(&asyncSizeComputed));
+
+  gAsyncSizeComputedCalled = false;
+  gAsyncSizeComputedWidth  = 0.0f;
+  gAsyncSizeComputedHeight = 0.0f;
+
+  float dummySize = 100.0f;
+
+  std::string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+  dummy1.SetProperty(TextLabel::Property::TEXT, text);
+  dummy2.SetProperty(TextLabel::Property::TEXT, text);
+  label.SetProperty(TextLabel::Property::TEXT, text);
+
+  // Request size computation, due to dummy's requests, text manager's loader queue is full.
+  DevelTextLabel::RequestAsyncNaturalSize(dummy1);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy1, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy2);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy2, dummySize);
+
+  // Request compute .. [Task 1]
+  DevelTextLabel::RequestAsyncHeightForWidth(label, 100.0f);
+
+  // Request compute .. [Task 2]
+  // The unfinished [Task 1] will be canceled.
+  DevelTextLabel::RequestAsyncHeightForWidth(label, 300.0f);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(6, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(asyncSizeComputed);
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = label.GetHeightForWidth(expectedWidth);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncSizeComputedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncSizeComputedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncSceneDisconnection(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncSceneDisconnection");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+  application.GetScene().Remove(label);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(!gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(!asyncTextRendered);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncUnparentAndReset01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncUnparentAndReset01");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+  label.Unparent();
+  label.Reset();
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(!gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(!asyncTextRendered);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncUnparentAndReset02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncUnparentAndReset02");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  application.GetScene().Add(label);
+
+  // dummy text for test.
+  TextLabel dummy1 = TextLabel::New();
+  DALI_TEST_CHECK(dummy1);
+  dummy1.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy1.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  dummy1.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy1.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy1.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy2 = TextLabel::New();
+  DALI_TEST_CHECK(dummy2);
+  dummy2.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy2.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  dummy2.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy2.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy2.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy3 = TextLabel::New();
+  DALI_TEST_CHECK(dummy3);
+  dummy3.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy3.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  dummy3.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy3.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy3.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  TextLabel dummy4 = TextLabel::New();
+  DALI_TEST_CHECK(dummy4);
+  dummy4.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  dummy4.SetProperty(TextLabel::Property::TEXT, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+  dummy4.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  dummy4.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  dummy4.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+  gAsyncTextRenderedCalled = false;
+
+  // Connect to the async natural size computed signal.
+  ConnectionTracker* testNaturalSizeTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncNaturalSizeComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncNaturalSizeComputed = false;
+  label.ConnectSignal(testNaturalSizeTracker, "asyncNaturalSizeComputed", CallbackFunctor(&asyncNaturalSizeComputed));
+
+  // Connect to the async height for width computed signal.
+  ConnectionTracker* testHeightForWidthTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncHeightForWidthComputedSignal(label).Connect(&TestAsyncSizeComputed);
+
+  bool asyncHeightForWidthComputed = false;
+  label.ConnectSignal(testHeightForWidthTracker, "asyncHeightForWidthComputed", CallbackFunctor(&asyncHeightForWidthComputed));
+
+  gAsyncSizeComputedCalled = false;
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+  float dummySize      = 100.0f;
+
+  // Request size computation, due to dummy's requests, text manager's loader queue is full.
+  DevelTextLabel::RequestAsyncNaturalSize(dummy1);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy1, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy2);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy2, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy3);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy3, dummySize);
+  DevelTextLabel::RequestAsyncNaturalSize(dummy4);
+  DevelTextLabel::RequestAsyncHeightForWidth(dummy4, dummySize);
+
+  // Request render and size computation.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+  DevelTextLabel::RequestAsyncNaturalSize(label);
+  DevelTextLabel::RequestAsyncHeightForWidth(label, expectedWidth);
+
+  dummy3.Unparent();
+  dummy3.Reset();
+
+  application.SendNotification();
+  application.Render();
+
+  dummy4.Unparent();
+  dummy4.Reset();
+
+  label.Unparent();
+  label.Reset();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(9, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(!gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(!asyncTextRendered);
+
+  DALI_TEST_CHECK(!gAsyncSizeComputedCalled);
+  DALI_TEST_CHECK(!asyncNaturalSizeComputed);
+  DALI_TEST_CHECK(!asyncHeightForWidthComputed);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncSetText(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncSetText");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 12);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+
+  // Request render.
+  std::string text = "Hello, world!";
+  label.SetProperty(TextLabel::Property::TEXT, text);
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  // Request render.
+  std::string emptyText = "";
+  label.SetProperty(TextLabel::Property::TEXT, emptyText);
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  expectedWidth  = 300.0f;
+  expectedHeight = 0.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, expectedWidth, std::numeric_limits<float>::infinity());
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  expectedWidth  = 0.0f;
+  expectedHeight = 0.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextLabelAsyncTextMultiline(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelAsyncTextMultiline");
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+  // Set the dpi of AsyncTextLoader and FontClient to be identical.
+  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+  fontClient.SetDpi(0u, 0u);
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK(label);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.0f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 20);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(TextLabel::Property::ELLIPSIS, true);
+
+  std::string longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Vestibulum volutpat pretium libero. Vivamus at augue. In hac habitasse platea dictumst. Pellentesque eu metus. Etiam vitae tortor. Morbi vestibulum volutpat enim. Fusce vel dui. Sed vulputate odio vel purus. Aliquam at lorem. \U0001F31F";
+  label.SetProperty(TextLabel::Property::TEXT, longText);
+
+  application.GetScene().Add(label);
+
+  // Connect to the async text rendered signal.
+  ConnectionTracker* testTracker = new ConnectionTracker();
+  DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+  bool asyncTextRendered = false;
+  label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  float expectedWidth  = 300.0f;
+  float expectedHeight = 300.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedSize(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  expectedWidth  = 300.0f;
+  expectedHeight = 300.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithFixedWidth(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  asyncTextRendered        = false;
+  gAsyncTextRenderedCalled = false;
+  gAsyncTextRenderedWidth  = 0.0f;
+  gAsyncTextRenderedHeight = 0.0f;
+
+  expectedWidth  = 300.0f;
+  expectedHeight = 300.0f;
+
+  // Request render.
+  DevelTextLabel::RequestAsyncRenderWithConstraint(label, expectedWidth, expectedHeight);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+  DALI_TEST_CHECK(asyncTextRendered);
+
+  DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, label.GetProperty<bool>(DevelTextLabel::Property::MANUAL_RENDERED), TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index aed1457..2784930 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -84,6 +84,10 @@ const char* const PROPERTY_NAME_REMOVE_FRONT_INSET    = "removeFrontInset";
 const char* const PROPERTY_NAME_REMOVE_BACK_INSET     = "removeBackInset";
 const char* const PROPERTY_NAME_REMOVE_CUTOUT         = "cutout";
 
+const char* const PROPERTY_NAME_RENDER_MODE           = "renderMode";
+const char* const PROPERTY_NAME_MANUAL_RENDERED       = "manualRendered";
+const char* const PROPERTY_NAME_ASYNC_LINE_COUNT      = "asyncLineCount";
+
 const std::string  DEFAULT_FONT_DIR("/resources/fonts");
 const unsigned int EMOJI_FONT_SIZE = 3840u; // 60 * 64
 
@@ -367,6 +371,9 @@ int UtcDaliToolkitTextLabelGetPropertyP(void)
   DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_REMOVE_FRONT_INSET) == DevelTextLabel::Property::REMOVE_FRONT_INSET);
   DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_REMOVE_BACK_INSET) == DevelTextLabel::Property::REMOVE_BACK_INSET);
   DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_REMOVE_CUTOUT) == DevelTextLabel::Property::CUTOUT);
+  DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_RENDER_MODE) == DevelTextLabel::Property::RENDER_MODE);
+  DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_MANUAL_RENDERED) == DevelTextLabel::Property::MANUAL_RENDERED);
+  DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_ASYNC_LINE_COUNT) == DevelTextLabel::Property::ASYNC_LINE_COUNT);
 
   END_TEST;
 }
@@ -1032,13 +1039,13 @@ int UtcDaliToolkitTextLabelSetPropertyP(void)
   application.Render();
 
   // Check Remove Front/Back Inset Property
-  DALI_TEST_CHECK(label.GetProperty<bool>(DevelTextLabel::Property::REMOVE_FRONT_INSET));
-  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, false);
   DALI_TEST_CHECK(!label.GetProperty<bool>(DevelTextLabel::Property::REMOVE_FRONT_INSET));
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  DALI_TEST_CHECK(label.GetProperty<bool>(DevelTextLabel::Property::REMOVE_FRONT_INSET));
 
-  DALI_TEST_CHECK(label.GetProperty<bool>(DevelTextLabel::Property::REMOVE_BACK_INSET));
-  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, false);
   DALI_TEST_CHECK(!label.GetProperty<bool>(DevelTextLabel::Property::REMOVE_BACK_INSET));
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
+  DALI_TEST_CHECK(label.GetProperty<bool>(DevelTextLabel::Property::REMOVE_BACK_INSET));
 
   application.SendNotification();
   application.Render();
@@ -1051,6 +1058,23 @@ int UtcDaliToolkitTextLabelSetPropertyP(void)
   application.SendNotification();
   application.Render();
 
+  // Check render mode property
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::SYNC);
+  DALI_TEST_EQUALS(label.GetProperty<int>(DevelTextLabel::Property::RENDER_MODE), static_cast<int>(DevelTextLabel::Render::SYNC), TEST_LOCATION);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+  DALI_TEST_EQUALS(label.GetProperty<int>(DevelTextLabel::Property::RENDER_MODE), static_cast<int>(DevelTextLabel::Render::ASYNC_AUTO), TEST_LOCATION);
+
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_MANUAL);
+  DALI_TEST_EQUALS(label.GetProperty<int>(DevelTextLabel::Property::RENDER_MODE), static_cast<int>(DevelTextLabel::Render::ASYNC_MANUAL), TEST_LOCATION);
+
+  // Invalid index
+  label.SetProperty(DevelTextLabel::Property::RENDER_MODE, 3);
+  DALI_TEST_EQUALS(label.GetProperty<int>(DevelTextLabel::Property::RENDER_MODE), static_cast<int>(DevelTextLabel::Render::SYNC), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
   END_TEST;
 }
 
@@ -1708,6 +1732,8 @@ int UtcDaliToolkitTextlabelTextWrapMode(void)
 
   TextLabel label = TextLabel::New();
   label.SetProperty(Actor::Property::SIZE, Vector2(300.0f, 300.f));
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Hello world Hello world");
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
 
@@ -2043,6 +2069,8 @@ int UtcDaliToolkitTextlabelTextFit(void)
   TextLabel label = TextLabel::New();
   Vector2   size(460.0f, 100.0f);
   label.SetProperty(Actor::Property::SIZE, size);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Hello world");
 
   // connect to the text git changed signal.
@@ -2099,6 +2127,8 @@ int UtcDaliToolkitTextlabelTextFitStressTest(void)
   TextLabel label = TextLabel::New();
   Vector2   size(460.0f, 100.0f);
   label.SetProperty(Actor::Property::SIZE, size);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "Hello world");
 
   // connect to the text git changed signal.
@@ -2224,6 +2254,8 @@ int UtcDaliToolkitTextlabelTextFitArray(void)
   TextLabel label = TextLabel::New();
   Vector2   size(300.0f, 80.0f);
   label.SetProperty(Actor::Property::SIZE, size);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "A Quick Brown Fox Jumps Over The Lazy Dog");
   label.SetProperty(DevelTextLabel::Property::MIN_LINE_SIZE, 80.f);
   label.SetProperty(TextLabel::Property::POINT_SIZE, 10.f);
@@ -2746,6 +2778,8 @@ int UtcDaliTextLabelHyphenWrapMode(void)
   label.SetProperty(Actor::Property::SIZE, Vector2(150.0f, 300.f));
   label.SetProperty(TextLabel::Property::POINT_SIZE, 12.f);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   application.GetScene().Add(label);
   application.SendNotification();
   application.Render();
@@ -2841,6 +2875,8 @@ int utcDaliTextLabelGeometryRTL(void)
   label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
   label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
   label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "line1 \nline2\nline 3\nالاخيرالسطر");
   // Avoid a crash when core load gl resources.
   application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
@@ -2939,6 +2975,8 @@ int utcDaliTextLabelGeometryOneGlyph(void)
   label.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
   label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
   label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_FRONT_INSET, true);
+  label.SetProperty(DevelTextLabel::Property::REMOVE_BACK_INSET, true);
   label.SetProperty(TextLabel::Property::TEXT, "H");
 
   // Avoid a crash when core load gl resources.
@@ -3381,8 +3419,9 @@ int utcDaliTextLabelRemoveFrontInset(void)
   application.SendNotification();
   application.Render();
 
-  DevelTextLabel::SetRemoveFrontInset(label, false);
-  DALI_TEST_CHECK(!DevelTextLabel::IsRemoveFrontInset(label));
+  DALI_TEST_CHECK(!DevelTextLabel::IsRemoveFrontInset(label)); // default value is false.
+  DevelTextLabel::SetRemoveFrontInset(label, true);
+  DALI_TEST_CHECK(DevelTextLabel::IsRemoveFrontInset(label));
 
   END_TEST;
 }
@@ -3399,8 +3438,9 @@ int utcDaliTextLabelRemoveBackInset(void)
   application.SendNotification();
   application.Render();
 
-  DevelTextLabel::SetRemoveBackInset(label, false);
-  DALI_TEST_CHECK(!DevelTextLabel::IsRemoveBackInset(label));
+  DALI_TEST_CHECK(!DevelTextLabel::IsRemoveBackInset(label)); // default value is false.
+  DevelTextLabel::SetRemoveBackInset(label, true);
+  DALI_TEST_CHECK(DevelTextLabel::IsRemoveBackInset(label));
 
   END_TEST;
 }
\ No newline at end of file
index e2659ff..c407198 100644 (file)
@@ -555,6 +555,7 @@ int UtcDaliVisualGetPropertyMap1(void)
   propertyMap.Insert(DevelVisual::Property::BORDERLINE_COLOR, Color::RED);
   propertyMap.Insert(DevelVisual::Property::BORDERLINE_OFFSET, -1.0f);
   propertyMap.Insert(DevelColorVisual::Property::BLUR_RADIUS, 20.0f);
+  propertyMap.Insert(DevelColorVisual::Property::CUTOUT_POLICY, DevelColorVisual::CutoutPolicy::CUTOUT_VIEW_WITH_CORNER_RADIUS);
   Visual::Base colorVisual = factory.CreateVisual(propertyMap);
 
   Property::Map resultMap;
@@ -592,6 +593,10 @@ int UtcDaliVisualGetPropertyMap1(void)
   DALI_TEST_CHECK(blurRadiusValue);
   DALI_TEST_CHECK(blurRadiusValue->Get<float>() == 20.0f);
 
+  Property::Value* cutoutPolicyValue = resultMap.Find(DevelColorVisual::Property::CUTOUT_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cutoutPolicyValue);
+  DALI_TEST_CHECK(cutoutPolicyValue->Get<int>() == DevelColorVisual::CutoutPolicy::CUTOUT_VIEW_WITH_CORNER_RADIUS);
+
   // change the blend color
   propertyMap[ColorVisual::Property::MIX_COLOR] = Color::CYAN;
   colorVisual                                   = factory.CreateVisual(propertyMap);
@@ -611,6 +616,35 @@ int UtcDaliVisualGetPropertyMap1(void)
   DALI_TEST_CHECK(blurRadiusValue);
   DALI_TEST_CHECK(blurRadiusValue->Get<float>() == 0.0f);
 
+  // Test wrong values 2
+  propertyMap[DevelColorVisual::Property::CUTOUT_POLICY] = Vector2(2.0f, 3.0f);
+
+  colorVisual = factory.CreateVisual(propertyMap);
+  colorVisual.CreatePropertyMap(resultMap);
+
+  cutoutPolicyValue = resultMap.Find(DevelColorVisual::Property::CUTOUT_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cutoutPolicyValue);
+  DALI_TEST_CHECK(cutoutPolicyValue->Get<int>() == DevelColorVisual::CutoutPolicy::NONE);
+
+  // Test property set by string
+  propertyMap[DevelColorVisual::Property::CUTOUT_POLICY] = "CUTOUT_VIEW";
+
+  colorVisual = factory.CreateVisual(propertyMap);
+  colorVisual.CreatePropertyMap(resultMap);
+
+  cutoutPolicyValue = resultMap.Find(DevelColorVisual::Property::CUTOUT_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cutoutPolicyValue);
+  DALI_TEST_CHECK(cutoutPolicyValue->Get<int>() == DevelColorVisual::CutoutPolicy::CUTOUT_VIEW);
+
+  propertyMap[DevelColorVisual::Property::CUTOUT_POLICY] = "CUTOUT_VIEW_WITH_CORNER_RADIUS";
+
+  colorVisual = factory.CreateVisual(propertyMap);
+  colorVisual.CreatePropertyMap(resultMap);
+
+  cutoutPolicyValue = resultMap.Find(DevelColorVisual::Property::CUTOUT_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cutoutPolicyValue);
+  DALI_TEST_CHECK(cutoutPolicyValue->Get<int>() == DevelColorVisual::CutoutPolicy::CUTOUT_VIEW_WITH_CORNER_RADIUS);
+
   END_TEST;
 }
 
@@ -1140,14 +1174,6 @@ int UtcDaliVisualGetPropertyMap9(void)
   ToolkitTestApplication application;
   tet_infoline("UtcDaliVisualGetPropertyMap9: PrimitiveVisual");
 
-  static std::vector<UniformData> customUniforms =
-    {
-      UniformData("mixColor", Property::Type::VECTOR3),
-    };
-
-  TestGraphicsController& graphics = application.GetGraphicsController();
-  graphics.AddCustomUniforms(customUniforms);
-
   Vector4 color      = Vector4(1.0, 0.8, 0.6, 1.0);
   Vector3 dimensions = Vector3(1.0, 2.0, 3.0);
 
@@ -1234,7 +1260,7 @@ int UtcDaliVisualGetPropertyMap9(void)
   application.GetScene().Add(actor);
 
   Animation animation = Animation::New(1.0f);
-  animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, PrimitiveVisual::Property::MIX_COLOR), Vector3(Color::MAGENTA));
+  animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, PrimitiveVisual::Property::MIX_COLOR), Color::MAGENTA);
   animation.Play();
   application.SendNotification();
   application.Render(0);
@@ -1242,7 +1268,7 @@ int UtcDaliVisualGetPropertyMap9(void)
   application.SendNotification();
 
   auto& gl = application.GetGlAbstraction();
-  DALI_TEST_EQUALS(gl.CheckUniformValue<Vector3>("mixColor", Vector3(Color::MAGENTA)), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gl.CheckUniformValue<Vector4>("uColor", Color::MAGENTA), true, TEST_LOCATION);
 
   tet_infoline("Check property map after animation");
 
@@ -1251,7 +1277,7 @@ int UtcDaliVisualGetPropertyMap9(void)
   DALI_TEST_CHECK(value);
   color = value->Get<Vector4>();
   // Ignore alpha part
-  DALI_TEST_EQUALS(Vector3(color), Vector3(Color::MAGENTA), 0.001f, TEST_LOCATION);
+  DALI_TEST_EQUALS(color, Color::MAGENTA, 0.001f, TEST_LOCATION);
 
   END_TEST;
 }
@@ -1925,7 +1951,6 @@ int UtcDaliVisualAnimateBorderVisual01(void)
   static std::vector<UniformData> customUniforms =
     {
       UniformData("borderColor", Property::Type::VECTOR4),
-      UniformData("mixColor", Property::Type::VECTOR3),
     };
 
   TestGraphicsController& graphics = application.GetGraphicsController();
@@ -1960,7 +1985,7 @@ int UtcDaliVisualAnimateBorderVisual01(void)
 
   Renderer        renderer         = actor.GetRendererAt(0);
   Property::Index borderColorIndex = renderer.GetPropertyIndex(BorderVisual::Property::COLOR);
-  Property::Index mixColorIndex    = VisualRenderer::Property::VISUAL_MIX_COLOR; //renderer.GetPropertyIndex( Visual::Property::MIX_COLOR );
+  Property::Index mixColorIndex    = Renderer::Property::MIX_COLOR;
 
   Animation animation = dummyImpl.CreateTransition(transition);
 
@@ -1978,14 +2003,10 @@ int UtcDaliVisualAnimateBorderVisual01(void)
   DALI_TEST_EQUALS(color, testColor, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("borderColor", testColor), true, TEST_LOCATION);
 
-  color     = renderer.GetCurrentProperty<Vector3>(mixColorIndex);
+  color     = renderer.GetCurrentProperty<Vector4>(mixColorIndex);
   testColor = Vector4(1, 1, 1, 0.45f);
-  DALI_TEST_EQUALS(Vector3(color), Vector3(testColor), 0.0001f, TEST_LOCATION);
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(testColor)), true, TEST_LOCATION);
-
-  Vector4 uColor;
-  DALI_TEST_CHECK(application.GetGlAbstraction().GetUniformValue<Vector4>("uColor", uColor));
-  DALI_TEST_EQUALS(uColor.a, testColor.a, TEST_LOCATION);
+  DALI_TEST_EQUALS(color, testColor, 0.0001f, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", testColor), true, TEST_LOCATION);
 
   application.Render(2000u);
 
@@ -1993,13 +2014,10 @@ int UtcDaliVisualAnimateBorderVisual01(void)
   DALI_TEST_EQUALS(color, Color::WHITE, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("borderColor", Color::WHITE), true, TEST_LOCATION);
 
-  color     = renderer.GetCurrentProperty<Vector3>(mixColorIndex);
+  color     = renderer.GetCurrentProperty<Vector4>(mixColorIndex);
   testColor = Vector4(1, 1, 1, 0.1);
-  DALI_TEST_EQUALS(Vector3(color), Vector3(testColor), TEST_LOCATION);
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(testColor)), true, TEST_LOCATION);
-
-  DALI_TEST_CHECK(application.GetGlAbstraction().GetUniformValue<Vector4>("uColor", uColor));
-  DALI_TEST_EQUALS(uColor.a, testColor.a, TEST_LOCATION);
+  DALI_TEST_EQUALS(color, testColor, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", testColor), true, TEST_LOCATION);
 
   END_TEST;
 }
@@ -2062,14 +2080,6 @@ int UtcDaliVisualAnimateColorVisual(void)
   ToolkitTestApplication application;
   tet_infoline("UtcDaliAnimateColorVisual mixColor");
 
-  static std::vector<UniformData> customUniforms =
-    {
-      UniformData("mixColor", Property::Type::VECTOR3),
-    };
-
-  TestGraphicsController& graphics = application.GetGraphicsController();
-  graphics.AddCustomUniforms(customUniforms);
-
   VisualFactory factory = VisualFactory::Get();
   Property::Map propertyMap;
   propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
@@ -2086,31 +2096,31 @@ int UtcDaliVisualAnimateColorVisual(void)
   DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
 
   Renderer        renderer      = actor.GetRendererAt(0);
-  Property::Index mixColorIndex = VisualRenderer::Property::VISUAL_MIX_COLOR; //renderer.GetPropertyIndex( ColorVisual::Property::MIX_COLOR );
+  Property::Index mixColorIndex = Renderer::Property::MIX_COLOR;
 
   Property::Value blendModeValue = renderer.GetProperty(Renderer::Property::BLEND_MODE);
   DALI_TEST_EQUALS(blendModeValue.Get<int>(), (int)BlendMode::AUTO, TEST_LOCATION);
 
   Animation animation = Animation::New(4.0f);
-  animation.AnimateTo(Property(renderer, mixColorIndex), Vector3(Color::WHITE));
+  animation.AnimateTo(Property(renderer, mixColorIndex), Color::WHITE);
   animation.Play();
 
   application.SendNotification();
   application.Render(0);
   application.Render(2000u); // halfway point
 
-  Vector3 color     = renderer.GetCurrentProperty<Vector3>(mixColorIndex);
-  Vector3 testColor = Vector3(Color::BLUE + Color::WHITE) * 0.5f;
+  Vector4 color     = renderer.GetCurrentProperty<Vector4>(mixColorIndex);
+  Vector4 testColor = (Color::BLUE + Color::WHITE) * 0.5f;
   DALI_TEST_EQUALS(color, testColor, TEST_LOCATION);
 
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", testColor), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", testColor), true, TEST_LOCATION);
 
   application.Render(2000u); // halfway point between blue and white
 
-  color = renderer.GetCurrentProperty<Vector3>(mixColorIndex);
-  DALI_TEST_EQUALS(color, Vector3(Color::WHITE), TEST_LOCATION);
+  color = renderer.GetCurrentProperty<Vector4>(mixColorIndex);
+  DALI_TEST_EQUALS(color, Color::WHITE, TEST_LOCATION);
 
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(Color::WHITE)), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Color::WHITE), true, TEST_LOCATION);
 
   blendModeValue = renderer.GetCurrentProperty(Renderer::Property::BLEND_MODE);
   DALI_TEST_EQUALS(blendModeValue.Get<int>(), (int)BlendMode::AUTO, TEST_LOCATION);
@@ -2123,14 +2133,6 @@ int UtcDaliVisualAnimatePrimitiveVisual(void)
   ToolkitTestApplication application;
   tet_infoline("UtcDaliAnimatePrimitiveVisual color");
 
-  static std::vector<UniformData> customUniforms =
-    {
-      UniformData("mixColor", Property::Type::VECTOR3),
-    };
-
-  TestGraphicsController& graphics = application.GetGraphicsController();
-  graphics.AddCustomUniforms(customUniforms);
-
   {
     VisualFactory factory = VisualFactory::Get();
     Property::Map propertyMap;
@@ -2181,8 +2183,7 @@ int UtcDaliVisualAnimatePrimitiveVisual(void)
     application.SendNotification();
 
     Vector4 halfwayColor = (INITIAL_MIX_COLOR + TARGET_MIX_COLOR) * 0.5;
-    DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(0.5f, 0.5f, 0.5f, halfwayColor.a)), true, TEST_LOCATION);
-    DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(halfwayColor)), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(0.5f, 0.5f, 0.5f, 1.0f) * halfwayColor), true, TEST_LOCATION);
 
     DALI_TEST_CHECK(glEnableStack.FindMethodAndParams("Enable", blendStr.str()));
 
@@ -2192,8 +2193,7 @@ int UtcDaliVisualAnimatePrimitiveVisual(void)
     application.SendNotification(); // Trigger signals
 
     DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), Color::WHITE, TEST_LOCATION);
-    DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, TARGET_MIX_COLOR.a)), true, TEST_LOCATION);
-    DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(TARGET_MIX_COLOR)), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, 1.0f) * TARGET_MIX_COLOR), true, TEST_LOCATION);
 
     DALI_TEST_CHECK(glEnableStack.FindMethodAndParams("Disable", blendStr.str()));
 
@@ -4790,7 +4790,6 @@ int UtcDaliVisualBorderlineColorAnimateTest(void)
   TestGraphicsController&         graphics = application.GetGraphicsController();
   static std::vector<UniformData> customUniforms =
     {
-      UniformData("mixColor", Property::Type::VECTOR3),
       UniformData("cornerRadius", Property::Type::VECTOR4),
       UniformData("cornerRadiusPolicy", Property::Type::FLOAT),
       UniformData("borderlineWidth", Property::Type::FLOAT),
@@ -4800,12 +4799,10 @@ int UtcDaliVisualBorderlineColorAnimateTest(void)
   graphics.AddCustomUniforms(customUniforms);
 
   {
-    const Vector3 INITIAL_MIX_COLOR(1.0f, 0.0f, 1.0f);
-    const float   INITIAL_MIX_OPACITY(0.5f);
+    const Vector4 INITIAL_MIX_COLOR(1.0f, 0.0f, 1.0f, 0.5f);
     const Vector4 INITIAL_BORDERLINE_COLOR(0.0f, 1.0f, 0.0f, 1.0f);
     const float   INITIAL_ACTOR_OPACITY(1.0f);
-    const Vector3 TARGET_MIX_COLOR(1.0f, 0.0f, 0.0f);
-    const float   TARGET_MIX_OPACITY(0.8f);
+    const Vector4 TARGET_MIX_COLOR(1.0f, 0.0f, 0.0f, 0.8f);
     const Vector4 TARGET_BORDERLINE_COLOR(1.0f, 0.0f, 1.0f, 0.2f);
     const float   TARGET_ACTOR_OPACITY(0.5f);
 
@@ -4813,7 +4810,6 @@ int UtcDaliVisualBorderlineColorAnimateTest(void)
     Property::Map propertyMap;
     propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
     propertyMap.Insert(Visual::Property::MIX_COLOR, INITIAL_MIX_COLOR);
-    propertyMap.Insert(Visual::Property::OPACITY, INITIAL_MIX_OPACITY);
     propertyMap.Insert(DevelVisual::Property::BORDERLINE_WIDTH, 1.0f);
     propertyMap.Insert(DevelVisual::Property::BORDERLINE_COLOR, INITIAL_BORDERLINE_COLOR);
     Visual::Base visual = factory.CreateVisual(propertyMap);
@@ -4830,7 +4826,6 @@ int UtcDaliVisualBorderlineColorAnimateTest(void)
 
     Animation animation = Animation::New(4.0f);
     animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, Visual::Property::MIX_COLOR), TARGET_MIX_COLOR);
-    animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), TARGET_MIX_OPACITY);
     animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::BORDERLINE_COLOR), TARGET_BORDERLINE_COLOR);
     animation.AnimateTo(Property(actor, Actor::Property::OPACITY), TARGET_ACTOR_OPACITY);
     animation.Play();
@@ -4842,13 +4837,10 @@ int UtcDaliVisualBorderlineColorAnimateTest(void)
     application.Render(2000u); // halfway point
     application.SendNotification();
 
-    Vector3 halfwayMixColor        = (INITIAL_MIX_COLOR + TARGET_MIX_COLOR) * 0.5f;
-    float   halfwayMixOpacity      = (INITIAL_MIX_OPACITY + TARGET_MIX_OPACITY) * 0.5f;
+    Vector4 halfwayMixColor        = (INITIAL_MIX_COLOR + TARGET_MIX_COLOR) * 0.5f;
     Vector4 halfwayBorderlineColor = (INITIAL_BORDERLINE_COLOR + TARGET_BORDERLINE_COLOR) * 0.5f;
     float   halfwayActorOpacity    = (INITIAL_ACTOR_OPACITY + TARGET_ACTOR_OPACITY) * 0.5f;
-    halfwayMixOpacity *= halfwayActorOpacity;
-    DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector3>("mixColor", halfwayMixColor), true, TEST_LOCATION);
-    DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, halfwayMixOpacity)), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, halfwayActorOpacity) * halfwayMixColor), true, TEST_LOCATION);
     DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("uActorColor", Vector4(1.0f, 1.0f, 1.0f, halfwayActorOpacity)), true, TEST_LOCATION);
     DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("borderlineColor", halfwayBorderlineColor), true, TEST_LOCATION);
 
@@ -4856,8 +4848,7 @@ int UtcDaliVisualBorderlineColorAnimateTest(void)
     application.SendNotification(); // Trigger signals
 
     DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector4>(Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, TARGET_ACTOR_OPACITY), TEST_LOCATION);
-    DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector3>("mixColor", TARGET_MIX_COLOR), true, TEST_LOCATION);
-    DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, TARGET_MIX_OPACITY * TARGET_ACTOR_OPACITY)), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("uColor", Vector4(1.0f, 1.0f, 1.0f, TARGET_ACTOR_OPACITY) * TARGET_MIX_COLOR), true, TEST_LOCATION);
     DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("uActorColor", Vector4(1.0f, 1.0f, 1.0f, TARGET_ACTOR_OPACITY)), true, TEST_LOCATION);
     DALI_TEST_EQUALS(glAbstraction.CheckUniformValue<Vector4>("borderlineColor", TARGET_BORDERLINE_COLOR), true, TEST_LOCATION);
 
@@ -5053,7 +5044,6 @@ int UtcDaliVisualGetVisualProperty01(void)
 
   static std::vector<UniformData> customUniforms =
     {
-      UniformData("mixColor", Property::Type::VECTOR3),
       UniformData("offset", Property::Type::VECTOR2),
       UniformData("size", Property::Type::VECTOR2),
       UniformData("cornerRadius", Property::Type::VECTOR4),
@@ -5086,10 +5076,9 @@ int UtcDaliVisualGetVisualProperty01(void)
   application.SendNotification();
   application.Render();
 
-  Vector3 targetColor(1.0f, 1.0f, 1.0f);
+  Vector4 targetColor(1.0f, 1.0f, 1.0f, 0.5f);
   Vector2 targetOffset(0.05f, 0.05f);
   Vector2 targetSize(1.1f, 1.1f);
-  float   targetOpacity = 0.5f;
   Vector4 targetCornerRadius(0.0f, 0.0f, 0.0f, 0.0f);
   float   targetBlurRadius      = 10.0f;
   float   targetBorderlineWidth = 25.0f;
@@ -5098,7 +5087,6 @@ int UtcDaliVisualGetVisualProperty01(void)
 
   Animation animation = Animation::New(1.0f);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::MIX_COLOR), targetColor);
-  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), targetOpacity);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Transform::Property::OFFSET), targetOffset);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Transform::Property::SIZE), targetSize);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), targetCornerRadius);
@@ -5118,7 +5106,7 @@ int UtcDaliVisualGetVisualProperty01(void)
   // Test property values: they should be updated
   Property::Value* colorValue = resultMap.Find(ColorVisual::Property::MIX_COLOR, Property::VECTOR4);
   DALI_TEST_CHECK(colorValue);
-  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetColor.r, targetColor.g, targetColor.b, targetOpacity), TEST_LOCATION);
+  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), targetColor, TEST_LOCATION);
 
   Property::Value*     transformValue = resultMap.Find(Dali::Toolkit::Visual::Property::TRANSFORM);
   Dali::Property::Map* transformMap   = transformValue->GetMap();
@@ -5153,7 +5141,6 @@ int UtcDaliVisualGetVisualProperty01(void)
   DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), targetBorderlineOffset, TEST_LOCATION);
 
   // Test uniform values
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", targetColor), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("offset", targetOffset), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("size", targetSize), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
@@ -5177,7 +5164,6 @@ int UtcDaliVisualGetVisualProperty02(void)
 
   static std::vector<UniformData> customUniforms =
     {
-      UniformData("mixColor", Property::Type::VECTOR3),
       UniformData("offset", Property::Type::VECTOR2),
       UniformData("size", Property::Type::VECTOR2),
       UniformData("cornerRadius", Property::Type::VECTOR4),
@@ -5204,10 +5190,9 @@ int UtcDaliVisualGetVisualProperty02(void)
   application.SendNotification();
   application.Render();
 
-  Vector3 targetColor(1.0f, 1.0f, 1.0f);
+  Vector4 targetColor(1.0f, 1.0f, 1.0f, 0.5f);
   Vector2 targetOffset(0.05f, 0.05f);
   Vector2 targetSize(1.1f, 1.1f);
-  float   targetOpacity = 0.5f;
   Vector4 targetCornerRadius(20.0f, 0.0f, 20.0f, 0.0f);
   float   targetBorderlineWidth = 77.7f;
   Vector4 targetBorderlineColor(0.4f, 0.2f, 0.3f, 0.9f);
@@ -5217,7 +5202,6 @@ int UtcDaliVisualGetVisualProperty02(void)
   // Should work when the properties are not set before
   Animation animation = Animation::New(1.0f);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "mixColor"), targetColor);
-  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "opacity"), targetOpacity);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "offset"), targetOffset);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "size"), targetSize);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "cornerRadius"), targetCornerRadius);
@@ -5237,7 +5221,7 @@ int UtcDaliVisualGetVisualProperty02(void)
   // Test property values: they should be updated
   Property::Value* colorValue = resultMap.Find(ColorVisual::Property::MIX_COLOR, Property::VECTOR4);
   DALI_TEST_CHECK(colorValue);
-  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetColor.r, targetColor.g, targetColor.b, targetOpacity), TEST_LOCATION);
+  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), targetColor, TEST_LOCATION);
 
   Property::Value*     transformValue = resultMap.Find(Dali::Toolkit::Visual::Property::TRANSFORM);
   Dali::Property::Map* transformMap   = transformValue->GetMap();
@@ -5272,7 +5256,6 @@ int UtcDaliVisualGetVisualProperty02(void)
   DALI_TEST_EQUALS(blurRadiusValue->Get<float>(), targetBlurRadius, TEST_LOCATION);
 
   // Test uniform values
-  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", targetColor), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("offset", targetOffset), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("size", targetSize), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
@@ -6652,4 +6635,87 @@ int UtcDaliVisualUpdatePropertyChangeShader05(void)
   application.GetGlAbstraction().mShaderLanguageVersion = originalShaderVersion;
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliVisualCutoutPolicyChangeShader01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliVisualCutoutPolicyChangeShader01: ColorVisual with cutout policy");
+
+  static std::vector<UniformData> customUniforms =
+    {
+      UniformData("uCutoutWithCornerRadius", Property::Type::INTEGER),
+    };
+
+  TestGraphicsController& graphics = application.GetGraphicsController();
+  graphics.AddCustomUniforms(customUniforms);
+
+  VisualFactory factory = VisualFactory::Get();
+
+  // Test (Enable/Disable) CornerRadius, (Enable/Disable) Borderline, (Enable/Disable) Blur, and 3 kind of CutoutPolicy
+  for(int testCase = 0; testCase < 2 * 2 * 2 * 3; ++testCase)
+  {
+    const bool enableCornerRadius = (testCase & 1);
+    const bool enableBorderline   = (testCase & 2);
+    const bool enableBlur         = (testCase & 4);
+
+    // clang-format off
+    const DevelColorVisual::CutoutPolicy::Type cutoutPolicy = (testCase / 8) == 0 ? DevelColorVisual::CutoutPolicy::NONE :
+                                                              (testCase / 8) == 1 ? DevelColorVisual::CutoutPolicy::CUTOUT_VIEW :
+                                                                                    DevelColorVisual::CutoutPolicy::CUTOUT_VIEW_WITH_CORNER_RADIUS;
+    // clang-format on
+
+    Property::Map propertyMap;
+    propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
+    propertyMap.Insert(Visual::Property::MIX_COLOR, Color::BLUE);
+    if(enableCornerRadius)
+    {
+      propertyMap.Insert(DevelVisual::Property::CORNER_RADIUS, 10.0f);
+      propertyMap.Insert(DevelVisual::Property::CORNER_RADIUS_POLICY, Toolkit::Visual::Transform::Policy::RELATIVE);
+    }
+    if(enableBorderline)
+    {
+      propertyMap.Insert(DevelVisual::Property::BORDERLINE_WIDTH, 20.0f);
+      propertyMap.Insert(DevelVisual::Property::BORDERLINE_COLOR, Color::RED);
+      propertyMap.Insert(DevelVisual::Property::BORDERLINE_OFFSET, -1.0f);
+    }
+    if(enableBlur)
+    {
+      propertyMap.Insert(DevelColorVisual::Property::BLUR_RADIUS, 20.0f);
+    }
+    propertyMap.Insert(DevelColorVisual::Property::CUTOUT_POLICY, cutoutPolicy);
+
+    Visual::Base colorVisual = factory.CreateVisual(propertyMap);
+
+    DummyControl        dummyControl = DummyControl::New(true);
+    Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+    dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, colorVisual);
+    dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+    application.GetScene().Add(dummyControl);
+
+    application.SendNotification();
+    application.Render();
+
+    TestShaderCodeContainSubstrings(
+      dummyControl,
+      {
+        {"#define IS_REQUIRED_BLUR", enableBlur},
+        {"#define IS_REQUIRED_BORDERLINE", !enableBlur && enableBorderline}, ///< Since borderline is ignored, due to blur enabled.
+        {"#define IS_REQUIRED_ROUNDED_CORNER", enableCornerRadius},
+        {"#define IS_REQUIRED_CUTOUT", cutoutPolicy != DevelColorVisual::CutoutPolicy::NONE},
+      },
+      TEST_LOCATION);
+
+    if(cutoutPolicy != DevelColorVisual::CutoutPolicy::NONE)
+    {
+      auto& gl = application.GetGlAbstraction();
+      DALI_TEST_EQUALS(gl.CheckUniformValue<int>("uCutoutWithCornerRadius", cutoutPolicy == DevelColorVisual::CutoutPolicy::CUTOUT_VIEW_WITH_CORNER_RADIUS ? 1 : 0), true, TEST_LOCATION);
+    }
+    dummyControl.Unparent();
+
+    application.SendNotification();
+    application.Render();
+  }
+
+  END_TEST;
+}
index 726a7b8..2bbc07e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali-toolkit/devel-api/utility/npatch-utilities.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
-#include <dali-toolkit/internal/visuals/npatch-loader.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-loader.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
-#include <dali/integration-api/debug.h>
 #include <dali/integration-api/adaptor-framework/shader-precompiler.h>
+#include <dali/integration-api/debug.h>
 
 using namespace Dali;
 using namespace Dali::Toolkit;
@@ -54,6 +54,15 @@ const char* TEST_GIF_FILE_NAME = TEST_RESOURCE_DIR "/anim.gif";
 // resolution: 34*34, pixel format: RGBA8888
 static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
 
+// custom shader
+static const char* VertexSource =
+  "This is a custom vertex shader\n"
+  "made on purpose to look nothing like a normal vertex shader inside dali\n";
+
+static const char* FragmentSource =
+  "This is a custom fragment shader\n"
+  "made on purpose to look nothing like a normal fragment shader inside dali\n";
+
 Property::Map DefaultTransform()
 {
   Property::Map transformMap;
@@ -188,14 +197,6 @@ int UtcDaliVisualFactoryGetColorVisual1(void)
   ToolkitTestApplication application;
   tet_infoline("UtcDaliVisualFactoryGetColorVisual1:  Request color visual with a Property::Map");
 
-  static std::vector<UniformData> customUniforms =
-    {
-      UniformData("mixColor", Property::Type::VECTOR3),
-    };
-
-  TestGraphicsController& graphics = application.GetGraphicsController();
-  graphics.AddCustomUniforms(customUniforms);
-
   VisualFactory factory = VisualFactory::Get();
   DALI_TEST_CHECK(factory);
 
@@ -210,13 +211,10 @@ int UtcDaliVisualFactoryGetColorVisual1(void)
   DummyControl actor = DummyControl::New(true);
   TestVisualRender(application, actor, visual);
 
-  Vector3            actualValue(Vector4::ZERO);
   Vector4            actualColor(Vector4::ZERO);
   TestGlAbstraction& gl = application.GetGlAbstraction();
-  DALI_TEST_CHECK(gl.GetUniformValue<Vector3>("mixColor", actualValue));
   DALI_TEST_CHECK(gl.GetUniformValue<Vector4>("uColor", actualColor));
-  DALI_TEST_EQUALS(actualValue, Vector3(testColor), TEST_LOCATION);
-  DALI_TEST_EQUALS(actualColor.a, testColor.a, TEST_LOCATION);
+  DALI_TEST_EQUALS(actualColor, testColor, TEST_LOCATION);
 
   END_TEST;
 }
@@ -226,14 +224,6 @@ int UtcDaliVisualFactoryGetColorVisual2(void)
   ToolkitTestApplication application;
   tet_infoline("UtcDaliVisualFactoryGetColorVisual2: Request color visual with a Vector4");
 
-  static std::vector<UniformData> customUniforms =
-    {
-      UniformData("mixColor", Property::Type::VECTOR3),
-    };
-
-  TestGraphicsController& graphics = application.GetGraphicsController();
-  graphics.AddCustomUniforms(customUniforms);
-
   VisualFactory factory = VisualFactory::Get();
   DALI_TEST_CHECK(factory);
 
@@ -247,13 +237,10 @@ int UtcDaliVisualFactoryGetColorVisual2(void)
   DummyControl actor = DummyControl::New(true);
   TestVisualRender(application, actor, visual);
 
-  Vector3            actualValue;
   Vector4            actualColor;
   TestGlAbstraction& gl = application.GetGlAbstraction();
-  DALI_TEST_CHECK(gl.GetUniformValue<Vector3>("mixColor", actualValue));
   DALI_TEST_CHECK(gl.GetUniformValue<Vector4>("uColor", actualColor));
-  DALI_TEST_EQUALS(actualValue, Vector3(testColor), TEST_LOCATION);
-  DALI_TEST_EQUALS(actualColor.a, testColor.a, TEST_LOCATION);
+  DALI_TEST_EQUALS(actualColor, testColor, TEST_LOCATION);
 
   application.GetScene().Remove(actor);
   DALI_TEST_CHECK(actor.GetRendererCount() == 0u);
@@ -1625,6 +1612,8 @@ int UtcDaliVisualFactoryGetSvgVisualAtlas(void)
   END_TEST;
 }
 
+namespace
+{
 //Creates a mesh visual from the given propertyMap and tries to load it on stage in the given application.
 //This is expected to succeed, which will then pass the test.
 void MeshVisualLoadsCorrectlyTest(Property::Map& propertyMap, ToolkitTestApplication& application)
@@ -1706,6 +1695,7 @@ void MeshVisualDoesNotLoadCorrectlyTest(Property::Map& propertyMap, ToolkitTestA
   actor.Unparent();
   DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
 }
+} // namespace
 
 //Test if mesh loads correctly when supplied with only the bare minimum requirements, an object file.
 int UtcDaliVisualFactoryGetMeshVisual1(void)
@@ -2790,12 +2780,107 @@ int UtcDaliVisualFactoryGetAnimatedImageVisual2(void)
   END_TEST;
 }
 
+int UtcDaliVisualFactoryGetAnimatedImageVisualWithOption(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliVisualFactoryGetAnimatedImageVisualWithOption: Request animated image with option that we will use regular image visual even if gif url");
+
+  DummyControl actor = DummyControl::New(true);
+
+  Property::Map    map;
+  Property::Value* valuePtr = nullptr;
+
+  VisualFactory factory = VisualFactory::Get();
+  Visual::Base  visual;
+
+  tet_printf("Test CreateVisual(url, size) with options\n");
+  visual = factory.CreateVisual(TEST_GIF_FILE_NAME, ImageDimensions(), VisualFactory::CreationOptions::IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY);
+  DALI_TEST_CHECK(visual);
+
+  TestVisualAsynchronousRender(application, actor, visual);
+
+  visual.CreatePropertyMap(map);
+  valuePtr = map.Find(Dali::Toolkit::Visual::Property::TYPE);
+  DALI_TEST_CHECK(valuePtr);
+  DALI_TEST_CHECK(valuePtr->Get<int>() == Dali::Toolkit::Visual::Type::IMAGE);
+
+  tet_printf("Test CreateVisual(propertyMap) with options\n");
+  map.Clear();
+  map.Add(Toolkit::Visual::Property::TYPE, Visual::IMAGE)
+    .Add(ImageVisual::Property::URL, TEST_GIF_FILE_NAME);
+
+  // Change actor and visual as new reference.
+  actor  = DummyControl::New(true);
+  visual = factory.CreateVisual(map, VisualFactory::CreationOptions::IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY);
+
+  // We will use cached image.
+  TestVisualRender(application, actor, visual);
+
+  visual.CreatePropertyMap(map);
+  valuePtr = map.Find(Dali::Toolkit::Visual::Property::TYPE);
+  DALI_TEST_CHECK(valuePtr);
+  DALI_TEST_CHECK(valuePtr->Get<int>() == Dali::Toolkit::Visual::Type::IMAGE);
+
+  END_TEST;
+}
 
-int UtcDaliVisualFactoryGetPreCompiler(void)
+int UtcDaliVisualFactorySetGetDefaultCreationOptions(void)
 {
   ToolkitTestApplication application;
-  tet_infoline("UtcDaliVisualFactoryGetAnimatedImageVisual2: Request animated image visual with a Property::Map, test custom wrap mode and pixel area");
+  tet_infoline("UtcDaliVisualFactorySetGetDefaultCreationOptions");
+
+  VisualFactory factory = VisualFactory::Get();
+
+  tet_printf("Check default creation options is NONE\n");
+  DALI_TEST_EQUALS(factory.GetDefaultCreationOptions(), VisualFactory::CreationOptions::NONE, TEST_LOCATION);
+  factory.SetDefaultCreationOptions(VisualFactory::CreationOptions::IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY);
+
+  tet_printf("Check default creation options changed\n");
+  DALI_TEST_EQUALS(factory.GetDefaultCreationOptions(), VisualFactory::CreationOptions::IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY, TEST_LOCATION);
+
+  tet_printf("Check default creation options applied\n");
+  DummyControl actor = DummyControl::New(true);
+
+  Property::Map    map;
+  Property::Value* valuePtr = nullptr;
 
+  Visual::Base visual;
+
+  tet_printf("Test CreateVisual(url, size) with options\n");
+  visual = factory.CreateVisual(TEST_GIF_FILE_NAME, ImageDimensions());
+  DALI_TEST_CHECK(visual);
+
+  TestVisualAsynchronousRender(application, actor, visual);
+
+  visual.CreatePropertyMap(map);
+  valuePtr = map.Find(Dali::Toolkit::Visual::Property::TYPE);
+  DALI_TEST_CHECK(valuePtr);
+  DALI_TEST_CHECK(valuePtr->Get<int>() == Dali::Toolkit::Visual::Type::IMAGE);
+
+  tet_printf("Test CreateVisual(propertyMap) with options\n");
+  map.Clear();
+  map.Add(Toolkit::Visual::Property::TYPE, Visual::IMAGE)
+    .Add(ImageVisual::Property::URL, TEST_GIF_FILE_NAME);
+
+  // Change actor and visual as new reference.
+  actor  = DummyControl::New(true);
+  visual = factory.CreateVisual(map);
+
+  // We will use cached image.
+  TestVisualRender(application, actor, visual);
+
+  visual.CreatePropertyMap(map);
+  valuePtr = map.Find(Dali::Toolkit::Visual::Property::TYPE);
+  DALI_TEST_CHECK(valuePtr);
+  DALI_TEST_CHECK(valuePtr->Get<int>() == Dali::Toolkit::Visual::Type::IMAGE);
+
+  END_TEST;
+}
+
+int UtcDaliVisualFactoryUsePreCompiledShader(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliVisualFactoryUsePreCompiledShader: Test a UsePreCompiledShader fucntion");
 
   std::vector<RawShaderData> precompiledShaderList;
   DALI_TEST_CHECK(precompiledShaderList.size() == 0u); // before Get Shader
@@ -2805,6 +2890,83 @@ int UtcDaliVisualFactoryGetPreCompiler(void)
   VisualFactory factory = VisualFactory::Get();
   DALI_TEST_CHECK(factory);
 
+  Property::Map imageShader;
+  imageShader["shaderType"]   = "image";
+  imageShader["shaderOption"] = Property::Map().Add("YUV_AND_RGB", true);
+  imageShader["shaderName"]   = "IMAGE_SHADER_YUV_AND_RGB";
+
+  Property::Map imageShader2;
+  imageShader2["shaderType"]   = "image";
+  imageShader2["shaderOption"] = Property::Map()
+                                   .Add("ROUNDED_CORNER", true)
+                                   .Add("BORDERLINE", true)
+                                   .Add("MASKING", true);
+
+  Property::Map imageShader3;
+  imageShader3["shaderType"]   = "image";
+  imageShader3["shaderOption"] = Property::Map().Add("YUV_TO_RGB", true);
+
+  Property::Map imageShader4;
+  imageShader4["shaderType"]   = "image";
+  imageShader4["shaderOption"] = Property::Map().Add("ATLAS_DEFAULT", true);
+
+  Property::Map imageShader5;
+  imageShader5["shaderType"]   = "image";
+  imageShader5["shaderOption"] = Property::Map().Add("ATLAS_CUSTOM", true);
+
+  Property::Map textShader;
+  textShader["shaderType"]   = "text";
+  textShader["shaderOption"] = Property::Map()
+                                 .Add("MULTI_COLOR", true)
+                                 .Add("OVERLAY", true)
+                                 .Add("STYLES", true);
+
+  Property::Map textShader2;
+  textShader2["shaderType"]   = "text";
+  textShader2["shaderOption"] = Property::Map()
+                                  .Add("EMOJI", true);
+
+  Property::Map colorShader;
+  colorShader["shaderType"]   = "color";
+  colorShader["shaderOption"] = Property::Map()
+                                  .Add("CUTOUT", true)
+                                  .Add("BORDERLINE", true);
+
+  Property::Map colorShader2;
+  colorShader2["shaderType"]   = "color";
+  colorShader2["shaderOption"] = Property::Map()
+                                   .Add("ROUNDED_CORNER,", true)
+                                   .Add("BLUR_EDGE", true);
+
+  Property::Map npatchShader;
+  npatchShader["shaderType"] = "npatch";
+
+  Property::Map npatchShader2;
+  npatchShader2["shaderType"]    = "npatch";
+  npatchShader2["shaderOption"]  = Property::Map().Add("MASKING", true);
+  npatchShader2["xStretchCount"] = 4;
+  npatchShader2["yStretchCount"] = 3;
+
+  Property::Map customSHader;
+  customSHader["shaderType"]     = "custom";
+  customSHader["shaderName"]     = "myShader";
+  customSHader["vertexShader"]   = VertexSource;
+  customSHader["fragmentShader"] = FragmentSource;
+
+  factory.AddPrecompileShader(imageShader);
+  factory.AddPrecompileShader(imageShader); // use same shader, because check line coverage
+  factory.AddPrecompileShader(imageShader2);
+  factory.AddPrecompileShader(imageShader3);
+  factory.AddPrecompileShader(imageShader4);
+  factory.AddPrecompileShader(imageShader5);
+  factory.AddPrecompileShader(textShader);
+  factory.AddPrecompileShader(textShader2);
+  factory.AddPrecompileShader(colorShader);
+  factory.AddPrecompileShader(colorShader2);
+  factory.AddPrecompileShader(npatchShader);
+  factory.AddPrecompileShader(npatchShader2);
+  factory.AddPrecompileShader(customSHader);
+
   factory.UsePreCompiledShader();
 
   ShaderPreCompiler::Get().GetPreCompileShaderList(precompiledShaderList);
index 2ede899..6946a93 100644 (file)
@@ -103,6 +103,10 @@ static std::unique_ptr<Dali::WebEngineContextMenu>                        gConte
 static int                                                                gHitTestCreatedCallbackCalled           = 0;
 static int                                                                gCookieManagerChangsWatchCallbackCalled = 0;
 static int                                                                gPlainTextReceivedCallbackCalled        = 0;
+static int                                                                gNewWindowPolicyDecidedCallbackCalled   = 0;
+static int                                                                gFullscreenEnteredCallbackCalled        = 0;
+static int                                                                gFullscreenExitedCallbackCalled         = 0;
+static int                                                                gTextFoundCallbackCalled                = 0;
 
 struct CallbackFunctor
 {
@@ -325,6 +329,26 @@ static void OnContextMenuHidden(std::unique_ptr<Dali::WebEngineContextMenu> menu
   gContextMenuHiddenInstance = std::move(menu);
 }
 
+static void OnNewWindowPolicyDecided(std::unique_ptr<Dali::WebEnginePolicyDecision> decision)
+{
+  gNewWindowPolicyDecidedCallbackCalled++;
+}
+
+static void OnFullscreenEntered()
+{
+  gFullscreenEnteredCallbackCalled++;
+}
+
+static void OnFullscreenExited()
+{
+  gFullscreenExitedCallbackCalled++;
+}
+
+static void OnTextFound(uint32_t arg)
+{
+  gTextFoundCallbackCalled++;
+}
+
 } // namespace
 
 void web_view_startup(void)
@@ -864,6 +888,8 @@ int UtcDaliWebViewSslCertificateHttpAuthentication(void)
   DALI_TEST_CHECK(gSslCertificateInstance);
   DALI_TEST_EQUALS(gSslCertificateInstance->GetPem(), "abc", TEST_LOCATION);
   DALI_TEST_CHECK(gSslCertificateInstance->IsContextSecure());
+  DALI_TEST_EQUALS(gSslCertificateInstance->GetPolicyDecisionError(), 1, TEST_LOCATION);
+  DALI_TEST_CHECK(gSslCertificateInstance->SuspendPolicyDecision());
 
   // http authentication.
   DALI_TEST_CHECK(gHttpAuthInstance);
@@ -1481,10 +1507,19 @@ int UtcDaliWebViewMethodsForCoverage(void)
   view.AddJavaScriptMessageHandler("jsObject",
                                    [](const std::string& arg) {
                                    });
+
+  view.AddJavaScriptEntireMessageHandler("jsObject2",
+                                   [](const std::string& arg, const std::string& arg2) {
+                                   });
+
   view.SetTtsFocus(true);
 
+  //view.ChangeOrientation(90);
+
   DALI_TEST_CHECK(view);
 
+  //view.ExitFullscreen();
+
   END_TEST;
 }
 
@@ -2413,4 +2448,99 @@ int UtcDaliWebViewVisibilityChange(void)
   }
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliWebViewMethodsForCoverage2(void)
+{
+  ToolkitTestApplication application;
+
+  WebView view = WebView::New();
+  view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  view.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  view.SetProperty(Actor::Property::POSITION, Vector2(0, 0));
+  view.SetProperty(Actor::Property::SIZE, Vector2(800, 600));
+
+  application.GetScene().Add(view);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(view);
+
+  try
+  {
+    // Just call API and exception check.
+    view.ChangeOrientation(90);
+    view.ExitFullscreen();
+    tet_result(TET_PASS);
+  }
+  catch(...)
+  {
+    // Should not throw exception
+    tet_result(TET_FAIL);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliWebViewRegisterNewWindowPolicyDecidedCallback(void)
+{
+  ToolkitTestApplication application;
+
+  WebView view = WebView::New();
+  DALI_TEST_CHECK(view);
+
+  view.RegisterNewWindowPolicyDecidedCallback(&OnNewWindowPolicyDecided);
+  DALI_TEST_EQUALS(gNewWindowPolicyDecidedCallbackCalled, 0, TEST_LOCATION);
+
+  view.LoadUrl(TEST_URL1);
+  Test::EmitGlobalTimerSignal();
+  DALI_TEST_EQUALS(gNewWindowPolicyDecidedCallbackCalled, 1, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliWebViewRegisterFullscreenEnteredCallback(void)
+{
+  ToolkitTestApplication application;
+
+  WebView view = WebView::New();
+  DALI_TEST_CHECK(view);
+
+  view.RegisterFullscreenEnteredCallback(&OnFullscreenEntered);
+  DALI_TEST_EQUALS(gFullscreenEnteredCallbackCalled, 0, TEST_LOCATION);
+
+  view.LoadUrl(TEST_URL1);
+  Test::EmitGlobalTimerSignal();
+  DALI_TEST_EQUALS(gFullscreenEnteredCallbackCalled, 1, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliWebViewRegisterFullscreenExitedCallback(void)
+{
+  ToolkitTestApplication application;
+
+  WebView view = WebView::New();
+  DALI_TEST_CHECK(view);
+
+  view.RegisterFullscreenExitedCallback(&OnFullscreenExited);
+  DALI_TEST_EQUALS(gFullscreenExitedCallbackCalled, 0, TEST_LOCATION);
+
+  view.LoadUrl(TEST_URL1);
+  Test::EmitGlobalTimerSignal();
+  DALI_TEST_EQUALS(gFullscreenExitedCallbackCalled, 1, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliWebViewRegisterTextFoundCallback(void)
+{
+  ToolkitTestApplication application;
+
+  WebView view = WebView::New();
+  DALI_TEST_CHECK(view);
+
+  view.RegisterTextFoundCallback(&OnTextFound);
+  DALI_TEST_EQUALS(gTextFoundCallbackCalled, 0, TEST_LOCATION);
+
+  view.LoadUrl(TEST_URL1);
+  Test::EmitGlobalTimerSignal();
+  DALI_TEST_EQUALS(gTextFoundCallbackCalled, 1, TEST_LOCATION);
+  END_TEST;
+}
index 056af0b..9cd1e6b 100644 (file)
@@ -31,6 +31,7 @@ OPTION(ENABLE_I18N               "Turns on internationalisation" OFF)
 OPTION(ENABLE_COVERAGE           "Coverage" OFF)
 OPTION(ENABLE_PKG_CONFIGURE      "Use pkgconfig" ON)
 OPTION(ENABLE_LINK_TEST          "Enable the link test" ON)
+OPTION(ENABLE_VULKAN             "Enable Vulkan instead of GLES" OFF)
 OPTION(INSTALL_DOXYGEN_DOC       "Install doxygen doc" ON)
 OPTION(CONFIGURE_AUTOMATED_TESTS "Configure automated tests" ON)
 OPTION(USE_DEFAULT_RESOURCE_DIR  "Whether to use the default resource folders. Otherwise set environment variables for DALI_IMAGE_DIR, DALI_SOUND_DIR, DALI_STYLE_DIR, DALI_STYLE_IMAGE_DIR and DALI_DATA_READ_ONLY_DIR" ON)
@@ -359,6 +360,13 @@ IF ( WIN32 )
   FIND_PACKAGE( pthreads REQUIRED )
 ENDIF()
 
+IF( NOT ENABLE_VULKAN)
+  SET(SOURCES ${SOURCES}
+    ${toolkit_internal_egl_src_files}
+    ${public_api_egl_src_files}
+  )
+ENDIF()
+
 ADD_LIBRARY( ${name} ${LIBTYPE} ${SOURCES} )
 
 SET(CUSTOM_COMPILE_OPTIONS "")
@@ -581,10 +589,20 @@ IF( DOXYGEN_FOUND )
   # We need to get doxygen version, and block in doxygen in-files.
   EXECUTE_PROCESS( COMMAND bash -c "${DOXYGEN_EXECUTABLE} --version" OUTPUT_VARIABLE DOXYGEN_VERSION )
 
+  MESSAGE( STATUS "Doxygen version " ${DOXYGEN_VERSION} )
+
   IF(${DOXYGEN_VERSION} VERSION_LESS "1.9.1")
     SET( DOXYGEN_VERSION_LESS_1_9_1_BLOCKED "# ")
+    SET( DOXYGEN_VERSION_LESS_1_9_8_BLOCKED "# ")
+    SET( DOXYGEN_VERSION_OVER_1_9_8_BLOCKED "")
+  ELSEIF(${DOXYGEN_VERSION} VERSION_LESS "1.9.8")
+    SET( DOXYGEN_VERSION_LESS_1_9_1_BLOCKED "")
+    SET( DOXYGEN_VERSION_LESS_1_9_8_BLOCKED "# ")
+    SET( DOXYGEN_VERSION_OVER_1_9_8_BLOCKED "")
   ELSE()
     SET( DOXYGEN_VERSION_LESS_1_9_1_BLOCKED "")
+    SET( DOXYGEN_VERSION_LESS_1_9_8_BLOCKED "")
+    SET( DOXYGEN_VERSION_OVER_1_9_8_BLOCKED "# ")
   ENDIF()
 
   SET( DOXYGEN_DOCS_DIR ${ROOT_SRC_DIR}/docs )
@@ -640,6 +658,7 @@ MESSAGE( STATUS "Debug build:                   " ${ENABLE_DEBUG} )
 MESSAGE( STATUS "Export all symbols:            " ${ENABLE_EXPORTALL} )
 MESSAGE( STATUS "Coverage:                      " ${ENABLE_COVERAGE} )
 MESSAGE( STATUS "Trace:                         " ${ENABLE_TRACE} )
+MESSAGE( STATUS "Vulkan:                        " ${ENABLE_VULKAN} )
 MESSAGE( STATUS "Doxygen:                       " ${doxygenEnabled} )
 MESSAGE( STATUS "Data Dir (Read/Write):         " ${dataReadWriteDir} )
 MESSAGE( STATUS "Data Dir (Read Only):          " ${dataReadOnlyDir} )
index 4d281e5..15f387d 100644 (file)
@@ -1,6 +1,8 @@
 # Doxyfile @DOXYGEN_VERSION@
-# Note : Some option need to be blocked if version is lower than 1.9.1.
-# See how "DOXYGEN_VERSION_LESS_1_9_1_BLOCKED" defined in CMakeList.txt.
+# Note : Some option need to be blocked if version is lower than 1.9.1 (Ubuntu 22.04 defualt)
+# or lower / greate or equal than 1.9.8 (Ubuntu 24.04 default).
+# See how "DOXYGEN_VERSION_LESS_1_9_1_BLOCKED" and "DOXYGEN_VERSION_LESS_1_9_8_BLOCKED"
+# and "DOXYGEN_VERSION_OVER_1_9_8_BLOCKED" defined in CMakeList.txt
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
 # For lists, items can also be appended using:
 # TAG += value [value, ...]
 # Values that contain spaces should be placed between quotes (\" \").
+#
+# Note:
+#
+# Use doxygen to compare the used configuration file with the template
+# configuration file:
+# doxygen -x [configFile]
+# Use doxygen to compare the used configuration file with the template
+# configuration file without replacing the environment variables or CMake type
+# replacement variables:
+# doxygen -x_noenv [configFile]
 
 #---------------------------------------------------------------------------
 # Project related configuration options
@@ -62,16 +74,28 @@ PROJECT_LOGO           =
 
 OUTPUT_DIRECTORY       = @DOXYGEN_DOCS_DIR@/generated
 
-# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
+# sub-directories (in 2 levels) under the output directory of each output format
+# and will distribute the generated files over these directories. Enabling this
 # option can be useful when feeding doxygen a huge amount of source files, where
 # putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
+# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
+# control the number of sub-directories.
 # The default value is: NO.
 
 CREATE_SUBDIRS         = NO
 
+# Controls the number of sub-directories that will be created when
+# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
+# level increment doubles the number of directories, resulting in 4096
+# directories at level 8 which is the default and also the maximum value. The
+# sub-directories are organized in 2 levels, the first level always has a fixed
+# number of 16 directories.
+# Minimum value: 0, maximum value: 8, default value: 8.
+# This tag requires that the tag CREATE_SUBDIRS is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ CREATE_SUBDIRS_LEVEL   = 8
+
 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
 # characters to appear in the names of generated files. If set to NO, non-ASCII
 # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
@@ -83,26 +107,18 @@ ALLOW_UNICODE_NAMES    = NO
 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
 # documentation generated by doxygen is written. Doxygen will use this
 # information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
+# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
+# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
+# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
+# English messages), Korean, Korean-en (Korean with English messages), Latvian,
+# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
+# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
+# Swedish, Turkish, Ukrainian and Vietnamese.
 # The default value is: English.
 
 OUTPUT_LANGUAGE        = English
 
-# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all generated output in the proper direction.
-# Possible values are: None, LTR, RTL and Context.
-# The default value is: None.
-
-OUTPUT_TEXT_DIRECTION = None
-
 # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
 # descriptions after the members that are listed in the file and class
 # documentation (similar to Javadoc). Set to NO to disable this.
@@ -207,7 +223,7 @@ JAVADOC_AUTOBRIEF      = YES
 # interpreted by doxygen.
 # The default value is: NO.
 
-JAVADOC_BANNER = NO
+JAVADOC_BANNER         = NO
 
 # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
 # line (until the first dot) of a Qt-style comment as the brief description. If
@@ -235,7 +251,7 @@ MULTILINE_CPP_IS_BRIEF = NO
 # documentation blocks is shown as doxygen documentation.
 # The default value is: YES.
 
-@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ PYTHON_DOCSTRING = YES
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ PYTHON_DOCSTRING       = YES
 
 # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
 # documentation from any documented member that it re-implements.
@@ -260,16 +276,16 @@ TAB_SIZE               = 2
 # the documentation. An alias has the form:
 # name=value
 # For example adding
-# "sideeffect=@par Side Effects:\n"
+# "sideeffect=@par Side Effects:^^"
 # will allow you to put the command \sideeffect (or @sideeffect) in the
 # documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines (in the resulting output). You can put ^^ in the value part of an
-# alias to insert a newline as if a physical newline was in the original file.
-# When you need a literal { or } or , in the value part of an alias you have to
-# escape them by means of a backslash (\), this can lead to conflicts with the
-# commands \{ and \} for these it is advised to use the version @{ and @} or use
-# a double escape (\\{ and \\})
+# "Side Effects:". Note that you cannot put \n's in the value part of an alias
+# to insert newlines (in the resulting output). You can put ^^ in the value part
+# of an alias to insert a newline as if a physical newline was in the original
+# file. When you need a literal { or } or , in the value part of an alias you
+# have to escape them by means of a backslash (\), this can lead to conflicts
+# with the commands \{ and \} for these it is advised to use the version @{ and
+# @} or use a double escape (\\{ and \\})
 
 ALIASES                =
 # Clip alias inserts the specified file between two text markers.
@@ -525,15 +541,15 @@ OPTIMIZE_OUTPUT_VHDL   = NO
 # separated into more groups, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_SLICE = NO
+OPTIMIZE_OUTPUT_SLICE  = NO
 
 # Doxygen selects the parser to use depending on the extension of the files it
 # parses. With this tag you can assign which parser to use for a given
 # extension. Doxygen has a built-in mapping, but you can override or extend it
 # using this tag. The format is ext=language, where ext is a file extension, and
 # language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
-# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
 # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
 # tries to guess whether the code is fixed or free formatted code, this is the
 # default for Fortran type files). For instance to make doxygen treat .inc files
@@ -567,7 +583,18 @@ MARKDOWN_SUPPORT       = YES
 # Minimum value: 0, maximum value: 99, default value: 5.
 # This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
 
-TOC_INCLUDE_HEADINGS = 5
+TOC_INCLUDE_HEADINGS   = 5
+
+# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
+# generate identifiers for the Markdown headings. Note: Every identifier is
+# unique.
+# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
+# sequence number starting at 0 and GITHUB use the lower case version of title
+# with any whitespace replaced by '-' and punctuation characters removed.
+# The default value is: DOXYGEN.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ MARKDOWN_ID_STYLE      = DOXYGEN
 
 # When enabled doxygen tries to link words that correspond to documented
 # classes, or namespaces to their corresponding documentation. Such a link can
@@ -680,18 +707,26 @@ TYPEDEF_HIDES_STRUCT   = NO
 
 LOOKUP_CACHE_SIZE      = 0
 
-# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
 # during processing. When set to 0 doxygen will based this on the number of
 # cores available in the system. You can set it explicitly to a value larger
 # than 0 to get more control over the balance between CPU load and processing
 # speed. At this moment only the input processing can be done using multiple
 # threads. Since this is still an experimental feature the default is set to 1,
-# which efficively disables parallel processing. Please report any issues you
+# which effectively disables parallel processing. Please report any issues you
 # encounter. Generating dot graphs in parallel is controlled by the
 # DOT_NUM_THREADS setting.
 # Minimum value: 0, maximum value: 32, default value: 1.
 
-@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ NUM_PROC_THREADS = 1
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ NUM_PROC_THREADS       = 1
+
+# If the TIMESTAMP tag is set different from NO then each generated page will
+# contain the date or date and time when the page was generated. Setting this to
+# NO can help when comparing the output of multiple runs.
+# Possible values are: YES, NO, DATETIME and DATE.
+# The default value is: NO.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ TIMESTAMP              = YES
 
 #---------------------------------------------------------------------------
 # Build related configuration options
@@ -717,7 +752,7 @@ EXTRACT_PRIVATE        = YES
 # methods of a class will be included in the documentation.
 # The default value is: NO.
 
-EXTRACT_PRIV_VIRTUAL = NO
+EXTRACT_PRIV_VIRTUAL   = NO
 
 # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
 # scope will be included in the documentation.
@@ -774,7 +809,8 @@ HIDE_UNDOC_MEMBERS     = NO
 # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
 # undocumented classes that are normally visible in the class hierarchy. If set
 # to NO, these classes will be included in the various overviews. This option
-# has no effect if EXTRACT_ALL is enabled.
+# will also hide undocumented C++ concepts if enabled. This option has no effect
+# if EXTRACT_ALL is enabled.
 # The default value is: NO.
 
 HIDE_UNDOC_CLASSES     = NO
@@ -805,14 +841,15 @@ INTERNAL_DOCS          = NO
 # filesystem is case sensitive (i.e. it supports files in the same directory
 # whose names only differ in casing), the option must be set to YES to properly
 # deal with such files in case they appear in the input. For filesystems that
-# are not case sensitive the option should be be set to NO to properly deal with
+# are not case sensitive the option should be set to NO to properly deal with
 # output files written for symbols that only differ in casing, such as for two
 # classes, one named CLASS and the other named Class, and to also support
 # references to files without having to specify the exact matching casing. On
 # Windows (including Cygwin) and MacOS, users should typically set this option
 # to NO, whereas on Linux or other Unix flavors it should typically be set to
 # YES.
-# The default value is: system dependent.
+# Possible values are: SYSTEM, NO and YES.
+# The default value is: SYSTEM.
 
 CASE_SENSE_NAMES       = NO
 
@@ -830,6 +867,12 @@ HIDE_SCOPE_NAMES       = NO
 
 HIDE_COMPOUND_REFERENCE= NO
 
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ SHOW_HEADERFILE        = YES
+
 # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
 # the files that are included by a file in the documentation of that file.
 # The default value is: YES.
@@ -987,7 +1030,8 @@ FILE_VERSION_FILTER    =
 # output files in an output format independent way. To create the layout file
 # that represents doxygen's defaults, run doxygen with the -l option. You can
 # optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
 #
 # Note that if you run doxygen from a directory containing a file called
 # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
@@ -1033,27 +1077,50 @@ WARNINGS               = YES
 WARN_IF_UNDOCUMENTED   = YES
 
 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
 # The default value is: YES.
 
 WARN_IF_DOC_ERROR      = YES
 
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ WARN_IF_INCOMPLETE_DOC = YES
+
 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
 # are documented, but have no documentation for their parameters or return
-# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation. If
-# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# value. If set to NO, doxygen will only warn about wrong parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
 # The default value is: NO.
 
 WARN_NO_PARAMDOC       = YES
 
+# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
+# undocumented enumeration values. If set to NO, doxygen will accept
+# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: NO.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ WARN_IF_UNDOC_ENUM_VAL = NO
+
 # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
 # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
 # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
 # at the end of the doxygen process doxygen will return with a non-zero status.
-# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
+# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
+# write the warning messages in between other messages but write them at the end
+# of a run, in case a WARN_LOGFILE is defined the warning messages will be
+# besides being in the defined file also be shown at the end of a run, unless
+# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
+# the behavior will remain as with the setting FAIL_ON_WARNINGS.
+# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
 # The default value is: NO.
 
 WARN_AS_ERROR          = NO
@@ -1064,13 +1131,27 @@ WARN_AS_ERROR          = NO
 # and the warning text. Optionally the format may contain $version, which will
 # be replaced by the version of the file (if it could be obtained via
 # FILE_VERSION_FILTER)
+# See also: WARN_LINE_FORMAT
 # The default value is: $file:$line: $text.
 
 WARN_FORMAT            = "$file:$line: $text"
 
+# In the $text part of the WARN_FORMAT command it is possible that a reference
+# to a more specific place is given. To make it easier to jump to this place
+# (outside of doxygen) the user can define a custom "cut" / "paste" string.
+# Example:
+# WARN_LINE_FORMAT = "'vi $file +$line'"
+# See also: WARN_FORMAT
+# The default value is: at line $line of file $file.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ WARN_LINE_FORMAT       = "at line $line of file $file"
+
 # The WARN_LOGFILE tag can be used to specify a file to which warning and error
 # messages should be written. If left blank the output is written to standard
-# error (stderr).
+# error (stderr). In case the file specified cannot be opened for writing the
+# warning and error messages are written to standard error. When as file - is
+# specified the warning and error messages are written to standard output
+# (stdout).
 
 WARN_LOGFILE           = doxygen-errors.txt
 
@@ -1100,10 +1181,21 @@ INPUT                  = @DOXYGEN_DOCS_DIR@/content \
 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
 # documentation (see:
 # https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# See also: INPUT_FILE_ENCODING
 # The default value is: UTF-8.
 
 INPUT_ENCODING         = UTF-8
 
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
+# character encoding on a per file pattern basis. Doxygen will compare the file
+# name with each pattern and apply the encoding instead of the default
+# INPUT_ENCODING) if there is a match. The character encodings are a list of the
+# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
+# "INPUT_ENCODING" for further information on supported encodings.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ INPUT_FILE_ENCODING    =
+
 # If the value of the INPUT tag contains directories, you can use the
 # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
 # *.h) to filter out the source-files in the directories.
@@ -1115,12 +1207,12 @@ INPUT_ENCODING         = UTF-8
 # Note the list of default checked file patterns might differ from the list of
 # default file extension mappings.
 #
-# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
-# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
-# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
-# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
-# *.ucf, *.qsf and *.ice.
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
+# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl,
+# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php,
+# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be
+# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
 
 FILE_PATTERNS          = *.h \
                          *.md
@@ -1160,10 +1252,7 @@ EXCLUDE_PATTERNS       =
 # (namespaces, classes, functions, etc.) that should be excluded from the
 # output. The symbol name can be a fully qualified name, a word, or if the
 # wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
+# ANamespace::AClass, ANamespace::*Test
 
 EXCLUDE_SYMBOLS        = DaliInternal \
                          Dali::Internal \
@@ -1212,6 +1301,11 @@ IMAGE_PATH             = @DOXYGEN_DOCS_DIR@/content/images
 # code is scanned, but not when the output code is generated. If lines are added
 # or removed, the anchors will not be placed correctly.
 #
+# Note that doxygen will use the data processed and written to standard output
+# for further processing, therefore nothing else, like debug statements or used
+# commands (so in case of a Windows batch file always use @echo OFF), should be
+# written to standard output.
+#
 # Note that for custom extensions or not directly supported extensions you also
 # need to set EXTENSION_MAPPING for the extension otherwise the files are not
 # properly processed by doxygen.
@@ -1251,7 +1345,16 @@ FILTER_SOURCE_PATTERNS =
 # (index.html). This can be useful if you have a project on for instance GitHub
 # and want to reuse the introduction page also for the doxygen output.
 
-USE_MDFILE_AS_MAINPAGE = main.md
+USE_MDFILE_AS_MAINPAGE = @DOXYGEN_DOCS_DIR@/content/main.md
+
+# The Fortran standard specifies that for fixed formatted Fortran code all
+# characters from position 72 are to be considered as comment. A common
+# extension is to allow longer lines before the automatic comment starts. The
+# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
+# be processed before the automatic comment starts.
+# Minimum value: 7, maximum value: 10000, default value: 72.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ FORTRAN_COMMENT_AFTER  = 72
 
 #---------------------------------------------------------------------------
 # Configuration options related to source browsing
@@ -1350,11 +1453,13 @@ VERBATIM_HEADERS       = YES
 
 CLANG_ASSISTED_PARSING = NO
 
-# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
-# YES then doxygen will add the directory of each input to the include path.
+# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
+# tag is set to YES then doxygen will add the directory of each input to the
+# include path.
 # The default value is: YES.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
 
-@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ CLANG_ADD_INC_PATHS = YES
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ CLANG_ADD_INC_PATHS    = YES
 
 # If clang assisted parsing is enabled you can provide the compiler with command
 # line options that you would normally use when invoking the compiler. Note that
@@ -1375,7 +1480,7 @@ CLANG_OPTIONS          =
 # Note: The availability of this option depends on whether or not doxygen was
 # generated with the -Duse_libclang=ON option for CMake.
 
-CLANG_DATABASE_PATH =
+CLANG_DATABASE_PATH    =
 
 #---------------------------------------------------------------------------
 # Configuration options related to the alphabetical class index
@@ -1388,10 +1493,11 @@ CLANG_DATABASE_PATH =
 
 ALPHABETICAL_INDEX     = NO
 
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
+# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
+# that should be ignored while generating the index headers. The IGNORE_PREFIX
+# tag works for classes, function and member names. The entity will be placed in
+# the alphabetical list under the first letter of the entity name that remains
+# after removing the prefix.
 # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 IGNORE_PREFIX          =
@@ -1470,7 +1576,12 @@ HTML_STYLESHEET        =
 # Doxygen will copy the style sheet files to the output directory.
 # Note: The order of the extra style sheet files is of importance (e.g. the last
 # style sheet in the list overrules the setting of the previous ones in the
-# list). For an example see the documentation.
+# list).
+# Note: Since the styling of scrollbars can currently not be overruled in
+# Webkit/Chromium, the styling will be left out of the default doxygen.css if
+# one or more extra stylesheets have been specified. So if scrollbar
+# customization is desired it has to be added explicitly. For an example see the
+# documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_EXTRA_STYLESHEET  = @DOXYGEN_DOCS_DIR@/dali_doxygen.css
@@ -1485,9 +1596,22 @@ HTML_EXTRA_STYLESHEET  = @DOXYGEN_DOCS_DIR@/dali_doxygen.css
 
 HTML_EXTRA_FILES       =
 
+# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
+# should be rendered with a dark or light theme.
+# Possible values are: LIGHT always generate light mode output, DARK always
+# generate dark mode output, AUTO_LIGHT automatically set the mode according to
+# the user preference, use light mode if no preference is set (the default),
+# AUTO_DARK automatically set the mode according to the user preference, use
+# dark mode if no preference is set and TOGGLE allow to user to switch between
+# light and dark mode via a button.
+# The default value is: AUTO_LIGHT.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ HTML_COLORSTYLE        = LIGHT
+
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
 # will adjust the colors in the style sheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
+# this color. Hue is specified as an angle on a color-wheel, see
 # https://en.wikipedia.org/wiki/Hue for more information. For instance the value
 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
 # purple, and 360 is red again.
@@ -1497,7 +1621,7 @@ HTML_EXTRA_FILES       =
 HTML_COLORSTYLE_HUE    = 220
 
 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
 # value of 255 will produce the most vivid colors.
 # Minimum value: 0, maximum value: 255, default value: 100.
 # This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1522,7 +1646,7 @@ HTML_COLORSTYLE_GAMMA  = 80
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_TIMESTAMP         = YES
+@DOXYGEN_VERSION_OVER_1_9_8_BLOCKED@ HTML_TIMESTAMP         = YES
 
 # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
 # documentation will contain a main index with vertical navigation menus that
@@ -1533,7 +1657,7 @@ HTML_TIMESTAMP         = YES
 # The default value is: YES.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_DYNAMIC_MENUS = YES
+HTML_DYNAMIC_MENUS     = NO
 
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
@@ -1543,6 +1667,13 @@ HTML_DYNAMIC_MENUS = YES
 
 HTML_DYNAMIC_SECTIONS  = NO
 
+# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
+# dynamically folded and expanded in the generated HTML source code.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ HTML_CODE_FOLDING      = NO
+
 # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
 # shown in the various tree structured indices initially; the user can expand
 # and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -1579,6 +1710,13 @@ GENERATE_DOCSET        = NO
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
+# This tag determines the URL of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ DOCSET_FEEDURL         =
+
 # This tag specifies a string that should uniquely identify the documentation
 # set bundle. This should be a reverse domain-name style string, e.g.
 # com.mycompany.MyDocSet. Doxygen will append .docset to the name.
@@ -1604,8 +1742,12 @@ DOCSET_PUBLISHER_NAME  = Publisher
 # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
 # additional HTML index files: index.hhp, index.hhc, and index.hhk. The
 # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see:
-# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
 #
 # The HTML Help Workshop contains a compiler that can convert all HTML output
 # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1662,6 +1804,16 @@ BINARY_TOC             = NO
 
 TOC_EXPAND             = NO
 
+# The SITEMAP_URL tag is used to specify the full URL of the place where the
+# generated documentation will be placed on the server by the user during the
+# deployment of the documentation. The generated sitemap is called sitemap.xml
+# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
+# is specified no sitemap is generated. For information about the sitemap
+# protocol see https://www.sitemaps.org
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ SITEMAP_URL            =
+
 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
 # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
 # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@@ -1764,16 +1916,28 @@ DISABLE_INDEX          = NO
 # to work a browser that supports JavaScript, DHTML, CSS and frames is required
 # (i.e. any modern browser). Windows users are probably better off using the
 # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_TREEVIEW      = NONE
 
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATE_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ FULL_SIDEBAR           = NO
+
 # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
 # doxygen will group on one line in the generated HTML documentation.
 #
@@ -1798,6 +1962,13 @@ TREEVIEW_WIDTH         = 250
 
 EXT_LINKS_IN_WINDOW    = NO
 
+# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
+# addresses.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ OBFUSCATE_EMAILS       = YES
+
 # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
 # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
 # https://inkscape.org) to generate formulas as SVG images instead of PNGs for
@@ -1807,7 +1978,7 @@ EXT_LINKS_IN_WINDOW    = NO
 # The default value is: png.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ HTML_FORMULA_FORMAT = png
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ HTML_FORMULA_FORMAT    = png
 
 # Use this tag to change the font size of LaTeX formulas included as images in
 # the HTML documentation. When you change the font size after a successful
@@ -1818,22 +1989,11 @@ EXT_LINKS_IN_WINDOW    = NO
 
 FORMULA_FONTSIZE       = 10
 
-# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_TRANSPARENT    = YES
-
 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
 # to create new LaTeX commands to be used in formulas as building blocks. See
 # the section "Including formulas" for details.
 
-FORMULA_MACROFILE =
+FORMULA_MACROFILE      =
 
 # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
 # https://www.mathjax.org) which uses client side JavaScript for the rendering
@@ -1846,11 +2006,29 @@ FORMULA_MACROFILE =
 
 USE_MATHJAX            = NO
 
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ MATHJAX_VERSION        = MathJax_2
+
 # When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
 # Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
 # The default value is: HTML-CSS.
 # This tag requires that the tag USE_MATHJAX is set to YES.
 
@@ -1863,15 +2041,21 @@ MATHJAX_FORMAT         = HTML-CSS
 # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
 # Content Delivery Network so you can quickly see the result without installing
 # MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
+# MathJax from https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
 # This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
 
 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
 # extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
 # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
 # This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_EXTENSIONS     =
@@ -2020,7 +2204,7 @@ MAKEINDEX_CMD_NAME     = makeindex
 # The default value is: makeindex.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
-LATEX_MAKEINDEX_CMD = makeindex
+LATEX_MAKEINDEX_CMD    = makeindex
 
 # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
 # documents. This may be useful for small projects and may help to save some
@@ -2051,29 +2235,31 @@ PAPER_TYPE             = a4
 
 EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
-# generated LaTeX document. The header should contain everything until the first
-# chapter. If it is left blank doxygen will generate a standard header. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
+# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
+# the generated LaTeX document. The header should contain everything until the
+# first chapter. If it is left blank doxygen will generate a standard header. It
+# is highly recommended to start with a default header using
+# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
+# and then modify the file new_header.tex. See also section "Doxygen usage" for
+# information on how to generate the default header that doxygen normally uses.
 #
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
-# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
-# string, for the replacement values of the other commands the user is referred
-# to HTML_HEADER.
+# Note: Only use a user-defined header if you know what you are doing!
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. The following
+# commands have a special meaning inside the header (and footer): For a
+# description of the possible markers and block names see the documentation.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HEADER           =
 
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
-# generated LaTeX document. The footer should contain everything after the last
-# chapter. If it is left blank doxygen will generate a standard footer. See
+# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
+# the generated LaTeX document. The footer should contain everything after the
+# last chapter. If it is left blank doxygen will generate a standard footer. See
 # LATEX_HEADER for more information on how to generate a default footer and what
-# special commands can be used inside the footer.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
+# special commands can be used inside the footer. See also section "Doxygen
+# usage" for information on how to generate the default footer that doxygen
+# normally uses. Note: Only use a user-defined footer if you know what you are
+# doing!
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_FOOTER           =
@@ -2116,10 +2302,16 @@ PDF_HYPERLINKS         = YES
 
 USE_PDFLATEX           = YES
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
-# command to the generated LaTeX files. This will instruct LaTeX to keep running
-# if errors occur, instead of asking the user for help. This option is also used
-# when generating formulas in HTML.
+# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
+# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
+# mode nothing is printed on the terminal, errors are scrolled as if <return> is
+# hit at every error; missing files that TeX tries to input or request from
+# keyboard input (\read on a not open input stream) cause the job to abort,
+# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
+# but there is no possibility of user interaction just like in batch mode,
+# SCROLL In scroll mode, TeX will stop only for missing files to input or if
+# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
+# each error, asking for user intervention.
 # The default value is: NO.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
@@ -2132,16 +2324,6 @@ LATEX_BATCHMODE        = NO
 
 LATEX_HIDE_INDICES     = NO
 
-# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
-# code with syntax highlighting in the LaTeX output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_SOURCE_CODE      = NO
-
 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
 # bibliography, e.g. plainnat, or ieeetr. See
 # https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
@@ -2150,21 +2332,13 @@ LATEX_SOURCE_CODE      = NO
 
 LATEX_BIB_STYLE        = plain
 
-# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
-# page will contain the date and time when the page was generated. Setting this
-# to NO can help when comparing the output of multiple runs.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_TIMESTAMP        = NO
-
 # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
 # path from which the emoji images will be read. If a relative path is entered,
 # it will be relative to the LATEX_OUTPUT directory. If left blank the
 # LATEX_OUTPUT directory will be used.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
-LATEX_EMOJI_DIRECTORY =
+LATEX_EMOJI_DIRECTORY  =
 
 #---------------------------------------------------------------------------
 # Configuration options related to the RTF output
@@ -2222,16 +2396,6 @@ RTF_STYLESHEET_FILE    =
 
 RTF_EXTENSIONS_FILE    =
 
-# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
-# with syntax highlighting in the RTF output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_SOURCE_CODE        = NO
-
 #---------------------------------------------------------------------------
 # Configuration options related to the man page output
 #---------------------------------------------------------------------------
@@ -2328,21 +2492,12 @@ GENERATE_DOCBOOK       = NO
 
 DOCBOOK_OUTPUT         = docbook
 
-# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
-# program listings (including syntax highlighting and cross-referencing
-# information) to the DOCBOOK output. Note that enabling this will significantly
-# increase the size of the DOCBOOK output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_PROGRAMLISTING = NO
-
 #---------------------------------------------------------------------------
 # Configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
 # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
 # the structure of the code including all documentation. Note that this feature
 # is still experimental and incomplete at the moment.
 # The default value is: NO.
@@ -2350,6 +2505,32 @@ DOCBOOK_PROGRAMLISTING = NO
 GENERATE_AUTOGEN_DEF   = NO
 
 #---------------------------------------------------------------------------
+# Configuration options related to Sqlite3 output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
+# database with symbols found by doxygen stored in tables.
+# The default value is: NO.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ GENERATE_SQLITE3       = NO
+
+# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
+# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
+# in front of it.
+# The default directory is: sqlite3.
+# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ SQLITE3_OUTPUT         = sqlite3
+
+# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
+# database file will be recreated with each doxygen run. If set to NO, doxygen
+# will warn if an a database file is already found and not modify it.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ SQLITE3_RECREATE_DB    = YES
+
+#---------------------------------------------------------------------------
 # Configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
@@ -2423,7 +2604,8 @@ SEARCH_INCLUDES        = YES
 
 # The INCLUDE_PATH tag can be used to specify one or more directories that
 # contain include files that are not input files but should be processed by the
-# preprocessor.
+# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
+# RECURSIVE has no effect here.
 # This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
 INCLUDE_PATH           =
@@ -2448,7 +2630,7 @@ PREDEFINED             = DALI_CORE_API \
                          DALI_ADAPTOR_API \
                          DALI_TOOLKIT_API \
                          DALI_SCENE3D_API \
-                         DALI_INTERNAL \
+                         DALI_INTERNAL
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
@@ -2494,15 +2676,15 @@ TAGFILES               =
 
 GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
-# the class index. If set to NO, only the inherited external classes will be
-# listed.
+# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
+# will be listed in the class and namespace index. If set to NO, only the
+# inherited external classes will be listed.
 # The default value is: NO.
 
 ALLEXTERNALS           = NO
 
 # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will be
+# in the topic index. If set to NO, only the current project's groups will be
 # listed.
 # The default value is: YES.
 
@@ -2516,25 +2698,9 @@ EXTERNAL_GROUPS        = YES
 EXTERNAL_PAGES         = YES
 
 #---------------------------------------------------------------------------
-# Configuration options related to the dot tool
+# Configuration options related to diagram generator tools
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
-# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
-# NO turns the diagrams off. Note that this option also works with HAVE_DOT
-# disabled, but it is recommended to install and use dot, since it yields more
-# powerful graphs.
-# The default value is: YES.
-
-CLASS_DIAGRAMS         = YES
-
-# You can include diagrams made with dia in doxygen documentation. Doxygen will
-# then run dia to produce the diagram and insert it in the documentation. The
-# DIA_PATH tag allows you to specify the directory where the dia binary resides.
-# If left empty dia is assumed to be found in the default search path.
-
-DIA_PATH               =
-
 # If set to YES the inheritance and collaboration graphs will hide inheritance
 # and usage relations if the target is undocumented or is not a class.
 # The default value is: YES.
@@ -2543,7 +2709,7 @@ HIDE_UNDOC_RELATIONS   = YES
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
 # available from the path. This tool is part of Graphviz (see:
-# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
 # Bell Labs. The other options in this section have no effect if this option is
 # set to NO
 # The default value is: YES.
@@ -2560,49 +2726,73 @@ HAVE_DOT               = NO
 
 DOT_NUM_THREADS        = 0
 
-# When you want a differently looking font in the dot files that doxygen
-# generates you can specify the font name using DOT_FONTNAME. You need to make
-# sure dot is able to find the font, which can be done by putting it in a
-# standard location or by setting the DOTFONTPATH environment variable or by
-# setting DOT_FONTPATH to the directory containing the font.
-# The default value is: Helvetica.
+# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
+# subgraphs. When you want a differently looking font in the dot files that
+# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
+# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
+# Edge and Graph Attributes specification</a> You need to make sure dot is able
+# to find the font, which can be done by putting it in a standard location or by
+# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font. Default graphviz fontsize is 14.
+# The default value is: fontname=Helvetica,fontsize=10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ DOT_COMMON_ATTR        = "fontname=Helvetica,fontsize=10"
+
+# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
+# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
+# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
+# arrows shapes.</a>
+# The default value is: labelfontname=Helvetica,labelfontsize=10.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_FONTNAME           =
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ DOT_EDGE_ATTR          = "labelfontname=Helvetica,labelfontsize=10"
 
-# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
-# dot graphs.
-# Minimum value: 4, maximum value: 24, default value: 10.
+# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
+# around nodes set 'shape=plain' or 'shape=plaintext' <a
+# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
+# The default value is: shape=box,height=0.2,width=0.4.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_FONTSIZE           = 10
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ DOT_NODE_ATTR          = "shape=box,height=0.2,width=0.4"
 
-# By default doxygen will tell dot to use the default font as specified with
-# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
-# the path where dot can find it using this tag.
+# You can set the path where dot can find font specified with fontname in
+# DOT_COMMON_ATTR and others dot attributes.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTPATH           =
 
-# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
-# each documented class showing the direct and indirect inheritance relations.
-# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
+# generate a graph for each documented class showing the direct and indirect
+# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
+# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
+# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
+# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
+# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
+# relations will be shown as texts / links.
+# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
 # The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
 
 CLASS_GRAPH            = YES
 
 # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
 # graph for each documented class showing the direct and indirect implementation
 # dependencies (inheritance, containment, and class references variables) of the
-# class with other documented classes.
+# class with other documented classes. Explicit enabling a collaboration graph,
+# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
+# command \collaborationgraph. Disabling a collaboration graph can be
+# accomplished by means of the command \hidecollaborationgraph.
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
 COLLABORATION_GRAPH    = YES
 
 # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
+# groups, showing the direct groups dependencies. Explicit enabling a group
+# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
+# of the command \groupgraph. Disabling a directory graph can be accomplished by
+# means of the command \hidegroupgraph. See also the chapter Grouping in the
+# manual.
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2627,7 +2817,7 @@ UML_LOOK               = NO
 # Minimum value: 0, maximum value: 100, default value: 10.
 # This tag requires that the tag UML_LOOK is set to YES.
 
-UML_LIMIT_NUM_FIELDS = 10
+UML_LIMIT_NUM_FIELDS   = 10
 
 # If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
 # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
@@ -2640,7 +2830,7 @@ UML_LIMIT_NUM_FIELDS = 10
 # The default value is: NO.
 # This tag requires that the tag UML_LOOK is set to YES.
 
-@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ DOT_UML_DETAILS = NO
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ DOT_UML_DETAILS        = NO
 
 # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
 # to display on a single line. If the actual line length exceeds this threshold
@@ -2649,7 +2839,7 @@ UML_LIMIT_NUM_FIELDS = 10
 # Minimum value: 0, maximum value: 1000, default value: 17.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ DOT_WRAP_THRESHOLD = 17
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ DOT_WRAP_THRESHOLD     = 17
 
 # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
 # collaboration graphs will show the relations between templates and their
@@ -2662,7 +2852,9 @@ TEMPLATE_RELATIONS     = NO
 # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
 # YES then doxygen will generate a graph for each documented file showing the
 # direct and indirect include dependencies of the file with other documented
-# files.
+# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
+# can be accomplished by means of the command \includegraph. Disabling an
+# include graph can be accomplished by means of the command \hideincludegraph.
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2671,7 +2863,10 @@ INCLUDE_GRAPH          = YES
 # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
 # set to YES then doxygen will generate a graph for each documented file showing
 # the direct and indirect include dependencies of the file with other documented
-# files.
+# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
+# to NO, can be accomplished by means of the command \includedbygraph. Disabling
+# an included by graph can be accomplished by means of the command
+# \hideincludedbygraph.
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2711,23 +2906,32 @@ GRAPHICAL_HIERARCHY    = YES
 # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
 # dependencies a directory has on other directories in a graphical way. The
 # dependency relations are determined by the #include relations between the
-# files in the directories.
+# files in the directories. Explicit enabling a directory graph, when
+# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
+# \directorygraph. Disabling a directory graph can be accomplished by means of
+# the command \hidedirectorygraph.
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
 DIRECTORY_GRAPH        = YES
 
+# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
+# of child directories generated in directory dependency graphs by dot.
+# Minimum value: 1, maximum value: 25, default value: 1.
+# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ DIR_GRAPH_MAX_DEPTH    = 1
+
 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
 # generated by dot. For an explanation of the image formats see the section
 # output formats in the documentation of the dot tool (Graphviz (see:
-# http://www.graphviz.org/)).
+# https://www.graphviz.org/)).
 # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
 # to make the SVG files visible in IE 9+ (other browsers do not have this
 # requirement).
-# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
-# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
-# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
-# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd,
+# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd,
+# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
 # png:gdiplus:gdiplus.
 # The default value is: png.
 # This tag requires that the tag HAVE_DOT is set to YES.
@@ -2759,11 +2963,12 @@ DOT_PATH               =
 
 DOTFILE_DIRS           =
 
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the \mscfile
-# command).
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
 
-MSCFILE_DIRS           =
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ DIA_PATH               =
 
 # The DIAFILE_DIRS tag can be used to specify one or more directories that
 # contain dia files that are included in the documentation (see the \diafile
@@ -2772,17 +2977,17 @@ MSCFILE_DIRS           =
 DIAFILE_DIRS           =
 
 # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
-# path where java can find the plantuml.jar file. If left blank, it is assumed
-# PlantUML is not used or called during a preprocessing step. Doxygen will
-# generate a warning when it encounters a \startuml command in this case and
-# will not generate output for the diagram.
+# path where java can find the plantuml.jar file or to the filename of jar file
+# to be used. If left blank, it is assumed PlantUML is not used or called during
+# a preprocessing step. Doxygen will generate a warning when it encounters a
+# \startuml command in this case and will not generate output for the diagram.
 
 PLANTUML_JAR_PATH      =
 
 # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
 # configuration file for plantuml.
 
-PLANTUML_CFG_FILE =
+PLANTUML_CFG_FILE      =
 
 # When using plantuml, the specified paths are searched for files specified by
 # the !include statement in a plantuml block.
@@ -2813,18 +3018,6 @@ DOT_GRAPH_MAX_NODES    = 50
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not seem
-# to support this out of the box.
-#
-# Warning: Depending on the platform used, enabling this option may lead to
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
-# read).
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_TRANSPARENT        = NO
-
 # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
 # files in one run (i.e. multiple -o and -T options on the command line). This
 # makes dot run faster, but since only newer versions of dot (>1.8.10) support
@@ -2837,6 +3030,8 @@ DOT_MULTI_TARGETS      = NO
 # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
 # explaining the meaning of the various boxes and arrows in the dot generated
 # graphs.
+# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
+# graphical representation for inheritance and collaboration diagrams is used.
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
@@ -2845,8 +3040,24 @@ GENERATE_LEGEND        = YES
 # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
 # files that are used to generate the various graphs.
 #
-# Note: This setting is not only used for dot files but also for msc and
-# plantuml temporary files.
+# Note: This setting is not only used for dot files but also for msc temporary
+# files.
 # The default value is: YES.
 
 DOT_CLEANUP            = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
+# use a built-in version of mscgen tool to produce the charts. Alternatively,
+# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
+# specifying prog as the value, doxygen will call the tool as prog -T
+# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
+# output file formats "png", "eps", "svg", and "ismap".
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ MSCGEN_TOOL            =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+@DOXYGEN_VERSION_LESS_1_9_8_BLOCKED@ MSCFILE_DIRS           =
index df626ad..806983c 100644 (file)
@@ -28,6 +28,7 @@
 #include <dali-scene3d/public-api/common/environment-map.h>
 
 #include <dali-scene3d/public-api/controls/model/model.h>
+#include <dali-scene3d/public-api/controls/panel/panel.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
 
 #include <dali-scene3d/public-api/loader/alpha-function-helper.h>
index 3d3ab93..7b2f45f 100644 (file)
@@ -19,6 +19,8 @@
 #include <dali-scene3d/internal/common/image-resource-loader.h>
 
 // EXTERNAL INCLUDES
+#include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/devel-api/adaptor-framework/lifecycle-controller.h>
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
@@ -28,6 +30,7 @@
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/pixel-data-integ.h>
+#include <dali/integration-api/trace.h>
 #include <dali/public-api/adaptor-framework/timer.h>
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/base-object.h>
@@ -47,9 +50,12 @@ namespace
 constexpr uint32_t MAXIMUM_COLLECTING_ITEM_COUNTS_PER_GC_CALL = 5u;
 constexpr uint32_t GC_PERIOD_MILLISECONDS                     = 1000u;
 
+constexpr std::string_view PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME = "brdfLUT.png";
+
 #ifdef DEBUG_ENABLED
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_IMAGE_RESOURCE_LOADER");
 #endif
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_MODEL_PERFORMANCE_MARKER, false);
 
 bool IsDefaultPixelData(const Dali::PixelData& pixelData)
 {
@@ -153,12 +159,29 @@ Dali::PixelData CreatePixelDataFromImageInfo(const ImageInformation& info, bool
 {
   Dali::PixelData pixelData;
 
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOAD_IMAGE_FROM_FILE", [&](std::ostringstream& oss) {
+    oss << "[";
+    if(info.mDimensions.GetWidth() > 0 || info.mDimensions.GetHeight() > 0)
+    {
+      oss << "d:" << info.mDimensions.GetWidth() << "x" << info.mDimensions.GetHeight() << " ";
+    }
+    oss << "f:" << info.mFittingMode << " s:" << info.mSamplingMode << " c:" << info.mOrientationCorrection << " ";
+    oss << "u:" << info.mUrl << "]";
+  });
   // Load the image synchronously (block the thread here).
   Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromFile(info.mUrl, info.mDimensions, info.mFittingMode, info.mSamplingMode, info.mOrientationCorrection);
   if(pixelBuffer)
   {
     pixelData = Dali::Devel::PixelBuffer::Convert(pixelBuffer, releasePixelData);
   }
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOAD_IMAGE_FROM_FILE", [&](std::ostringstream& oss) {
+    oss << "[";
+    if(pixelData)
+    {
+      oss << "d:" << pixelData.GetWidth() << "x" << pixelData.GetHeight() << " f:" << pixelData.GetPixelFormat() << " ";
+    }
+    oss << "u:" << info.mUrl << "]";
+  });
   return pixelData;
 }
 
@@ -497,8 +520,11 @@ private:
 };
 
 static std::shared_ptr<CacheImpl> gCacheImpl{nullptr};
-static Dali::Texture              gEmptyTextureWhiteRGB{};
-static Dali::Texture              gEmptyCubeTextureWhiteRGB{};
+
+// Static singletone instance what we usually used.
+static Dali::Texture gEmptyTextureWhiteRGB{};
+static Dali::Texture gEmptyCubeTextureWhiteRGB{};
+static Dali::Texture gDefaultBrdfTexture{};
 
 std::shared_ptr<CacheImpl> GetCacheImpl()
 {
@@ -516,6 +542,7 @@ void DestroyCacheImpl()
   // Remove texture object when application stopped.
   gEmptyTextureWhiteRGB.Reset();
   gEmptyCubeTextureWhiteRGB.Reset();
+  gDefaultBrdfTexture.Reset();
 }
 
 } // namespace
@@ -527,7 +554,7 @@ namespace ImageResourceLoader
 // Called by main thread..
 Dali::Texture GetEmptyTextureWhiteRGB()
 {
-  if(!gEmptyTextureWhiteRGB)
+  if(DALI_UNLIKELY(!gEmptyTextureWhiteRGB))
   {
     Dali::PixelData emptyPixelData = GetEmptyPixelDataWhiteRGB();
     gEmptyTextureWhiteRGB          = Texture::New(TextureType::TEXTURE_2D, emptyPixelData.GetPixelFormat(), emptyPixelData.GetWidth(), emptyPixelData.GetHeight());
@@ -538,7 +565,7 @@ Dali::Texture GetEmptyTextureWhiteRGB()
 
 Dali::Texture GetEmptyCubeTextureWhiteRGB()
 {
-  if(!gEmptyCubeTextureWhiteRGB)
+  if(DALI_UNLIKELY(!gEmptyCubeTextureWhiteRGB))
   {
     Dali::PixelData emptyPixelData = GetEmptyPixelDataWhiteRGB();
     gEmptyCubeTextureWhiteRGB      = Texture::New(TextureType::TEXTURE_CUBE, emptyPixelData.GetPixelFormat(), emptyPixelData.GetWidth(), emptyPixelData.GetHeight());
@@ -550,6 +577,17 @@ Dali::Texture GetEmptyCubeTextureWhiteRGB()
   return gEmptyCubeTextureWhiteRGB;
 }
 
+Dali::Texture GetDefaultBrdfTexture()
+{
+  if(DALI_UNLIKELY(!gDefaultBrdfTexture))
+  {
+    Dali::PixelData brdfPixelData = GetDefaultBrdfPixelData();
+    gDefaultBrdfTexture           = Texture::New(TextureType::TEXTURE_2D, brdfPixelData.GetPixelFormat(), brdfPixelData.GetWidth(), brdfPixelData.GetHeight());
+    gDefaultBrdfTexture.Upload(brdfPixelData, 0, 0, 0, 0, brdfPixelData.GetWidth(), brdfPixelData.GetHeight());
+  }
+  return gDefaultBrdfTexture;
+}
+
 Dali::Texture GetCachedTexture(Dali::PixelData pixelData, bool mipmapRequired)
 {
   if(Dali::Adaptor::IsAvailable() && SupportPixelDataCache(pixelData))
@@ -603,6 +641,26 @@ Dali::PixelData GetEmptyPixelDataZAxisAndAlphaRGBA()
   return emptyPixelData;
 }
 
+Dali::PixelData GetDefaultBrdfPixelData()
+{
+  static Dali::Mutex sPixelDataMutex;
+  {
+    Dali::Mutex::ScopedLock lock(sPixelDataMutex);
+
+    static Dali::PixelData defaultBrdfPixelData;
+
+    if(DALI_UNLIKELY(!defaultBrdfPixelData))
+    {
+      Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromFile(Dali::Toolkit::AssetManager::GetDaliImagePath() + std::string(PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME));
+      if(pixelBuffer)
+      {
+        defaultBrdfPixelData = Devel::PixelBuffer::Convert(pixelBuffer);
+      }
+    }
+    return defaultBrdfPixelData;
+  }
+}
+
 Dali::PixelData GetCachedPixelData(const std::string& url)
 {
   return GetCachedPixelData(url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true);
index 3957009..b498b30 100644 (file)
@@ -54,6 +54,12 @@ Dali::Texture GetEmptyTextureWhiteRGB();
 Dali::Texture GetEmptyCubeTextureWhiteRGB();
 
 /**
+ * @brief Get default BRDF texture.
+ * @return A Texture object containing the default BRDF texture.
+ */
+Dali::Texture GetDefaultBrdfTexture();
+
+/**
  * @brief Get cached texture handle, or create new texture and upload.
  * @param[in] pixelData The PixelData of image to upload
  * @param[in] mipmapRequired True if this texture need to generate mipmap
@@ -99,6 +105,12 @@ Dali::PixelData GetEmptyPixelDataZAxisRGB();
 Dali::PixelData GetEmptyPixelDataZAxisAndAlphaRGBA();
 
 /**
+ * @brief Get cached pixelData handle with brdf pixel data has.
+ * @return A PixelData object containing brdf data.
+ */
+Dali::PixelData GetDefaultBrdfPixelData();
+
+/**
  * @brief Get cached image, or loads an image synchronously.
  * @note If cache handler is not created yet, or destroyed due to app terminated, it will load image synchronously without cache.
  * @param[in] url The URL of the image file to load
index 10e0aba..fd634f8 100644 (file)
 
 // EXTERNAL INCLUDES
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
 #include <filesystem>
 
+#ifdef TRACE_ENABLED
+#include <chrono>
+#include <iomanip>
+#include <sstream>
+#include <thread>
+#endif
+
 namespace Dali
 {
 namespace Scene3D
@@ -31,6 +39,18 @@ namespace Internal
 namespace
 {
 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
+
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_MODEL_PERFORMANCE_MARKER, false);
+
+#ifdef TRACE_ENABLED
+uint64_t GetNanoseconds()
+{
+  // Get the time of a monotonic clock since its epoch.
+  auto epoch    = std::chrono::steady_clock::now().time_since_epoch();
+  auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
+  return static_cast<uint64_t>(duration.count());
+}
+#endif
 } // namespace
 
 ModelLoadTask::ModelLoadTask(const std::string& modelUrl, const std::string& resourceDirectoryUrl, CallbackBase* callback)
@@ -51,24 +71,45 @@ ModelLoadTask::~ModelLoadTask()
 
 void ModelLoadTask::Process()
 {
+#ifdef TRACE_ENABLED
+  uint64_t mStartTimeNanoSceonds = 0;
+  uint64_t mEndTimeNanoSceonds   = 0;
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    mStartTimeNanoSceonds = GetNanoseconds();
+    DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOADING_TASK", [&](std::ostringstream& oss)
+                                            { oss << "[u:" << mModelUrl << ",dir:" << mResourceDirectoryUrl << "]"; });
+  }
+#endif
+
   if(mResourceDirectoryUrl.empty())
   {
     std::filesystem::path modelUrl(mModelUrl);
     mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
   }
 
-  Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
+  Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type)
+  {
     return mResourceDirectoryUrl;
   };
 
   mModelLoader = std::make_shared<Dali::Scene3D::Loader::ModelLoader>(mModelUrl, mResourceDirectoryUrl, mLoadResult);
 
   bool loadSucceeded = false;
+
+#ifdef TRACE_ENABLED
+  bool useCachedModel = false;
+#endif
+
   {
     // Lock model url during process, so let we do not try to load same model multiple times.
     mModelCacheManager.LockModelLoadScene(mModelUrl);
     if(mModelCacheManager.IsSceneLoaded(mModelUrl))
     {
+#ifdef TRACE_ENABLED
+      useCachedModel = true;
+#endif
+
       loadSucceeded = true;
     }
     else
@@ -90,6 +131,21 @@ void ModelLoadTask::Process()
     mModelCacheManager.UnlockModelLoadScene(mModelUrl);
   }
 
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    mEndTimeNanoSceonds = GetNanoseconds();
+    DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOADING_TASK", [&](std::ostringstream& oss)
+                                          {
+      oss << std::fixed << std::setprecision(3);
+      oss << "[";
+      oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
+      oss << "c?" << useCachedModel << " ";
+      oss << "s?" << loadSucceeded << " ";
+      oss << "dir:" << mResourceDirectoryUrl << " ";
+      oss << "u:" << mModelUrl << "]"; });
+  }
+#endif
   if(!loadSucceeded)
   {
     DALI_LOG_ERROR("Failed to load scene from '%s'\n", mModelUrl.c_str());
index f012aa6..bd911c5 100644 (file)
@@ -37,6 +37,7 @@
 #include <dali-scene3d/internal/event/collider-mesh-processor.h>
 #include <dali-scene3d/internal/light/light-impl.h>
 #include <dali-scene3d/internal/model-components/model-node-impl.h>
+#include <dali-scene3d/internal/model-components/model-node-tree-utility.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/loader/animation-definition.h>
 #include <dali-scene3d/public-api/loader/camera-parameters.h>
@@ -47,6 +48,7 @@
 #include <dali-scene3d/public-api/loader/shader-manager.h>
 #include <dali-scene3d/public-api/model-motion/motion-index/blend-shape-index.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
+
 using namespace Dali;
 
 namespace Dali
@@ -195,46 +197,6 @@ void UpdateBlendShapeNodeMapRecursively(Model::BlendShapeModelNodeMap& resultMap
   }
 }
 
-void UpdateShaderRecursively(Scene3D::ModelNode node, Scene3D::Loader::ShaderManagerPtr shaderManager)
-{
-  if(!node)
-  {
-    return;
-  }
-
-  GetImplementation(node).UpdateShader(shaderManager);
-
-  uint32_t childrenCount = node.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
-    if(childNode)
-    {
-      UpdateShaderRecursively(childNode, shaderManager);
-    }
-  }
-}
-
-void UpdateShadowMapTextureRecursively(Scene3D::ModelNode node, Dali::Texture shadowMapTexture)
-{
-  if(!node)
-  {
-    return;
-  }
-
-  GetImplementation(node).SetShadowMapTexture(shadowMapTexture);
-
-  uint32_t childrenCount = node.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
-    if(childNode)
-    {
-      UpdateShadowMapTextureRecursively(childNode, shadowMapTexture);
-    }
-  }
-}
-
 void ResetResourceTask(IntrusivePtr<AsyncTask>&& asyncTask)
 {
   if(!asyncTask)
@@ -312,11 +274,11 @@ void Model::AddModelNode(Scene3D::ModelNode modelNode)
     mModelResourceReady = true;
   }
 
-  UpdateShaderRecursively(modelNode, mShaderManager);
+  ModelNodeTreeUtility::UpdateShaderRecursively(modelNode, mShaderManager);
 
   if(mShadowMapTexture)
   {
-    UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture);
+    ModelNodeTreeUtility::UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture);
   }
 
   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
@@ -331,7 +293,7 @@ void Model::AddModelNode(Scene3D::ModelNode modelNode)
   if(modelNode.HasColliderMesh())
   {
     RegisterColliderMesh(modelNode);
-    Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self()));
+    Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(*this);
   }
 
   if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
@@ -345,7 +307,7 @@ void Model::RegisterColliderMesh(Scene3D::ModelNode& modelNode)
   mColliderMeshes[modelNode.GetProperty<int>(Actor::Property::ID)] = modelNode;
 
   // Add processor
-  Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self()));
+  Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(*this);
 }
 
 void Model::RemoveColliderMesh(Scene3D::ModelNode& node)
@@ -371,7 +333,7 @@ void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
 
   if(mModelRoot)
   {
-    UpdateShaderRecursively(modelNode, nullptr);
+    ModelNodeTreeUtility::UpdateShaderRecursively(modelNode, nullptr);
     mModelRoot.Remove(modelNode);
   }
 }
@@ -815,7 +777,7 @@ void Model::SetMotionData(Scene3D::MotionData motionData)
 void Model::CastShadow(bool castShadow)
 {
   mIsShadowCasting = castShadow;
-  UpdateCastShadowRecursively(mModelRoot, mIsShadowCasting);
+  ModelNodeTreeUtility::UpdateCastShadowRecursively(mModelRoot, mIsShadowCasting);
 }
 
 bool Model::IsShadowCasting() const
@@ -826,7 +788,7 @@ bool Model::IsShadowCasting() const
 void Model::ReceiveShadow(bool receiveShadow)
 {
   mIsShadowReceiving = receiveShadow;
-  UpdateReceiveShadowRecursively(mModelRoot, mIsShadowReceiving);
+  ModelNodeTreeUtility::UpdateReceiveShadowRecursively(mModelRoot, mIsShadowReceiving);
 }
 
 bool Model::IsShadowReceiving() const
@@ -834,7 +796,6 @@ bool Model::IsShadowReceiving() const
   return mIsShadowReceiving;
 }
 
-
 ///////////////////////////////////////////////////////////
 //
 // Private methods
@@ -842,6 +803,8 @@ bool Model::IsShadowReceiving() const
 
 void Model::OnInitialize()
 {
+  Collidable::SetCollidableActor(Scene3D::Model::DownCast(Self()));
+
   // Make ParentOrigin as Center.
   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
 
@@ -865,7 +828,7 @@ void Model::OnSceneConnection(int depth)
       if(mShaderManager != shaderManager)
       {
         mShaderManager = shaderManager;
-        UpdateShaderRecursively(mModelRoot, mShaderManager);
+        ModelNodeTreeUtility::UpdateShaderRecursively(mModelRoot, mShaderManager);
       }
       break;
     }
@@ -878,7 +841,7 @@ void Model::OnSceneConnection(int depth)
   if(!parentSceneView)
   {
     mShaderManager = new Dali::Scene3D::Loader::ShaderManager();
-    UpdateShaderRecursively(mModelRoot, mShaderManager);
+    ModelNodeTreeUtility::UpdateShaderRecursively(mModelRoot, mShaderManager);
   }
 
   if(!mModelLoadTask && !mModelResourceReady && !mModelUrl.empty())
@@ -1007,87 +970,6 @@ void Model::FitModelPosition()
   mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
 }
 
-void Model::UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow)
-{
-  if(!node)
-  {
-    return;
-  }
-
-  GetImplementation(node).CastShadow(castShadow);
-  uint32_t childrenCount = node.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
-    if(!childNode)
-    {
-      continue;
-    }
-    UpdateCastShadowRecursively(childNode, castShadow);
-  }
-}
-
-void Model::UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow)
-{
-  if(!node)
-  {
-    return;
-  }
-
-  GetImplementation(node).ReceiveShadow(receiveShadow);
-  uint32_t childrenCount = node.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
-    if(!childNode)
-    {
-      continue;
-    }
-    UpdateReceiveShadowRecursively(childNode, receiveShadow);
-  }
-}
-
-void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
-{
-  if(!node)
-  {
-    return;
-  }
-
-  GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
-  uint32_t childrenCount = node.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
-    if(!childNode)
-    {
-      continue;
-    }
-    UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
-  }
-}
-
-void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor)
-{
-  if(!node)
-  {
-    return;
-  }
-
-  GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
-
-  uint32_t childrenCount = node.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
-    if(!childNode)
-    {
-      continue;
-    }
-    UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor);
-  }
-}
-
 void Model::UpdateImageBasedLightTexture()
 {
   Dali::Texture currentDiffuseTexture          = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
@@ -1103,7 +985,7 @@ void Model::UpdateImageBasedLightTexture()
     currentIblSpecularMipmapLevels = 1u;
   }
 
-  UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
+  ModelNodeTreeUtility::UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
 }
 
 void Model::UpdateImageBasedLightScaleFactor()
@@ -1115,7 +997,7 @@ void Model::UpdateImageBasedLightScaleFactor()
   }
 
   float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-  UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor);
+  ModelNodeTreeUtility::UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor);
 }
 
 void Model::ApplyCameraTransform(Dali::CameraActor camera) const
@@ -1162,7 +1044,7 @@ void Model::NotifyShadowMapTexture(Dali::Texture shadowMapTexture)
   if(mShadowMapTexture != shadowMapTexture)
   {
     mShadowMapTexture = shadowMapTexture;
-    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+    ModelNodeTreeUtility::UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
   }
 }
 
@@ -1231,7 +1113,7 @@ void Model::OnModelLoadComplete()
 
   if(mShadowMapTexture)
   {
-    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+    ModelNodeTreeUtility::UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
   }
   UpdateImageBasedLightTexture();
   UpdateImageBasedLightScaleFactor();
index d82fa8f..e174394 100644 (file)
@@ -33,6 +33,7 @@
 #include <dali-scene3d/internal/common/environment-map-load-task.h>
 #include <dali-scene3d/internal/common/light-observer.h>
 #include <dali-scene3d/internal/common/model-load-task.h>
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
 #include <dali-scene3d/internal/model-components/model-node-impl.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
@@ -51,7 +52,7 @@ namespace Internal
 /**
  * @brief Impl class for Model.
  */
-class Model : public Dali::Toolkit::Internal::Control, public LightObserver
+class Model : public Dali::Toolkit::Internal::Control, public LightObserver, public Dali::Scene3D::Collidable
 {
 public:
   using AnimationData          = std::pair<std::string, Dali::Animation>;
@@ -315,26 +316,6 @@ private:
   void FitModelPosition();
 
   /**
-   * @brief Makes the input node cast shadow or not.
-   */
-  void UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow);
-
-  /**
-   * @brief Makes the input node receive shadow or not.
-   */
-  void UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow);
-
-  /**
-   * @brief Changes IBL information of the input node.
-   */
-  void UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels);
-
-  /**
-   * @brief Changes IBL factor of the input node.
-   */
-  void UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor);
-
-  /**
    * @brief Changes IBL textures of the input node.
    */
   void UpdateImageBasedLightTexture();
diff --git a/dali-scene3d/internal/controls/panel/panel-impl.cpp b/dali-scene3d/internal/controls/panel/panel-impl.cpp
new file mode 100644 (file)
index 0000000..c84fd49
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/internal/controls/panel/panel-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/math/math-utils.h>
+#include <dali/public-api/object/type-registry-helper.h>
+#include <dali/public-api/object/type-registry.h>
+#include <filesystem>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/image-resource-loader.h>
+#include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
+#include <dali-scene3d/internal/light/light-impl.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
+#include <dali-scene3d/internal/model-components/model-node-tree-utility.h>
+#include <dali-scene3d/public-api/controls/panel/panel.h>
+#include <dali-scene3d/public-api/loader/light-parameters.h>
+#include <dali-scene3d/public-api/loader/shader-manager.h>
+
+using namespace Dali;
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+namespace
+{
+/**
+ * Creates control through type registry
+ */
+BaseHandle Create()
+{
+  return Scene3D::Panel::New();
+}
+
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Panel, Toolkit::Control, Create);
+DALI_PROPERTY_REGISTRATION(Scene3D, Panel, "Transparent", BOOLEAN, TRANSPARENT)
+DALI_PROPERTY_REGISTRATION(Scene3D, Panel, "DoubleSided", BOOLEAN, DOUBLE_SIDED)
+DALI_PROPERTY_REGISTRATION(Scene3D, Panel, "UseBackFacePlane", BOOLEAN, USE_BACK_FACE_PLANE)
+DALI_PROPERTY_REGISTRATION(Scene3D, Panel, "BackFacePlaneColor", VECTOR3, BACK_FACE_PLANE_COLOR)
+DALI_TYPE_REGISTRATION_END()
+
+static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
+
+constexpr int32_t PANEL_ORDER_INDEX = 90; // It should be lower value than SceneView's first RenderTask's value.
+
+Dali::Geometry CreatePlaneGeometry(bool flip = false)
+{
+  Property::Map texturedQuadVertexFormat;
+  texturedQuadVertexFormat["aPosition"]    = Property::VECTOR3;
+  texturedQuadVertexFormat["aNormal"]      = Property::VECTOR3;
+  texturedQuadVertexFormat["aTexCoord"]    = Property::VECTOR2;
+  texturedQuadVertexFormat["aVertexColor"] = Property::VECTOR4;
+
+  VertexBuffer vertexData   = VertexBuffer::New(texturedQuadVertexFormat);
+  const float  halfQuadSize = .5f;
+  struct TexturedQuadVertex
+  {
+    Vector3 position;
+    Vector3 normal;
+    Vector2 textureCoordinates;
+    Vector4 color;
+  };
+  TexturedQuadVertex texturedQuadVertexData[4] = {
+    {Vector3(-halfQuadSize, -halfQuadSize, 0.0f), Vector3(0.0f, 0.0f, 1.0f), Vector2(flip ? 1.0f : 0.0f, 0.0f), Vector4::ONE},
+    {Vector3(halfQuadSize, -halfQuadSize, 0.0f), Vector3(0.0f, 0.0f, 1.0f), Vector2(flip ? 0.0f : 1.0f, 0.0f), Vector4::ONE},
+    {Vector3(-halfQuadSize, halfQuadSize, 0.0f), Vector3(0.0f, 0.0f, 1.0f), Vector2(flip ? 1.0f : 0.0f, 1.0f), Vector4::ONE},
+    {Vector3(halfQuadSize, halfQuadSize, 0.0f), Vector3(0.0f, 0.0f, 1.0f), Vector2(flip ? 0.0f : 1.0f, 1.0f), Vector4::ONE}};
+  vertexData.SetData(texturedQuadVertexData, 4);
+
+  Geometry geometry = Geometry::New();
+  geometry.AddVertexBuffer(vertexData);
+  unsigned short indexData[6] = {0, 1, 3, 0, 3, 2};
+  geometry.SetIndexBuffer(indexData, sizeof(indexData) / sizeof(indexData[0]));
+  geometry.SetType(Dali::Geometry::TRIANGLES);
+
+  return geometry;
+}
+
+} // anonymous namespace
+
+Panel::Panel()
+: Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
+  mPanelResolution(Vector2::ZERO),
+  mResolutionPropertyIndex(Property::INVALID_INDEX),
+  mIsTransparent(false),
+  mIsDoubleSided(false),
+  mIsUsingBackFacePlane(true),
+  mBackFacePlaneColor(Vector3::ONE),
+  mShaderManager(new Scene3D::Loader::ShaderManager()),
+  mSceneIblScaleFactor(1.0f),
+  mSceneSpecularMipmapLevels(1u),
+  mIsShadowCasting(true),
+  mIsShadowReceiving(true)
+{
+}
+
+Panel::~Panel()
+{
+}
+
+Dali::Scene3D::Panel Panel::New()
+{
+  Panel* impl = new Panel();
+
+  Dali::Scene3D::Panel handle = Dali::Scene3D::Panel(*impl);
+
+  // Second-phase init of the implementation
+  // This can only be done after the CustomActor connection has been made...
+  impl->Initialize();
+
+  return handle;
+}
+
+void Panel::SetPanelResolution(const Vector2& resolution)
+{
+  if(resolution.width != mPanelResolution.width || resolution.height != mPanelResolution.height)
+  {
+    mPanelResolution         = resolution;
+    mResolutionPropertyIndex = mPanelNode.RegisterProperty("resolution", mPanelResolution);
+    UpdateRenderTask();
+  }
+}
+
+Vector2 Panel::GetPanelResolution() const
+{
+  return mPanelResolution;
+}
+
+void Panel::SetContent(Dali::Actor rootActor)
+{
+  ClearPanel();
+  mRootLayer.Add(rootActor);
+}
+
+Dali::Actor Panel::GetContent() const
+{
+  return (mRootLayer.GetChildCount() > 1) ? ((mRootLayer.GetChildAt(0u) == mCamera) ? mRootLayer.GetChildAt(1u) : mRootLayer.GetChildAt(0u)) : Dali::Actor();
+}
+
+void Panel::ClearPanel()
+{
+  // CameraActor is needed to be left.
+  while(mRootLayer.GetChildCount() > 0u)
+  {
+    Dali::Actor child = mRootLayer.GetChildAt(0u);
+    child.Unparent();
+  }
+
+  if(mCamera)
+  {
+    mRootLayer.Add(mCamera);
+  }
+}
+
+void Panel::CastShadow(bool castShadow)
+{
+  mIsShadowCasting = castShadow;
+  ModelNodeTreeUtility::UpdateCastShadowRecursively(mPanelNode, mIsShadowCasting);
+}
+
+bool Panel::IsShadowCasting() const
+{
+  return mIsShadowCasting;
+}
+
+void Panel::ReceiveShadow(bool receiveShadow)
+{
+  mIsShadowReceiving = receiveShadow;
+  ModelNodeTreeUtility::UpdateReceiveShadowRecursively(mPanelNode, mIsShadowReceiving);
+}
+
+bool Panel::IsShadowReceiving() const
+{
+  return mIsShadowReceiving;
+}
+
+void Panel::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
+{
+  Scene3D::Panel panel = Scene3D::Panel::DownCast(Dali::BaseHandle(object));
+
+  if(panel)
+  {
+    Panel& panelImpl(GetImpl(panel));
+
+    switch(index)
+    {
+      case Scene3D::Panel::Property::TRANSPARENT:
+      {
+        bool transparent = value.Get<bool>();
+        panelImpl.SetTransparent(transparent);
+        break;
+      }
+      case Scene3D::Panel::Property::DOUBLE_SIDED:
+      {
+        bool doubleSided = value.Get<bool>();
+        panelImpl.SetDoubleSided(doubleSided);
+        break;
+      }
+      case Scene3D::Panel::Property::USE_BACK_FACE_PLANE:
+      {
+        bool useBackFacePlane = value.Get<bool>();
+        panelImpl.SetUseBackFacePlane(useBackFacePlane);
+        break;
+      }
+      case Scene3D::Panel::Property::BACK_FACE_PLANE_COLOR:
+      {
+        Vector3 backFacePlaneColor = value.Get<Vector3>();
+        panelImpl.SetBackFacePlaneColor(backFacePlaneColor);
+        break;
+      }
+    }
+  }
+}
+
+Property::Value Panel::GetProperty(BaseObject* object, Property::Index index)
+{
+  Property::Value value;
+  Scene3D::Panel  panel = Scene3D::Panel::DownCast(Dali::BaseHandle(object));
+
+  if(panel)
+  {
+    Panel& panelImpl(GetImpl(panel));
+
+    switch(index)
+    {
+      case Scene3D::Panel::Property::TRANSPARENT:
+      {
+        value = panelImpl.IsTransparent();
+        break;
+      }
+      case Scene3D::Panel::Property::DOUBLE_SIDED:
+      {
+        value = panelImpl.IsDoubleSided();
+        break;
+      }
+      case Scene3D::Panel::Property::USE_BACK_FACE_PLANE:
+      {
+        value = panelImpl.IsUsingBackFacePlane();
+        break;
+      }
+      case Scene3D::Panel::Property::BACK_FACE_PLANE_COLOR:
+      {
+        value = panelImpl.GetBackFacePlaneColor();
+        break;
+      }
+    }
+  }
+  return value;
+}
+
+///////////////////////////////////////////////////////////
+//
+// Private methods
+//
+
+void Panel::OnInitialize()
+{
+  // Make ParentOrigin as Center.
+  Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+  mDefaultDiffuseTexture  = ImageResourceLoader::GetEmptyTextureWhiteRGB();
+  mDefaultSpecularTexture = ImageResourceLoader::GetEmptyTextureWhiteRGB();
+
+  Actor self = Self();
+  mRootLayer = Layer::New();
+  mRootLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_UI);
+  // The models in the SceneView should be have independent coordinate with DALi default coordinate.
+  mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_POSITION, false);
+  mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_ORIENTATION, false);
+  mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_SCALE, false);
+  self.Add(mRootLayer);
+
+  mPanelNode = Scene3D::ModelNode::New();
+  mPanelNode.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
+  mPanelNode.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mPanelNode.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  self.Add(mPanelNode);
+
+  if(!mGeometry)
+  {
+    mGeometry = CreatePlaneGeometry();
+  }
+
+  mContentPlaneNode = Scene3D::ModelNode::New();
+  mContentPlaneNode.SetProperty(Dali::Actor::Property::SIZE, Vector2::ONE);
+  mContentPlaneNode.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mContentPlaneNode.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  mPanelNode.Add(mContentPlaneNode);
+
+  mContentPlaneMaterial                         = Scene3D::Material::New();
+  Scene3D::ModelPrimitive ContentPlanePrimitive = Scene3D::ModelPrimitive::New();
+  ContentPlanePrimitive.SetMaterial(mContentPlaneMaterial);
+  ContentPlanePrimitive.SetGeometry(mGeometry);
+  mContentPlaneNode.AddModelPrimitive(ContentPlanePrimitive);
+
+  // Back Plane
+  mBackPlaneNode = Scene3D::ModelNode::New();
+  mBackPlaneNode.SetProperty(Dali::Actor::Property::SIZE, Vector2::ONE);
+  mBackPlaneNode.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mBackPlaneNode.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  mBackPlaneNode.SetProperty(Dali::Actor::Property::ORIENTATION, Quaternion(Dali::Radian(Dali::ANGLE_180), Vector3::YAXIS));
+  mPanelNode.Add(mBackPlaneNode);
+
+  mBackPlaneMaterial                         = Scene3D::Material::New();
+  Scene3D::ModelPrimitive backPlanePrimitive = Scene3D::ModelPrimitive::New();
+  backPlanePrimitive.SetMaterial(mBackPlaneMaterial);
+  backPlanePrimitive.SetGeometry(mGeometry);
+  mBackPlaneNode.AddModelPrimitive(backPlanePrimitive);
+
+  // Double Sided Plane
+  if(!mDoubleSidedGeometry)
+  {
+    mDoubleSidedGeometry = CreatePlaneGeometry(true);
+  }
+
+  mDoubleSidedPlaneNode = Scene3D::ModelNode::New();
+  mDoubleSidedPlaneNode.SetProperty(Dali::Actor::Property::SIZE, Vector2::ONE);
+  mDoubleSidedPlaneNode.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mDoubleSidedPlaneNode.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  mDoubleSidedPlaneNode.SetProperty(Dali::Actor::Property::ORIENTATION, Quaternion(Dali::Radian(Dali::ANGLE_180), Vector3::YAXIS));
+  mPanelNode.Add(mDoubleSidedPlaneNode);
+
+  Scene3D::ModelPrimitive doubleSidedPlanePrimitive = Scene3D::ModelPrimitive::New();
+  doubleSidedPlanePrimitive.SetMaterial(mContentPlaneMaterial);
+  doubleSidedPlanePrimitive.SetGeometry(mDoubleSidedGeometry);
+  mDoubleSidedPlaneNode.AddModelPrimitive(doubleSidedPlanePrimitive);
+
+  mResolutionPropertyIndex   = mPanelNode.RegisterProperty("resolution", mPanelResolution);
+  Constraint scaleConstraint = Constraint::New<Vector3>(mPanelNode, Dali::Actor::Property::SCALE, [](Vector3& output, const PropertyInputContainer& inputs)
+                                                        {
+    Vector3 panelSize = inputs[0]->GetVector3();
+    Vector2 panelResolution = inputs[1]->GetVector2();
+    output = Y_DIRECTION;
+    if((EqualsZero(panelSize.width) || EqualsZero(panelSize.height)) ||
+       (EqualsZero(panelResolution.width) || EqualsZero(panelResolution.height)))
+    {
+      return;
+    }
+
+    float aspectPanelSize = panelSize.width / panelSize.height;
+    float aspectPanelResolution = panelResolution.width / panelResolution.height;
+    if(aspectPanelResolution < aspectPanelSize)
+    {
+      float scaleFactor = panelSize.height / panelResolution.height;
+      output.x = panelResolution.width * scaleFactor;
+      output.y = panelSize.height;
+    }
+    else
+    {
+      float scaleFactor = panelSize.width / panelResolution.width;
+      output.x = panelSize.width;
+      output.y = panelResolution.height * scaleFactor;
+    }
+    output = output * Y_DIRECTION; });
+  scaleConstraint.AddSource(Source{self, Dali::Actor::Property::SIZE});
+  scaleConstraint.AddSource(Source{mPanelNode, mResolutionPropertyIndex});
+  scaleConstraint.Apply();
+
+  UpdateProperties();
+}
+
+void Panel::OnSceneConnection(int depth)
+{
+  Actor parent = Self().GetParent();
+  while(parent)
+  {
+    // If this Panel has parent SceneView and the its ShaderManager is same with privious ShaderManager,
+    // this Panel don't need to update shader.
+    Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
+    if(sceneView)
+    {
+      mParentSceneView = sceneView;
+      GetImpl(sceneView).RegisterSceneItem(this);
+      Scene3D::Loader::ShaderManagerPtr shaderManager = GetImpl(sceneView).GetShaderManager();
+      if(mShaderManager != shaderManager)
+      {
+        mShaderManager = shaderManager;
+        ModelNodeTreeUtility::UpdateShaderRecursively(mPanelNode, mShaderManager);
+      }
+      break;
+    }
+    parent = parent.GetParent();
+  }
+
+  // On-screen / Off-screen window
+  mSceneHolder = Integration::SceneHolder::Get(Self());
+  if(mSceneHolder && !mRenderTask)
+  {
+    RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+    mRenderTask             = taskList.CreateTask();
+    mRenderTask.SetSourceActor(mRootLayer);
+    mRenderTask.SetExclusive(true);
+    mRenderTask.SetInputEnabled(true);
+    mRenderTask.SetCullMode(true);
+    mRenderTask.SetOrderIndex(PANEL_ORDER_INDEX);
+    mRenderTask.SetScreenToFrameBufferMappingActor(mPanelNode);
+
+    UpdateRenderTask();
+  }
+
+  Control::OnSceneConnection(depth);
+}
+
+void Panel::OnSceneDisconnection()
+{
+  // If mParentSceneView is still onScene, that means this model
+  // is disconnected from mParentSceneView's sub tree.
+  // So, Unregister this Panel from SceneView.
+  Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
+  if(sceneView && sceneView.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+  {
+    GetImpl(sceneView).UnregisterSceneItem(this);
+    mParentSceneView.Reset();
+  }
+
+  if(mSceneHolder)
+  {
+    if(mRenderTask)
+    {
+      RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+      taskList.RemoveTask(mRenderTask);
+      mRenderTask.Reset();
+    }
+    mSceneHolder.Reset();
+  }
+  mTexture.Reset();
+  mFrameBuffer.Reset();
+
+  Control::OnSceneDisconnection();
+}
+
+void Panel::SetTransparent(bool transparent)
+{
+  if(mIsTransparent != transparent)
+  {
+    mIsTransparent = transparent;
+    UpdateProperties();
+  }
+}
+
+bool Panel::IsTransparent() const
+{
+  return mIsTransparent;
+}
+
+void Panel::SetDoubleSided(bool doubleSided)
+{
+  if(mIsDoubleSided != doubleSided)
+  {
+    mIsDoubleSided = doubleSided;
+    UpdateProperties();
+  }
+}
+
+bool Panel::IsDoubleSided() const
+{
+  return mIsDoubleSided;
+}
+
+void Panel::SetUseBackFacePlane(bool useBackFacePlane)
+{
+  if(mIsUsingBackFacePlane != useBackFacePlane)
+  {
+    mIsUsingBackFacePlane = useBackFacePlane;
+    UpdateProperties();
+  }
+}
+
+bool Panel::IsUsingBackFacePlane() const
+{
+  return mIsUsingBackFacePlane;
+}
+
+void Panel::SetBackFacePlaneColor(Vector3 backFacePlaneColor)
+{
+  if(mBackFacePlaneColor != backFacePlaneColor)
+  {
+    mBackFacePlaneColor     = backFacePlaneColor;
+    Vector4 baseColorFactor = Vector4(mBackFacePlaneColor);
+    baseColorFactor.a       = 1.0f;
+    mBackPlaneMaterial.SetProperty(Dali::Scene3D::Material::Property::BASE_COLOR_FACTOR, baseColorFactor);
+  }
+}
+
+Vector3 Panel::GetBackFacePlaneColor() const
+{
+  return mBackFacePlaneColor;
+}
+
+void Panel::UpdateProperties()
+{
+  if(mRenderTask)
+  {
+    mRenderTask.SetClearColor(mIsTransparent ? Color::TRANSPARENT : Color::WHITE);
+  }
+
+  if(mContentPlaneMaterial)
+  {
+    Dali::Scene3D::Material::AlphaModeType ContentPlaneAlphaMode = mIsTransparent ? Dali::Scene3D::Material::AlphaModeType::BLEND : Dali::Scene3D::Material::AlphaModeType::OPAQUE;
+    mContentPlaneMaterial.SetProperty(Dali::Scene3D::Material::Property::ALPHA_MODE, ContentPlaneAlphaMode);
+  }
+
+  bool isBackPlaneVisible = !mIsTransparent && mIsUsingBackFacePlane;
+  if(mBackPlaneNode)
+  {
+    mBackPlaneNode.SetProperty(Dali::Actor::Property::VISIBLE, isBackPlaneVisible);
+  }
+
+  if(mDoubleSidedPlaneNode)
+  {
+    bool isDoubleSidedRenderable = mIsDoubleSided && !isBackPlaneVisible;
+    mDoubleSidedPlaneNode.SetProperty(Dali::Actor::Property::VISIBLE, isDoubleSidedRenderable);
+  }
+}
+
+void Panel::UpdateImageBasedLightTexture()
+{
+  Dali::Texture currentDiffuseTexture          = mSceneDiffuseTexture;
+  Dali::Texture currentSpecularTexture         = mSceneSpecularTexture;
+  float         currentIblScaleFactor          = mSceneIblScaleFactor;
+  uint32_t      currentIblSpecularMipmapLevels = mSceneSpecularMipmapLevels;
+
+  if(!currentDiffuseTexture || !currentSpecularTexture)
+  {
+    currentDiffuseTexture          = mDefaultDiffuseTexture;
+    currentSpecularTexture         = mDefaultSpecularTexture;
+    currentIblScaleFactor          = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
+    currentIblSpecularMipmapLevels = 1u;
+  }
+
+  ModelNodeTreeUtility::UpdateImageBasedLightTextureRecursively(mPanelNode, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
+}
+
+void Panel::UpdateImageBasedLightScaleFactor()
+{
+  if(!mSceneDiffuseTexture || !mSceneSpecularTexture)
+  {
+    return;
+  }
+
+  ModelNodeTreeUtility::UpdateImageBasedLightScaleFactorRecursively(mPanelNode, mSceneIblScaleFactor);
+}
+
+void Panel::NotifyShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+  if(mShadowMapTexture != shadowMapTexture)
+  {
+    mShadowMapTexture = shadowMapTexture;
+    ModelNodeTreeUtility::UpdateShadowMapTextureRecursively(mPanelNode, mShadowMapTexture);
+  }
+}
+
+void Panel::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels)
+{
+  if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
+  {
+    mSceneDiffuseTexture       = diffuseTexture;
+    mSceneSpecularTexture      = specularTexture;
+    mSceneIblScaleFactor       = scaleFactor;
+    mSceneSpecularMipmapLevels = specularMipmapLevels;
+
+    UpdateImageBasedLightTexture();
+  }
+}
+
+void Panel::NotifyImageBasedLightScaleFactor(float scaleFactor)
+{
+  mSceneIblScaleFactor = scaleFactor;
+  if(mSceneDiffuseTexture && mSceneSpecularTexture)
+  {
+    UpdateImageBasedLightScaleFactor();
+  }
+}
+
+void Panel::UpdateRenderTask()
+{
+  if(mPanelResolution.x <= 0.0f || mPanelResolution.y <= 0.0f)
+  {
+    return;
+  }
+
+  if(mRenderTask)
+  {
+    if(mCamera)
+    {
+      mCamera.Unparent();
+      mCamera.Reset();
+    }
+
+    if(mTexture)
+    {
+      mTexture.Reset();
+    }
+
+    if(mFrameBuffer)
+    {
+      mFrameBuffer.Reset();
+    }
+
+    mCamera = Dali::CameraActor::New(mPanelResolution);
+    mCamera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+    mCamera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+    mCamera.SetProperty(Dali::Actor::Property::POSITION_X, mPanelResolution.x / 2.0f);
+    mCamera.SetProperty(Dali::Actor::Property::POSITION_Y, mPanelResolution.y / 2.0f);
+    mRootLayer.Add(mCamera);
+
+    mRootLayer.SetProperty(Dali::Actor::Property::SIZE, mPanelResolution);
+
+    mTexture     = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, mPanelResolution.width, mPanelResolution.height);
+    mFrameBuffer = Dali::FrameBuffer::New(mTexture.GetWidth(), mTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH_STENCIL);
+    mFrameBuffer.AttachColorTexture(mTexture);
+
+    mRenderTask.SetCameraActor(mCamera);
+    mRenderTask.SetFrameBuffer(mFrameBuffer);
+    mRenderTask.SetClearEnabled(true);
+    mRenderTask.SetClearColor(mIsTransparent ? Color::TRANSPARENT : Color::WHITE);
+
+    mContentPlaneMaterial.SetTexture(Dali::Scene3D::Material::TextureType::BASE_COLOR, mTexture);
+  }
+}
+
+} // namespace Internal
+} // namespace Scene3D
+} // namespace Dali
diff --git a/dali-scene3d/internal/controls/panel/panel-impl.h b/dali-scene3d/internal/controls/panel/panel-impl.h
new file mode 100644 (file)
index 0000000..769d866
--- /dev/null
@@ -0,0 +1,305 @@
+#ifndef DALI_SCENE3D_INTERNAL_PANEL_H
+#define DALI_SCENE3D_INTERNAL_PANEL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
+#include <dali/public-api/actors/layer.h>
+#include <dali/public-api/object/weak-handle.h>
+#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/rendering/frame-buffer.h>
+#include <dali/public-api/rendering/texture.h>
+#include <unordered_map>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/environment-map-load-task.h>
+#include <dali-scene3d/internal/common/light-observer.h>
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
+#include <dali-scene3d/public-api/controls/panel/panel.h>
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali-scene3d/public-api/light/light.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+class Panel;
+
+namespace Internal
+{
+/**
+ * @brief Impl class for Panel.
+ */
+class Panel : public Dali::Toolkit::Internal::Control, public LightObserver
+{
+public:
+
+  /**
+   * @copydoc Panel::New()
+   */
+  static Dali::Scene3D::Panel New();
+
+  /**
+   * @copydoc Panel::SetPanelResolution()
+   */
+  void SetPanelResolution(const Vector2& resolution);
+
+  /**
+   * @copydoc Panel::GetPanelResolution()
+   */
+  Vector2 GetPanelResolution() const;
+
+  /**
+   * @copydoc Panel::SetContent()
+   */
+  void SetContent(Dali::Actor rootActor);
+
+  /**
+   * @copydoc Panel::GetContent()
+   */
+  Dali::Actor GetContent() const;
+
+  /**
+   * @copydoc Panel::ClearPanel()
+   */
+  void ClearPanel();
+
+  /**
+   * @copydoc Panel::CastShadow()
+   */
+  void CastShadow(bool castShadow);
+
+  /**
+   * @copydoc Panel::IsShadowCasting()
+   */
+  bool IsShadowCasting() const;
+
+  /**
+   * @copydoc Panel::ReceiveShadow()
+   */
+  void ReceiveShadow(bool receiveShadow);
+
+  /**
+   * @copydoc Panel::IsShadowReceiving()
+   */
+  bool IsShadowReceiving() const;
+
+  // Properties
+
+  /**
+   * Called when a property of an object of this type is set.
+   * @param[in] object The object whose property is set.
+   * @param[in] index The property index.
+   * @param[in] value The new property value.
+   */
+  static void SetProperty(BaseObject* object, Property::Index index, const Property::Value& value);
+
+  /**
+   * Called to retrieve a property of an object of this type.
+   * @param[in] object The object whose property is to be retrieved.
+   * @param[in] index The property index.
+   * @return The current value of the property.
+   */
+  static Property::Value GetProperty(BaseObject* object, Property::Index index);
+
+protected:
+  /**
+   * @brief Constructs a new Panel.
+   */
+  Panel();
+
+  /**
+   * A reference counted object may only be deleted by calling Unreference()
+   */
+  virtual ~Panel();
+
+private:
+  /**
+   * @copydoc Toolkit::Control::OnInitialize
+   */
+  void OnInitialize();
+
+  /**
+   * @copydoc CustomActorImpl::OnSceneConnection()
+   */
+  void OnSceneConnection(int depth) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSceneDisconnection()
+   */
+  void OnSceneDisconnection() override;
+
+private:
+  /**
+   * @brief Sets whether the plane is transparent or not.
+   * @param[in] transparent True to make plane transparent.
+   */
+  void SetTransparent(bool transparent);
+
+  /**
+   * @brief Retrieves whether the plane is transparent or not.
+   * @return Ture if the plane is transparent.
+   */
+  bool IsTransparent() const;
+
+  /**
+   * @brief Sets whether the plane is double sided or not.
+   * @param[in] doubleSided True to make plane be rendered as double side.
+   */
+  void SetDoubleSided(bool doubleSided);
+
+  /**
+   * @brief Retrieves whether the plane is double sided or not.
+   * @return Ture if the plane is rendered as double side.
+   */
+  bool IsDoubleSided() const;
+
+  /**
+   * @brief Sets whether the Panel has back face plane or not.
+   * @param[in] useBackFacePlane True to use back face plane.
+   */
+  void SetUseBackFacePlane(bool useBackFacePlane);
+
+  /**
+   * @brief Retrieves whether the Panel has back face plane or not.
+   * @return Ture if the Panel has back face plane.
+   */
+  bool IsUsingBackFacePlane() const;
+
+  /**
+   * @brief Sets color of back face plane.
+   * @param[in] backFacePlaneColor Vector3 color of the back face plane.
+   */
+  void SetBackFacePlaneColor(Vector3 backFacePlaneColor);
+
+  /**
+   * @brief Retrieves color of back face plane.
+   * @return back face plane color.
+   */
+  Vector3 GetBackFacePlaneColor() const;
+
+  /**
+   * @brief Update plane's properties.
+   */
+  void UpdateProperties();
+
+  /**
+   * @brief Changes IBL textures of the input node.
+   */
+  void UpdateImageBasedLightTexture();
+
+  /**
+   * @brief Changes IBL scale factor of the input node.
+   */
+  void UpdateImageBasedLightScaleFactor();
+
+public: // Overrides LightObserver Methods.
+  /**
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyShadowMapTexture()
+   */
+  void NotifyShadowMapTexture(Dali::Texture shadowMapTexture) override;
+
+  /**
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyImageBasedLightTexture()
+   */
+  void NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels) override;
+
+  /**
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyImageBasedLightScaleFactor()
+   */
+  void NotifyImageBasedLightScaleFactor(float scaleFactor) override;
+
+private:
+  /**
+   * @brief Update model root scale when Panel size property is updated.
+   */
+  void OnSizeNotification(Dali::PropertyNotification& source);
+
+  /**
+   * @brief Updates RenderTask apply new panel size
+   */
+  void UpdateRenderTask();
+
+private:
+  Vector2                        mPanelResolution;
+  Scene3D::ModelNode             mPanelNode;
+  Scene3D::ModelNode             mContentPlaneNode;
+  Scene3D::ModelNode             mBackPlaneNode;
+  Scene3D::ModelNode             mDoubleSidedPlaneNode;
+  Dali::Layer                    mRootLayer;
+  Integration::SceneHolder       mSceneHolder;
+  CameraActor                    mCamera;
+  Dali::FrameBuffer              mFrameBuffer;
+  Dali::Texture                  mTexture;
+  Scene3D::Material              mContentPlaneMaterial;
+  Scene3D::Material              mBackPlaneMaterial;
+  Scene3D::Material              mDoubleSidedPlaneMaterial;
+  Dali::Geometry                 mGeometry;
+  Dali::Geometry                 mDoubleSidedGeometry;
+  Dali::RenderTask               mRenderTask;
+  WeakHandle<Scene3D::SceneView> mParentSceneView;
+
+  Property::Index mResolutionPropertyIndex;
+
+  bool    mIsTransparent : 1;        /// Checks whether the panel background is transparent or not. defalut is {false}
+  bool    mIsDoubleSided : 1;        /// Checkes whether the panel rendered on double sided or not. default is {false}.
+  bool    mIsUsingBackFacePlane : 1; /// Checked whether the panel uses opaque back face plane or not. default is {true}.
+  Vector3 mBackFacePlaneColor;
+
+  Dali::Scene3D::Loader::ShaderManagerPtr mShaderManager;
+
+  // IBL
+  Dali::Texture mDefaultSpecularTexture;
+  Dali::Texture mDefaultDiffuseTexture;
+  Dali::Texture mSceneSpecularTexture;
+  Dali::Texture mSceneDiffuseTexture;
+  float         mSceneIblScaleFactor;
+  uint32_t      mSceneSpecularMipmapLevels;
+
+  // Shadow
+  Dali::Texture mShadowMapTexture;
+  bool          mIsShadowCasting;
+  bool          mIsShadowReceiving;
+};
+
+} // namespace Internal
+
+// Helpers for public-api forwarding methods
+inline Dali::Scene3D::Internal::Panel& GetImpl(Dali::Scene3D::Panel& obj)
+{
+  DALI_ASSERT_ALWAYS(obj);
+  Dali::RefObject& handle = obj.GetImplementation();
+  return static_cast<Dali::Scene3D::Internal::Panel&>(handle);
+}
+
+inline const Dali::Scene3D::Internal::Panel& GetImpl(const Dali::Scene3D::Panel& obj)
+{
+  DALI_ASSERT_ALWAYS(obj);
+  const Dali::RefObject& handle = obj.GetImplementation();
+  return static_cast<const Dali::Scene3D::Internal::Panel&>(handle);
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_INTERNAL_PANEL_H
index 7fc62c1..f570010 100644 (file)
@@ -25,7 +25,9 @@
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
 #include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali-toolkit/public-api/image-loader/image.h>
+#include <dali/devel-api/actors/actor-devel.h>
 #include <dali/devel-api/actors/camera-actor-devel.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/rendering/frame-buffer-devel.h>
@@ -42,8 +44,6 @@
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-scene3d/internal/light/light-impl.h>
 
-#include <dali/integration-api/debug.h>
-
 using namespace Dali;
 
 namespace Dali
@@ -61,19 +61,19 @@ BaseHandle Create()
 
 // Setup properties, signals and actions using the type-registry.
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::SceneView, Toolkit::Control, Create);
-
 DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "AlphaMaskUrl", STRING, ALPHA_MASK_URL)
 DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "MaskContentScale", FLOAT, MASK_CONTENT_SCALE)
 DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "CropToMask", BOOLEAN, CROP_TO_MASK)
 DALI_TYPE_REGISTRATION_END()
 
 Property::Index    RENDERING_BUFFER        = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
-constexpr int32_t  DEFAULT_ORIENTATION     = 0;
-constexpr int32_t  INVALID_INDEX           = -1;
-constexpr uint32_t MAXIMUM_SIZE_SHADOW_MAP = 2048;
+static constexpr float    MIM_CAPTURE_SIZE        = 1.0f;
+static constexpr int32_t  DEFAULT_ORIENTATION     = 0;
+static constexpr int32_t  INVALID_INDEX           = -1;
+static constexpr uint32_t MAXIMUM_SIZE_SHADOW_MAP = 2048;
 
-constexpr int32_t SCENE_ORDER_INDEX  = 100;
-constexpr int32_t SHADOW_ORDER_INDEX = 99;
+static constexpr int32_t SCENE_ORDER_INDEX  = 100;
+static constexpr int32_t SHADOW_ORDER_INDEX = 99;
 
 static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity";
 static constexpr std::string_view Y_FLIP_MASK_TEXTURE     = "uYFlipMaskTexture";
@@ -171,7 +171,8 @@ void SetShadowLightConstraint(Dali::CameraActor selectedCamera, Dali::CameraActo
 
   // Compute ViewProjectionMatrix and store it to "tempViewProjectionMatrix" property
   auto       tempViewProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempViewProjectionMatrix", Matrix::IDENTITY);
-  Constraint projectionMatrixConstraint    = Constraint::New<Matrix>(shadowLightCamera, tempViewProjectionMatrixIndex, [](Matrix& output, const PropertyInputContainer& inputs) {
+  Constraint projectionMatrixConstraint    = Constraint::New<Matrix>(shadowLightCamera, tempViewProjectionMatrixIndex, [](Matrix& output, const PropertyInputContainer& inputs)
+                                                                  {
     Matrix worldMatrix  = inputs[0]->GetMatrix();
     float  tangentFov_2 = tanf(inputs[4]->GetFloat());
     float  nearDistance = inputs[5]->GetFloat();
@@ -284,8 +285,7 @@ void SetShadowLightConstraint(Dali::CameraActor selectedCamera, Dali::CameraActo
     projMatrix[14] = -(near + far) / deltaZ;
     projMatrix[15] = 1.0f;
 
-    output = output * shadowCameraViewMatrix;
-  });
+    output = output * shadowCameraViewMatrix; });
   projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::Actor::Property::WORLD_MATRIX});
   projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::PROJECTION_MODE});
   projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::DevelCameraActor::Property::PROJECTION_DIRECTION});
@@ -298,6 +298,31 @@ void SetShadowLightConstraint(Dali::CameraActor selectedCamera, Dali::CameraActo
   projectionMatrixConstraint.ApplyPost();
 }
 
+bool CheckInside(Actor root, Actor actor)
+{
+  Actor currentActor = actor;
+  while(currentActor)
+  {
+    if(currentActor == root)
+    {
+      return true;
+      break;
+    }
+    currentActor = currentActor.GetParent();
+  }
+  return false;
+}
+
+void ConvertFovFromVerticalToHorizontal(float aspect, float& fov)
+{
+  fov = 2.0f * (float)std::atan(std::tan(fov * 0.5f) * aspect);
+}
+
+void ConvertFovFromHorizontalToVertical(float aspect, float& fov)
+{
+  fov = 2.0f * (float)std::atan(std::tan(fov * 0.5f) / aspect);
+}
+
 } // anonymous namespace
 
 SceneView::SceneView()
@@ -306,6 +331,7 @@ SceneView::SceneView()
   mSkybox(),
   mSkyboxOrientation(Quaternion()),
   mSkyboxIntensity(1.0f),
+  mFailedCaptureCallbacks(nullptr),
   mLightObservers(),
   mShaderManager(new Scene3D::Loader::ShaderManager())
 {
@@ -330,6 +356,28 @@ SceneView::~SceneView()
       Dali::AsyncTaskManager::Get().RemoveTask(mSkyboxLoadTask);
       mSkyboxLoadTask.Reset();
     }
+    mSelectedCamera.OffSceneSignal().Disconnect(this, &SceneView::OnCameraDisconnected);
+
+    if(mInCameraTransition)
+    {
+      mTransitionAnimation.Stop();
+      ResetTransition();
+    }
+
+    for(auto&& capture : mCaptureContainer)
+    {
+      ResetCaptureData(capture.second);
+    }
+    mCaptureContainer.clear();
+    ResetCaptureTimer();
+
+    if(mFailedCaptureCallbacks && Adaptor::IsAvailable())
+    {
+      // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
+      Adaptor::Get().RemoveIdle(mFailedCaptureCallbacks);
+    }
+
+    Adaptor::Get().UnregisterProcessorOnce(*this);
 
     // Request image resource GC
     Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect();
@@ -395,7 +443,7 @@ uint32_t SceneView::GetCameraCount() const
 
 CameraActor SceneView::GetSelectedCamera() const
 {
-  return mSelectedCamera;
+  return (mInCameraTransition && mTransitionDestinationCamera) ? mTransitionDestinationCamera : mSelectedCamera;
 }
 
 CameraActor SceneView::GetCamera(uint32_t index) const
@@ -424,14 +472,34 @@ CameraActor SceneView::GetCamera(const std::string& name) const
 
 void SceneView::SelectCamera(uint32_t index)
 {
+  if(mInCameraTransition)
+  {
+    DALI_LOG_ERROR("Cannot change camera during Camera Transition.\n");
+    return;
+  }
   UpdateCamera(GetCamera(index));
 }
 
 void SceneView::SelectCamera(const std::string& name)
 {
+  if(mInCameraTransition)
+  {
+    DALI_LOG_ERROR("Cannot change camera during Camera Transition.\n");
+    return;
+  }
   UpdateCamera(GetCamera(name));
 }
 
+void SceneView::StartCameraTransition(uint32_t index, float durationSeconds, Dali::AlphaFunction alphaFunction)
+{
+  RegisterCameraTransition(GetCamera(index), durationSeconds, alphaFunction);
+}
+
+void SceneView::StartCameraTransition(std::string name, float durationSeconds, Dali::AlphaFunction alphaFunction)
+{
+  RegisterCameraTransition(GetCamera(name), durationSeconds, alphaFunction);
+}
+
 void SceneView::RegisterSceneItem(Scene3D::Internal::LightObserver* item)
 {
   if(item)
@@ -641,7 +709,8 @@ void SceneView::SetShadow(Scene3D::Light light)
     return;
   }
 
-  auto foundLight = std::find_if(mLights.begin(), mLights.end(), [light](std::pair<Scene3D::Light, bool> lightEntity) -> bool { return (lightEntity.second && lightEntity.first == light); });
+  auto foundLight = std::find_if(mLights.begin(), mLights.end(), [light](std::pair<Scene3D::Light, bool> lightEntity) -> bool
+                                 { return (lightEntity.second && lightEntity.first == light); });
 
   if(foundLight == mLights.end())
   {
@@ -847,6 +916,125 @@ Quaternion SceneView::GetSkyboxOrientation() const
   return mSkyboxOrientation;
 }
 
+int32_t SceneView::Capture(Dali::CameraActor camera, const Vector2& size)
+{
+  bool capturePossible = true;
+  if(size.x < MIM_CAPTURE_SIZE || size.y < MIM_CAPTURE_SIZE)
+  {
+    DALI_LOG_ERROR("The width and height should be positive.\n");
+    capturePossible = false;
+  }
+
+  uint32_t width = std::max(1u, unsigned(size.width));
+  uint32_t height = std::max(1u, unsigned(size.height));
+  if(width > Dali::GetMaxTextureSize() || height > Dali::GetMaxTextureSize())
+  {
+    DALI_LOG_ERROR("The input size is too large.\n");
+    capturePossible = false;
+  }
+
+  if(!mRootLayer.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+  {
+    DALI_LOG_ERROR("Current SceneView is not connected on scene tree\n");
+    capturePossible = false;
+  }
+
+  if(!capturePossible)
+  {
+    mFailedCaptureRequests.push_back(mCaptureId);
+    if(!mFailedCaptureCallbacks && DALI_LIKELY(Adaptor::IsAvailable()))
+    {
+      mFailedCaptureCallbacks = MakeCallback(this, &SceneView::OnCaptureFailedIdle);
+      if(!Adaptor::Get().AddIdle(mFailedCaptureCallbacks, false))
+      {
+        mFailedCaptureCallbacks = nullptr;
+      }
+    }
+  }
+  else
+  {
+    if(!camera.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+    {
+      mRootLayer.Add(camera);
+    }
+
+    std::shared_ptr<CaptureData> captureData = std::make_shared<CaptureData>();
+    captureData->mCaptureId                  = mCaptureId;
+    captureData->mCaptureTexture             = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, width, height);
+    captureData->mCaptureFrameBuffer         = Dali::FrameBuffer::New(captureData->mCaptureTexture.GetWidth(), captureData->mCaptureTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH_STENCIL);
+    DevelFrameBuffer::SetMultiSamplingLevel(captureData->mCaptureFrameBuffer, mFrameBufferMultiSamplingLevel);
+    captureData->mCaptureFrameBuffer.AttachColorTexture(captureData->mCaptureTexture);
+
+    captureData->mCaptureCamera                    = camera;
+    captureData->mCaptureCameraOriginalAspectRatio = captureData->mCaptureCamera.GetAspectRatio();
+    captureData->mCaptureCamera.SetAspectRatio((float)width / (float)height);
+
+    RenderTaskList taskList   = mSceneHolder.GetRenderTaskList();
+    captureData->mCaptureTask = taskList.CreateTask();
+    captureData->mCaptureTask.SetSourceActor(mRootLayer);
+    captureData->mCaptureTask.SetExclusive(true);
+    captureData->mCaptureTask.SetCullMode(false);
+    captureData->mCaptureTask.SetOrderIndex(SCENE_ORDER_INDEX + 1);
+    captureData->mCaptureTask.SetCameraActor(captureData->mCaptureCamera);
+    captureData->mCaptureTask.SetFrameBuffer(captureData->mCaptureFrameBuffer);
+    captureData->mCaptureTask.SetClearEnabled(true);
+    captureData->mCaptureTask.SetClearColor(Color::TRANSPARENT);
+    captureData->mCaptureTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
+
+    captureData->mCaptureInvertCamera = Dali::CameraActor::New(size);
+    captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+    captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+    captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::POSITION_X, size.x / 2.0f);
+    captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::POSITION_Y, size.y / 2.0f);
+
+    captureData->mCaptureUrl       = Dali::Toolkit::Image::GenerateUrl(captureData->mCaptureFrameBuffer, 0u);
+    captureData->mCaptureImageView = Dali::Toolkit::ImageView::New(captureData->mCaptureUrl.GetUrl());
+    captureData->mCaptureImageView.SetProperty(Dali::Actor::Property::SIZE, size);
+    captureData->mCaptureImageView.Add(captureData->mCaptureInvertCamera);
+
+    Window window = DevelWindow::Get(Self());
+    window.Add(captureData->mCaptureImageView);
+
+    captureData->mCaptureInvertTexture     = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, width, height);
+    captureData->mCaptureInvertFrameBuffer = Dali::FrameBuffer::New(captureData->mCaptureInvertTexture.GetWidth(), captureData->mCaptureInvertTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH_STENCIL);
+    captureData->mCaptureInvertFrameBuffer.AttachColorTexture(captureData->mCaptureInvertTexture);
+
+    captureData->mCaptureInvertTask = taskList.CreateTask();
+    captureData->mCaptureInvertTask.SetSourceActor(captureData->mCaptureImageView);
+    captureData->mCaptureInvertTask.SetExclusive(true);
+    captureData->mCaptureInvertTask.SetCullMode(false);
+    captureData->mCaptureInvertTask.SetOrderIndex(SCENE_ORDER_INDEX + 2);
+    captureData->mCaptureInvertTask.SetCameraActor(captureData->mCaptureInvertCamera);
+    captureData->mCaptureInvertTask.SetFrameBuffer(captureData->mCaptureInvertFrameBuffer);
+    captureData->mCaptureInvertTask.SetClearEnabled(true);
+    captureData->mCaptureInvertTask.SetClearColor(Color::TRANSPARENT);
+    captureData->mCaptureInvertTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
+    captureData->mCaptureInvertTask.FinishedSignal().Connect(this, &SceneView::OnCaptureFinished);
+
+    captureData->mStartTick = mTimerTickCount;
+
+    mCaptureContainer.push_back(std::make_pair(captureData->mCaptureInvertTask, captureData));
+
+    if(!mCaptureTimer)
+    {
+      mCaptureTimer = Dali::Timer::New(1000);
+      mCaptureTimer.TickSignal().Connect(this, &SceneView::OnTimeOut);
+      mCaptureTimer.Start();
+    }
+  }
+  return mCaptureId++;
+}
+
+Dali::Scene3D::SceneView::CaptureFinishedSignalType& SceneView::CaptureFinishedSignal()
+{
+  return mCaptureFinishedSignal;
+}
+
+Dali::Scene3D::SceneView::CameraTransitionFinishedSignalType& SceneView::CameraTransitionFinishedSignal()
+{
+  return mCameraTransitionFinishedSignal;
+}
+
 Dali::Scene3D::Loader::ShaderManagerPtr SceneView::GetShaderManager() const
 {
   return mShaderManager;
@@ -1013,6 +1201,13 @@ void SceneView::OnSceneConnection(int depth)
     UpdateRenderTask();
   }
 
+  CameraActor selectedCamera = GetSelectedCamera();
+  selectedCamera = selectedCamera ? selectedCamera : mDefaultCamera;
+  if(selectedCamera)
+  {
+    UpdateCamera(selectedCamera);
+  }
+
   Control::OnSceneConnection(depth);
 }
 
@@ -1027,6 +1222,23 @@ void SceneView::OnSceneDisconnection()
   }
   mWindow.Reset();
 
+  auto                                                                   self = Self();
+  Dali::Scene3D::SceneView                                               handle(Dali::Scene3D::SceneView::DownCast(self));
+  std::vector<std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>>> tempContainer(mCaptureContainer);
+  for(auto&& capture : tempContainer)
+  {
+    mCaptureFinishedSignal.Emit(handle, capture.second->mCaptureId, Dali::Toolkit::ImageUrl());
+  }
+  tempContainer.clear();
+
+  for(auto && capture : mCaptureContainer)
+  {
+    ResetCaptureData(capture.second);
+  }
+  mCaptureContainer.clear();
+
+  ResetCaptureTimer();
+
   if(mSceneHolder)
   {
     if(mRenderTask)
@@ -1043,9 +1255,17 @@ void SceneView::OnSceneDisconnection()
     }
     mSceneHolder.Reset();
   }
+  mTexture.Reset();
   mFrameBuffer.Reset();
+  mShadowTexture.Reset();
   mShadowFrameBuffer.Reset();
 
+  if(mInCameraTransition)
+  {
+    mTransitionAnimation.Stop();
+    ResetTransition();
+  }
+
   Control::OnSceneDisconnection();
 }
 
@@ -1113,14 +1333,24 @@ void SceneView::UpdateCamera(CameraActor camera)
 {
   if(camera)
   {
-    if(mSelectedCamera && mSelectedCamera.GetParent())
+    if(mSelectedCamera != camera)
+    {
+      if(mSelectedCamera)
+      {
+        mSelectedCamera.OffSceneSignal().Disconnect(this, &SceneView::OnCameraDisconnected);
+      }
+
+      mSelectedCamera = camera;
+      camera.OffSceneSignal().Connect(this, &SceneView::OnCameraDisconnected);
+    }
+
+    bool isCameraIncluded = CheckInside(mRootLayer, camera);
+    if(!isCameraIncluded)
     {
-      mSelectedCamera.Unparent();
+      mRootLayer.Add(camera);
     }
-    mRootLayer.Add(camera);
   }
 
-  mSelectedCamera = camera;
   if(mShadowLight)
   {
     SetShadowLightConstraint(mSelectedCamera, GetImplementation(mShadowLight).GetCamera());
@@ -1419,6 +1649,286 @@ void SceneView::UpdateShadowMapBuffer(uint32_t shadowMapSize)
   }
 }
 
+void SceneView::OnCaptureFinished(Dali::RenderTask& task)
+{
+  auto iter = std::find_if(mCaptureContainer.begin(), mCaptureContainer.end(), [task](std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>> item)
+                           { return item.first == task; });
+
+  if(iter != mCaptureContainer.end())
+  {
+    int32_t                 captureId = iter->second->mCaptureId;
+    Dali::Toolkit::ImageUrl imageUrl  = Dali::Toolkit::ImageUrl::New(iter->second->mCaptureInvertTexture);
+
+    ResetCaptureData(iter->second);
+    mCaptureContainer.erase(iter);
+
+    auto                     self = Self();
+    Dali::Scene3D::SceneView handle(Dali::Scene3D::SceneView::DownCast(self));
+    mCaptureFinishedSignal.Emit(handle, captureId, imageUrl);
+  }
+
+  ResetCaptureTimer();
+}
+
+bool SceneView::OnTimeOut()
+{
+  mTimerTickCount++;
+  auto                     self = Self();
+  Dali::Scene3D::SceneView handle(Dali::Scene3D::SceneView::DownCast(self));
+  std::vector<std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>>> tempContainer;
+  for(auto&& capture : mCaptureContainer)
+  {
+    if(capture.second->mStartTick + 1 < mTimerTickCount)
+    {
+      tempContainer.push_back(capture);
+    }
+  }
+
+  for(auto&& capture : tempContainer)
+  {
+    mCaptureFinishedSignal.Emit(handle, capture.second->mCaptureId, Dali::Toolkit::ImageUrl());
+  }
+
+  for(auto && capture : tempContainer)
+  {
+    ResetCaptureData(capture.second);
+  }
+  tempContainer.clear();
+
+  int32_t tickCount = mTimerTickCount;
+  auto it = std::remove_if(mCaptureContainer.begin(), mCaptureContainer.end(), [tickCount](std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>> item) {
+    return item.second->mStartTick + 1 < tickCount;
+  });
+  mCaptureContainer.erase(it, mCaptureContainer.end());
+  mCaptureContainer.shrink_to_fit();
+
+  ResetCaptureTimer();
+
+  return !mCaptureContainer.empty();
+}
+
+void SceneView::OnCameraDisconnected(Dali::Actor actor)
+{
+  if(!mIsProcessorRegistered)
+  {
+    mIsProcessorRegistered = true;
+    Adaptor::Get().RegisterProcessorOnce(*this);
+  }
+}
+
+void SceneView::RegisterCameraTransition(CameraActor destinationCamera, float durationSeconds, Dali::AlphaFunction alphaFunction)
+{
+  if(mInCameraTransition)
+  {
+    DALI_LOG_ERROR("Cannot start Camera transition before previous Camera transition is finished.\n");
+    return;
+  }
+
+  mTransitionSourceCamera      = GetSelectedCamera();
+  mTransitionDestinationCamera = destinationCamera;
+  mTransitionDurationSeconds   = durationSeconds;
+  mTransitionAlphaFunction     = alphaFunction;
+  mInCameraTransition          = true;
+
+  if(!mIsProcessorRegistered)
+  {
+    mIsProcessorRegistered = true;
+    Adaptor::Get().RegisterProcessorOnce(*this);
+  }
+}
+
+void SceneView::RequestCameraTransition()
+{
+  if(mTransitionSourceCamera && mTransitionDestinationCamera && !(mTransitionSourceCamera == mTransitionDestinationCamera))
+  {
+    Vector3 sourceWorldPosition = mTransitionSourceCamera.GetProperty<Vector3>(Dali::Actor::Property::WORLD_POSITION);
+    Quaternion sourceWorldOrientation = mTransitionSourceCamera.GetProperty<Quaternion>(Dali::Actor::Property::WORLD_ORIENTATION);
+
+    if(!CheckInside(mRootLayer, mTransitionDestinationCamera))
+    {
+      mRootLayer.Add(mTransitionDestinationCamera);
+    }
+
+    Vector3 destinationWorldPosition;
+    Quaternion destinationWorldOrientation;
+    Vector3 destinationWorldScale;
+    Dali::Matrix destinationWorldTransform = Dali::DevelActor::GetWorldTransform(mTransitionDestinationCamera);
+    destinationWorldTransform.GetTransformComponents(destinationWorldPosition, destinationWorldOrientation, destinationWorldScale);
+
+    if(!mTransitionAnimation)
+    {
+      mTransitionAnimation = Dali::Animation::New(mTransitionDurationSeconds);
+    }
+
+    if(mTransitionAnimation.GetState() != Animation::State::STOPPED)
+    {
+      mTransitionAnimation.Stop();
+    }
+    mTransitionAnimation.Clear();
+
+    Dali::KeyFrames positionKeyFrames = Dali::KeyFrames::New();
+    positionKeyFrames.Add(0.0f, sourceWorldPosition);
+    positionKeyFrames.Add(1.0f, destinationWorldPosition);
+
+    Dali::KeyFrames orientationKeyFrames = Dali::KeyFrames::New();
+    orientationKeyFrames.Add(0.0f, sourceWorldOrientation);
+    orientationKeyFrames.Add(1.0f, destinationWorldOrientation);
+
+    mTransitionCamera = Dali::CameraActor::New3DCamera();
+    mTransitionCamera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    mTransitionCamera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+    mRootLayer.Add(mTransitionCamera);
+
+    mTransitionAnimation.AnimateBetween(Dali::Property(mTransitionCamera, Dali::Actor::Property::POSITION), positionKeyFrames, mTransitionAlphaFunction, Dali::Animation::Interpolation::LINEAR);
+    mTransitionAnimation.AnimateBetween(Dali::Property(mTransitionCamera, Dali::Actor::Property::ORIENTATION), orientationKeyFrames, mTransitionAlphaFunction, Dali::Animation::Interpolation::LINEAR);
+
+    Dali::DevelCameraActor::ProjectionDirection sourceProjectionDirection      = mTransitionSourceCamera.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION);
+    Dali::DevelCameraActor::ProjectionDirection destinationProjectionDirection = mTransitionDestinationCamera.GetProperty<Dali::DevelCameraActor::ProjectionDirection>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION);
+    if(mTransitionDestinationCamera.GetProjectionMode() == Dali::Camera::ProjectionMode::PERSPECTIVE_PROJECTION)
+    {
+      float sourceFieldOfView = mTransitionSourceCamera.GetFieldOfView();
+      float destinationFieldOfView = mTransitionDestinationCamera.GetFieldOfView();
+
+      if(sourceProjectionDirection != destinationProjectionDirection)
+      {
+        float aspect = mTransitionDestinationCamera.GetAspectRatio();
+        if(destinationProjectionDirection == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
+        {
+          ConvertFovFromHorizontalToVertical(aspect, sourceFieldOfView);
+        }
+        else
+        {
+          ConvertFovFromVerticalToHorizontal(aspect, sourceFieldOfView);
+        }
+      }
+
+      KeyFrames fieldOfViewKeyFrames = KeyFrames::New();
+      fieldOfViewKeyFrames.Add(0.0f, sourceFieldOfView);
+      fieldOfViewKeyFrames.Add(1.0f, destinationFieldOfView);
+      mTransitionAnimation.AnimateBetween(Dali::Property(mTransitionCamera, Dali::CameraActor::Property::FIELD_OF_VIEW), fieldOfViewKeyFrames, mTransitionAlphaFunction, Dali::Animation::Interpolation::LINEAR);
+    }
+    else
+    {
+      float sourceOrthographicSize = mTransitionSourceCamera.GetProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE);
+      float destinationOrthographicSize = mTransitionDestinationCamera.GetProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE);
+
+      if(sourceProjectionDirection != destinationProjectionDirection)
+      {
+        float aspect = mTransitionDestinationCamera.GetAspectRatio();
+        if(destinationProjectionDirection == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
+        {
+          sourceOrthographicSize = sourceOrthographicSize / aspect;
+        }
+        else
+        {
+          sourceOrthographicSize = sourceOrthographicSize * aspect;
+        }
+      }
+
+      KeyFrames orthographicSizeKeyFrames = KeyFrames::New();
+      orthographicSizeKeyFrames.Add(0.0f, sourceOrthographicSize);
+      orthographicSizeKeyFrames.Add(1.0f, destinationOrthographicSize);
+      mTransitionAnimation.AnimateBetween(Dali::Property(mTransitionCamera, Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE), orthographicSizeKeyFrames, mTransitionAlphaFunction, Dali::Animation::Interpolation::LINEAR);
+    }
+
+    float destinationNearPlaneDistance = mTransitionDestinationCamera.GetNearClippingPlane();
+    float destinationFarPlaneDistance = mTransitionDestinationCamera.GetFarClippingPlane();
+    mTransitionCamera.SetNearClippingPlane(std::min(mTransitionSourceCamera.GetNearClippingPlane(), destinationNearPlaneDistance));
+    mTransitionCamera.SetFarClippingPlane(std::max(mTransitionSourceCamera.GetFarClippingPlane(), destinationFarPlaneDistance));
+
+    mTransitionCamera.SetProperty(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION, destinationProjectionDirection);
+    mTransitionCamera.SetProjectionMode(mTransitionDestinationCamera.GetProjectionMode());
+
+    UpdateCamera(mTransitionCamera);
+
+    mTransitionAnimation.FinishedSignal().Connect(this, &SceneView::OnTransitionFinished);
+
+    mTransitionAnimation.Play();
+  }
+  else
+  {
+    ResetTransition();
+  }
+}
+
+void SceneView::ResetTransition()
+{
+  mTransitionCamera.Reset();
+  mTransitionSourceCamera.Reset();
+  mTransitionDestinationCamera.Reset();
+  mTransitionAnimation.Reset();
+  mInCameraTransition = false;
+}
+
+void SceneView::OnTransitionFinished(Animation& animation)
+{
+  UpdateCamera(mTransitionDestinationCamera);
+  ResetTransition();
+
+  auto                     self = Self();
+  Dali::Scene3D::SceneView handle(Dali::Scene3D::SceneView::DownCast(self));
+  mCameraTransitionFinishedSignal.Emit(handle);
+}
+
+void SceneView::ResetCaptureData(std::shared_ptr<CaptureData> captureData)
+{
+  captureData->mCaptureCamera.SetAspectRatio(captureData->mCaptureCameraOriginalAspectRatio);
+  if(mSceneHolder)
+  {
+    RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+    taskList.RemoveTask(captureData->mCaptureTask);
+    taskList.RemoveTask(captureData->mCaptureInvertTask);
+  }
+  captureData->mCaptureTask.Reset();
+  captureData->mCaptureInvertTask.Reset();
+  captureData->mCaptureTexture.Reset();
+  captureData->mCaptureInvertTexture.Reset();
+  captureData->mCaptureFrameBuffer.Reset();
+  captureData->mCaptureInvertFrameBuffer.Reset();
+  captureData->mCaptureUrl.Reset();
+  captureData->mCaptureImageView.Unparent();
+  captureData->mCaptureImageView.Reset();
+  captureData->mCaptureInvertCamera.Unparent();
+  captureData->mCaptureInvertCamera.Reset();
+}
+
+void SceneView::ResetCaptureTimer()
+{
+  if(mCaptureContainer.empty() && mCaptureTimer)
+  {
+    mCaptureTimer.Stop();
+    mCaptureTimer.Reset();
+    mTimerTickCount = 0;
+  }
+}
+
+void SceneView::Process(bool postProcessor)
+{
+  CameraActor selectedCamera = GetSelectedCamera();
+  if(!selectedCamera || !CheckInside(mRootLayer, selectedCamera))
+  {
+    UpdateCamera(mDefaultCamera);
+  }
+
+  if(mInCameraTransition && !mTransitionCamera)
+  {
+    RequestCameraTransition();
+  }
+
+  mIsProcessorRegistered = false;
+}
+
+void SceneView::OnCaptureFailedIdle()
+{
+  for(auto&& captureId : mFailedCaptureRequests)
+  {
+    auto                     self = Self();
+    Dali::Scene3D::SceneView handle(Dali::Scene3D::SceneView::DownCast(self));
+    mCaptureFinishedSignal.Emit(handle, captureId, Dali::Toolkit::ImageUrl());
+  }
+  mFailedCaptureCallbacks = nullptr;
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali
index 29661ef..694594d 100644 (file)
 // EXTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/image/image-visual.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/controls/image-view/image-view.h>
 #include <dali/integration-api/adaptor-framework/scene-holder.h>
 #include <dali/integration-api/ordered-set.h>
 #include <dali/public-api/actors/camera-actor.h>
 #include <dali/public-api/actors/layer.h>
+#include <dali/public-api/adaptor-framework/timer.h>
 #include <dali/public-api/adaptor-framework/window.h>
 #include <dali/public-api/animation/animation.h>
+#include <dali/public-api/capture/capture.h>
 #include <dali/public-api/object/weak-handle.h>
 #include <dali/public-api/render-tasks/render-task.h>
 #include <dali/public-api/rendering/frame-buffer.h>
@@ -49,8 +52,29 @@ namespace Internal
 /**
  * @brief Impl class for SceneView.
  */
-class SceneView : public Dali::Toolkit::Internal::Control
+class SceneView : public Dali::Toolkit::Internal::Control, public Integration::Processor
 {
+private:
+  /**
+   * Data to store Capture related objects.
+   */
+  struct CaptureData
+  {
+    int32_t                  mStartTick;
+    int32_t                  mCaptureId;                        // Unique Key to distinguish requested Captures.
+    float                    mCaptureCameraOriginalAspectRatio; // Original AspectRatio of the input cameras
+    Dali::Toolkit::ImageUrl  mCaptureUrl;                       // URL for first captured buffer, but it is Y-inverted.
+    Dali::Toolkit::ImageView mCaptureImageView;                 // ImageView to draw first capture buffer to be transfered as input for invert.
+    Dali::CameraActor        mCaptureCamera;                    // CameraActor to draw first capture buffer
+    Dali::RenderTask         mCaptureTask;                      // RenderTask that is used to capture first buffer.
+    Dali::Texture            mCaptureTexture;                   // First Captured texture, but it is Y-inverted.
+    Dali::FrameBuffer        mCaptureFrameBuffer;               // First Captured FBO, but it is Y-inverted.
+    Dali::CameraActor        mCaptureInvertCamera;              // CameraActor to invert first captured buffer by second pass.
+    Dali::RenderTask         mCaptureInvertTask;                // RenderTask to invert first captured buffer.
+    Dali::Texture            mCaptureInvertTexture;             // Result texture of second pass. This is final Texture result.
+    Dali::FrameBuffer        mCaptureInvertFrameBuffer;         // FBO for firnal Texture result
+  };
+
 public:
   /**
    * @brief Creates a new SceneView.
@@ -100,6 +124,16 @@ public:
   void SelectCamera(const std::string& name);
 
   /**
+   * @copydoc SceneView::StartCameraTransition()
+   */
+  void StartCameraTransition(uint32_t index, float durationSeconds, Dali::AlphaFunction alphaFunction);
+
+  /**
+   * @copydoc SceneView::StartCameraTransition()
+   */
+  void StartCameraTransition(std::string name, float durationSeconds, Dali::AlphaFunction alphaFunction);
+
+  /**
    * @brief Register an item.
    *
    * Some works(e.g, lighting) of SceneView should be propagated to the child 3D items.
@@ -242,6 +276,21 @@ public:
   Quaternion GetSkyboxOrientation() const;
 
   /**
+   * @copydoc SceneView::Capture()
+   */
+  int32_t Capture(Dali::CameraActor camera, const Vector2& size);
+
+  /**
+   * @copydoc SceneView::CaptureFinishedSignal
+   */
+  Dali::Scene3D::SceneView::CaptureFinishedSignalType& CaptureFinishedSignal();
+
+  /**
+   * @copydoc SceneView::CameraTransitionFinishedSignal
+   */
+  Dali::Scene3D::SceneView::CameraTransitionFinishedSignalType& CameraTransitionFinishedSignal();
+
+  /**
    * @brief Retrieves ShaderManager of this SceneView.
    * @return ShaderManager of this SceneView.
    */
@@ -430,25 +479,116 @@ private:
    */
   void UpdateShadowMapBuffer(uint32_t shadowMapSize);
 
-private:
+  /**
+   * @brief CaptureFinished Callback that is called the capture rendering is finished.
+   * @param[in] task RenderTask that draws requested capture scene.
+   */
+  void OnCaptureFinished(Dali::RenderTask& task);
+
+  /**
+   * @brief Time out Callback to handle the case each capture request is not finished for long time.
+   * @return True if the timer needs to go on.
+   */
+  bool OnTimeOut();
+
+  /**
+   * @brief OffScene Callback of camera actor to check whether the SceneView has valid camera or not.
+   * @param[in] actor Disconnected Camera.
+   */
+  void OnCameraDisconnected(Dali::Actor actor);
+
+  /**
+   * @brief Registers Camera Transition to processor
+   */
+  void RegisterCameraTransition(CameraActor destinationCamera, float durationSeconds, Dali::AlphaFunction alphaFunction);
+
+  /**
+   * @brief Requests Camera Transition
+   * @note This method is called in Process.
+   */
+  void RequestCameraTransition();
+
+  /**
+   * @brief Resets Transition information.
+   * This method is called when the transition is finished or invalid transition is requested.
+   */
+  void ResetTransition();
+
+  /**
+   * @brief Camera Transition finished callback.
+   * @param[in] animation animation for this Transition
+   */
+  void OnTransitionFinished(Animation& animation);
+
+  /**
+   * @brief Reset CaptureData when the capture is finished or failed.
+   * @param[in] captureData CaptureData to be reset.
+   */
+  void ResetCaptureData(std::shared_ptr<CaptureData> captureData);
+
+  /**
+   * @brief Reset Capture timer when there isn't any capture in progress.
+   */
+  void ResetCaptureTimer();
+
+  /**
+   * @brief Emit capture failed event on idle.
+   */
+  void OnCaptureFailedIdle();
+
+private: // Implementation of Processor
+  /**
+   * @copydoc Dali::Integration::Processor::Process()
+   * @note This process check the SceneView has activated Camera or not.
+   * If not, select default camera.
+   * And, this process generates Camera transition animation that is triggered by StartCameraTransition method.
+   */
+  void Process(bool postProcessor) override;
+
+  /**
+   * @copydoc Dali::Integration::Processor::GetProcessorName()
+   */
+  std::string_view GetProcessorName() const override
+  {
+    return "SceneViewImpl";
+  }
+
   Toolkit::Visual::Base mVisual;
 
   /////////////////////////////////////////////////////////////
   // FrameBuffer and Rendertask to render child objects as a 3D Scene
-  Dali::WeakHandle<Dali::Window> mWindow;
-  Integration::SceneHolder       mSceneHolder;
-  CameraActor                    mDefaultCamera;
-  CameraActor                    mSelectedCamera;
-  std::vector<CameraActor>       mCameras;
-  Dali::FrameBuffer              mFrameBuffer;
-  Dali::Texture                  mTexture;
-  Dali::RenderTask               mRenderTask;
-  Layer                          mRootLayer;
-  int32_t                        mWindowOrientation;
-  Dali::Actor                    mSkybox;
-  Quaternion                     mSkyboxOrientation;
-  float                          mSkyboxIntensity{1.0f};
-  uint8_t                        mFrameBufferMultiSamplingLevel{0u};
+  Dali::WeakHandle<Dali::Window>                      mWindow;
+  Integration::SceneHolder                            mSceneHolder;
+  CameraActor                                         mDefaultCamera;
+  CameraActor                                         mSelectedCamera;
+  std::vector<CameraActor>                            mCameras;
+  Dali::FrameBuffer                                   mFrameBuffer;
+  Dali::Texture                                       mTexture;
+  Dali::RenderTask                                    mRenderTask;
+  Layer                                               mRootLayer;
+  int32_t                                             mWindowOrientation;
+  Dali::Actor                                         mSkybox;
+  Quaternion                                          mSkyboxOrientation;
+  float                                               mSkyboxIntensity{1.0f};
+  uint8_t                                             mFrameBufferMultiSamplingLevel{0u};
+  Dali::Scene3D::SceneView::CaptureFinishedSignalType mCaptureFinishedSignal;
+  std::vector<int32_t>                                mFailedCaptureRequests;
+  CallbackBase*                                       mFailedCaptureCallbacks;
+
+  // camera Transition
+  CameraActor                                                  mTransitionCamera;
+  CameraActor                                                  mTransitionSourceCamera;
+  CameraActor                                                  mTransitionDestinationCamera;
+  float                                                        mTransitionDurationSeconds{0.0f};
+  AlphaFunction                                                mTransitionAlphaFunction;
+  bool                                                         mInCameraTransition{false};
+  Animation                                                    mTransitionAnimation;
+  Dali::Scene3D::SceneView::CameraTransitionFinishedSignalType mCameraTransitionFinishedSignal;
+
+  int32_t                                                                mCaptureId{0};     // Capture ID for requested capture, this is incrementally increasing.
+  std::vector<std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>>> mCaptureContainer; // Container that stores CaptureData until the Capture is finished.
+  Dali::Timer                                                            mCaptureTimer;     // Timer to check the capture is time out or not.
+  int32_t                                                                mTimerTickCount{0};
 
   Dali::Integration::OrderedSet<Scene3D::Internal::LightObserver, false> mLightObservers; ///< The set of items to be notified when light properties change. (not owned)
 
@@ -493,6 +633,7 @@ private:
   bool                        mSkyboxDirty{false};
   bool                        mIblDiffuseDirty{false};
   bool                        mIblSpecularDirty{false};
+  bool                        mIsProcessorRegistered{false};
 };
 
 } // namespace Internal
index 2ec0fed..a49fd4b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -132,16 +132,20 @@ ColliderMeshProcessor::~ColliderMeshProcessor()
   }
 }
 
-void ColliderMeshProcessor::ColliderMeshChanged(Scene3D::Model model)
+void ColliderMeshProcessor::ColliderMeshChanged(Collidable& collidable)
 {
-  if(model.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
-  {
-    AddSceneViewParentToProcessingQueue(model);
-  }
-  else
+  Actor actor = collidable.GetCollidableActor();
+  if(actor)
   {
-    // TODO: Check if signal already connected
-    model.OnSceneSignal().Connect(this, &ColliderMeshProcessor::ModelOnScene);
+    if(actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+    {
+      AddSceneViewParentToProcessingQueue(collidable.GetCollidableActor());
+    }
+    else
+    {
+      // TODO: Check if signal already connected
+      collidable.GetCollidableActor().OnSceneSignal().Connect(this, &ColliderMeshProcessor::ModelOnScene);
+    }
   }
 }
 
@@ -155,19 +159,19 @@ void ColliderMeshProcessor::ModelOnScene(Actor actor)
   model.OnSceneSignal().Disconnect(this, &ColliderMeshProcessor::ModelOnScene);
 }
 
-void ColliderMeshProcessor::AddSceneViewParentToProcessingQueue(Scene3D::Model model)
+void ColliderMeshProcessor::AddSceneViewParentToProcessingQueue(Actor actor)
 {
-  Actor actor = model;
+  Actor currentActor = actor;
   do
   {
-    actor = actor.GetParent();
-    Scene3D::SceneView sceneView(Scene3D::SceneView::DownCast(actor));
+    currentActor = currentActor.GetParent();
+    Scene3D::SceneView sceneView(Scene3D::SceneView::DownCast(currentActor));
     if(sceneView)
     {
       mSceneViewsToProcess.push_back(sceneView);
       break;
     }
-  } while(actor);
+  } while(currentActor);
 }
 
 void ColliderMeshProcessor::Process(bool /* postProcess */)
index fb34eb5..b281589 100644 (file)
@@ -1,7 +1,7 @@
 #pragma once
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,12 +40,12 @@ public:
 
   ~ColliderMeshProcessor();
 
-  void ColliderMeshChanged(Scene3D::Model model);
+  void ColliderMeshChanged(Collidable& collidable);
 
 private:
   void ModelOnScene(Actor actor);
 
-  void AddSceneViewParentToProcessingQueue(Scene3D::Model model);
+  void AddSceneViewParentToProcessingQueue(Actor actor);
 
 protected: // Implementation of Processor
   /**
index 9f0ca0d..c4fbc28 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -52,9 +52,9 @@ ColliderMeshProcessor ColliderMeshProcessor::Get()
   return processor;
 }
 
-void ColliderMeshProcessor::ColliderMeshChanged(Scene3D::Model model)
+void ColliderMeshProcessor::ColliderMeshChanged(Collidable& collidable)
 {
-  GetImpl(*this).ColliderMeshChanged(model);
+  GetImpl(*this).ColliderMeshChanged(collidable);
 }
 
 ColliderMeshProcessor::ColliderMeshProcessor(Internal::ColliderMeshProcessor* impl)
index 0d874ee..8ccb6b9 100644 (file)
@@ -1,7 +1,7 @@
 #pragma once
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 // EXTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali/integration-api/debug.h>
 #include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/object/weak-handle.h>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/controls/model/model.h>
@@ -30,6 +33,26 @@ namespace Internal
 class ColliderMeshProcessor;
 }
 
+class Collidable
+{
+public:
+  Collidable() = default;
+
+  Dali::Actor GetCollidableActor() const
+  {
+    return mCollidableActor.GetHandle();
+  }
+
+protected:
+  void SetCollidableActor(Dali::Actor collidableActor)
+  {
+    mCollidableActor = collidableActor;
+  }
+
+private:
+  Dali::WeakHandle<Dali::Actor> mCollidableActor;
+};
+
 class ColliderMeshProcessor : public BaseHandle
 {
 public:
@@ -38,7 +61,7 @@ public:
 
   static ColliderMeshProcessor Get();
 
-  void ColliderMeshChanged(Scene3D::Model model);
+  void ColliderMeshChanged(Collidable& collidable);
 
 private:
   explicit ColliderMeshProcessor(Internal::ColliderMeshProcessor* impl);
index 1ca545c..ed5e3e9 100644 (file)
@@ -10,6 +10,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_internal_dir}/common/model-cache-manager.cpp
        ${scene3d_internal_dir}/common/model-load-task.cpp
        ${scene3d_internal_dir}/controls/model/model-impl.cpp
+       ${scene3d_internal_dir}/controls/panel/panel-impl.cpp
        ${scene3d_internal_dir}/controls/scene-view/scene-view-impl.cpp
        ${scene3d_internal_dir}/event/collider-mesh-processor.cpp
        ${scene3d_internal_dir}/event/collider-mesh-processor-impl.cpp
@@ -24,6 +25,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_internal_dir}/loader/json-util.cpp
        ${scene3d_internal_dir}/model-components/material-impl.cpp
        ${scene3d_internal_dir}/model-components/model-node-impl.cpp
+       ${scene3d_internal_dir}/model-components/model-node-tree-utility.cpp
        ${scene3d_internal_dir}/model-components/model-primitive-impl.cpp
        ${scene3d_internal_dir}/model-motion/motion-data-impl.cpp
        ${scene3d_internal_dir}/model-motion/motion-data-load-task.cpp
index 47bb831..a9e704b 100644 (file)
@@ -136,6 +136,8 @@ const float kPcfTheta = 2.0 * kPi * kInvSampleCount;
 const float kSinPcfTheta = sin(kPcfTheta);
 const float kCosPcfTheta = cos(kPcfTheta);
 
+
+uniform mediump int uShadowLightIndex;
 uniform lowp int uEnableShadowSoftFiltering;
 uniform mediump float uShadowIntensity;
 uniform highp float uShadowBias;
@@ -280,7 +282,7 @@ void main()
     for(int i = 0; i < uLightCount; ++i)
     {
       highp vec3 l = normalize(-uLightDirection[i]); // Vector from surface point to light
-      highp vec3 h = normalize(l+v);               // Half vector between both l and v
+      highp vec3 h = normalize(l+v);                 // Half vector between both l and v
       highp float VdotH = dot(v, h);
       highp vec3 specularReflection = f0 + (reflectance90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
 
@@ -304,6 +306,9 @@ void main()
   if(float(uIsShadowReceiving) * float(uIsShadowEnabled) * uShadowIntensity > 0.0)
   {
     mediump float exposureFactor = 0.0;
+
+    highp vec3 l = normalize(-uLightDirection[uShadowLightIndex]);
+    highp float NdotL = dot(n, l);
     if(uEnableShadowSoftFiltering > 0)
     {
 #ifdef SL_VERSION_LOW
@@ -321,12 +326,20 @@ void main()
         exposureFactor += (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
       }
       exposureFactor *= kInvSampleCount;
+
+      // Blend filtered shadow and shadow from fragment normal to allow soft filtering nearby where the NdotL is zero.
+      highp float shadowFactor = clamp((NdotL + 0.5) * 2.0, 0.0f, 1.0);
+      exposureFactor = mix(0.0, exposureFactor, shadowFactor);
     }
     else
     {
-      mediump float depthValue = TEXTURE(sShadowMap, positionFromLightView.xy).r;
-      exposureFactor           = (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+      if(NdotL > 0.0)
+      {
+        mediump float depthValue = TEXTURE(sShadowMap, positionFromLightView.xy).r;
+        exposureFactor           = (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+      }
     }
+
     color *= (1.0 - (1.0 - exposureFactor) * uShadowIntensity);
   }
 
index 83be657..8ee1bf4 100644 (file)
 #define ADD_EXTRA_SKINNING_ATTRIBUTES
 #define ADD_EXTRA_WEIGHTS
 
-#ifdef HIGHP
-  precision highp float;
-#else
-  precision mediump float;
-#endif
+precision highp float;
 
 INPUT vec3 aPosition;
 INPUT vec2 aTexCoord;
@@ -49,20 +45,20 @@ uniform int uBlendShapeGeometryHeight;
 #endif
 
 OUTPUT mediump vec2 vUV;
-OUTPUT lowp mat3 vTBN;
+OUTPUT highp mat3 vTBN;
 OUTPUT lowp vec4 vColor;
 OUTPUT highp vec3 vPositionToCamera;
 
 uniform highp mat4 uViewMatrix;
-uniform mat3 uNormalMatrix;
-uniform mat4 uModelMatrix;
-uniform mat4 uProjection;
+uniform highp mat3 uNormalMatrix;
+uniform highp mat4 uModelMatrix;
+uniform highp mat4 uProjection;
 
 #ifdef SKINNING
 
 #ifdef SL_VERSION_LOW
 #define MAX_BONES 80
-uniform mat4 uBone[MAX_BONES];
+uniform highp mat4 uBone[MAX_BONES];
 #else
 #define MAX_BONES 256
 layout(std140) uniform Bones
index 5e6f766..3ce016b 100644 (file)
@@ -1,7 +1,7 @@
 
 precision mediump float;
 uniform lowp vec4 uColor;
-flat INPUT float vColor;
+FLAT INPUT float vColor;
 
 void main()
 {
index 5e9d965..49860c8 100644 (file)
@@ -3,7 +3,7 @@ precision mediump float;
 uniform mat4 uMvpMatrix;
 INPUT vec3 aPosition;
 INPUT float aColor;
-flat OUTPUT float vColor;
+FLAT OUTPUT float vColor;
 
 void main()
 {
diff --git a/dali-scene3d/internal/model-components/model-node-tree-utility.cpp b/dali-scene3d/internal/model-components/model-node-tree-utility.cpp
new file mode 100644 (file)
index 0000000..03f571e
--- /dev/null
@@ -0,0 +1,142 @@
+#include <dali-scene3d/internal/model-components/model-node-tree-utility.h>
+
+namespace Dali
+{
+
+namespace Scene3D
+{
+
+namespace Internal
+{
+
+namespace ModelNodeTreeUtility
+{
+
+void UpdateShaderRecursively(Scene3D::ModelNode node, Scene3D::Loader::ShaderManagerPtr shaderManager)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).UpdateShader(shaderManager);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      UpdateShaderRecursively(childNode, shaderManager);
+    }
+  }
+}
+
+void UpdateShadowMapTextureRecursively(Scene3D::ModelNode node, Dali::Texture shadowMapTexture)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).SetShadowMapTexture(shadowMapTexture);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      UpdateShadowMapTextureRecursively(childNode, shadowMapTexture);
+    }
+  }
+}
+
+void UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).CastShadow(castShadow);
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
+    {
+      continue;
+    }
+    UpdateCastShadowRecursively(childNode, castShadow);
+  }
+}
+
+void UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).ReceiveShadow(receiveShadow);
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
+    {
+      continue;
+    }
+    UpdateReceiveShadowRecursively(childNode, receiveShadow);
+  }
+}
+
+void UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
+    {
+      continue;
+    }
+    UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+  }
+}
+
+void UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
+    {
+      continue;
+    }
+    UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor);
+  }
+}
+
+} // namespace ModelNodeTreeUtility
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/model-components/model-node-tree-utility.h b/dali-scene3d/internal/model-components/model-node-tree-utility.h
new file mode 100644 (file)
index 0000000..e36e2fa
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_TREE_UTILITY_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_TREE_UTILITY_H
+
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
+#include <dali-scene3d/public-api/loader/shader-manager.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
+
+namespace Dali
+{
+
+namespace Scene3D
+{
+
+namespace Internal
+{
+
+namespace ModelNodeTreeUtility
+{
+
+/**
+ * @brief Makes the input node update shader properties.
+ */
+void UpdateShaderRecursively(Scene3D::ModelNode node, Scene3D::Loader::ShaderManagerPtr shaderManager);
+
+/**
+ * @brief Makes the input node update shadow map texture.
+ */
+void UpdateShadowMapTextureRecursively(Scene3D::ModelNode node, Dali::Texture shadowMapTexture);
+
+/**
+ * @brief Makes the input node cast shadow or not.
+ */
+void UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow);
+
+/**
+ * @brief Makes the input node receive shadow or not.
+ */
+void UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow);
+
+/**
+ * @brief Changes IBL information of the input node.
+ */
+void UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels);
+
+/**
+ * @brief Changes IBL factor of the input node.
+ */
+void UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor);
+
+} // namespace ModelNodeTreeUtility
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_TREE_UTILITY_H
\ No newline at end of file
index 844f0b9..68d6024 100644 (file)
 #include <dali/public-api/object/property-array.h>
 #include <dali/public-api/object/property-map.h>
 
-#if defined(DEBUG_ENABLED)
-#include <sys/types.h>
-#include <unistd.h>
-#include <filesystem>
-namespace fs = std::filesystem;
-#endif
-
 namespace Dali
 {
 namespace Scene3D
@@ -52,24 +45,6 @@ namespace
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_SCENE3D_MODEL_PRIMITIVE");
 
-std::string tmpFilename(std::string prefix, std::string suffix)
-{
-  static int id = 0;
-  id++;
-  std::ostringstream oss;
-  oss << prefix << getpid() << "_" << std::setfill('0') << std::setw(4) << id << suffix;
-  return oss.str();
-}
-
-#define DALI_LOG_WRITE_FILE(filename, stream) \
-  {                                           \
-    fs::path tmp = fs::temp_directory_path(); \
-    tmp /= filename;                          \
-    std::ofstream ostrm(tmp, std::ios::out);  \
-    ostrm << stream;                          \
-    ostrm.flush();                            \
-  }
-
 inline Property::Map GetMap(Shader shader)
 {
   Property::Value program = shader[Shader::Property::PROGRAM];
@@ -356,21 +331,6 @@ void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag
     if(mShader != newShader)
     {
       DALI_LOG_INFO(gLogFilter, Debug::General, "Warning!  Model primitive shader changed: OldHash:%x NewHash:%x\n", oldHash, shaderOption.GetOptionHash());
-
-#if defined(DEBUG_ENABLED)
-      if(mShader)
-      {
-        Property::Map oldMap = GetMap(mShader);
-        DALI_LOG_WRITE_FILE(tmpFilename("oldShader", ".txt"), "Vertex Shader:\n"
-                                                                << oldMap["vertex"] << "\n\nFragmentShader: " << oldMap["fragment"] << "\n");
-      }
-      if(newShader)
-      {
-        Property::Map newMap = GetMap(newShader);
-        DALI_LOG_WRITE_FILE(tmpFilename("newShader", ".txt"), "Vertex Shader:\n"
-                                                                << newMap["vertex"] << "\n\nFragmentShader: " << newMap["fragment"] << "\n");
-      }
-#endif
     }
     mShader = newShader;
 
@@ -417,7 +377,7 @@ void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag
     }
     mTextureSet.SetTexture(textureCount++, mShadowMapTexture);
 
-    Texture brdfTexture = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
+    Texture brdfTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetDefaultBrdfTexture();
     if(!mSpecularTexture || !mDiffuseTexture)
     {
       Texture iblTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyCubeTextureWhiteRGB();
index 88bc524..58f7f68 100644 (file)
@@ -379,7 +379,7 @@ public:
    * @brief Sets whether this Model casts shadow or not.
    * If it is true, this model is drawn on Shadow Map.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @param[in] castShadow Whether this Model casts shadow or not.
    * @note This method affects all of the child ModelNode.
    * However, same property of each child ModelNode can be changed respectively and it not changes parent's property.
@@ -389,7 +389,7 @@ public:
   /**
    * @brief Retrieves whether the Model casts shadow or not for Light.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @return True if this model casts shadow.
    * @note IBL does not cast any shadow.
    */
@@ -399,7 +399,7 @@ public:
    * @brief Sets whether this Model receives shadow or not.
    * If it is true, shadows are drawn on this model.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @param[in] receiveShadow Whether this Model receives shadow or not.
    * @note This method affects all of the child ModelNode.
    * However, same property of each child ModelNode can be changed respectively and it not changes parent's property.
@@ -409,7 +409,7 @@ public:
   /**
    * @brief Retrieves whether the Model receives shadow or not for Light.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @return True if this model receives shadow.
    */
   bool IsShadowReceiving() const;
@@ -423,7 +423,7 @@ public:
    * @endcode
    * Here the model is the model that is hit and the ModelNode containing the collider mesh
    * was applied to.
-   * The return value of True, indicates that the hover event should be consumed.
+   * The return value of True, indicates that the event should be consumed.
    * Otherwise the signal will be emitted on the next sensitive parent of the actor.
    *
    * @SINCE_2_2.53
diff --git a/dali-scene3d/public-api/controls/panel/panel.cpp b/dali-scene3d/public-api/controls/panel/panel.cpp
new file mode 100644 (file)
index 0000000..3c2265e
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/controls/panel/panel.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/controls/panel/panel-impl.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
+
+namespace Dali::Scene3D
+{
+Panel::Panel() = default;
+
+Panel::Panel(const Panel& model) = default;
+
+Panel::Panel(Panel&& rhs) noexcept = default;
+
+Panel& Panel::operator=(const Panel& model) = default;
+
+Panel& Panel::operator=(Panel&& rhs) noexcept = default;
+
+Panel::~Panel() = default;
+
+Panel Panel::New()
+{
+  return Internal::Panel::New();
+}
+
+Panel Panel::DownCast(BaseHandle handle)
+{
+  return Control::DownCast<Panel, Internal::Panel>(handle);
+}
+
+Panel::Panel(Internal::Panel& implementation)
+: Control(implementation)
+{
+}
+
+Panel::Panel(Dali::Internal::CustomActor* internal)
+: Control(internal)
+{
+  VerifyCustomActorPointer<Internal::Panel>(internal);
+}
+
+void Panel::SetPanelResolution(const Vector2& resolution)
+{
+  GetImpl(*this).SetPanelResolution(resolution);
+}
+
+Vector2 Panel::GetPanelResolution() const
+{
+  return GetImpl(*this).GetPanelResolution();
+}
+
+void Panel::SetContent(Dali::Actor rootActor)
+{
+  GetImpl(*this).SetContent(rootActor);
+}
+
+Dali::Actor Panel::GetContent() const
+{
+  return GetImpl(*this).GetContent();
+}
+
+void Panel::ClearPanel()
+{
+  GetImpl(*this).ClearPanel();
+}
+
+void Panel::CastShadow(bool castShadow)
+{
+  GetImpl(*this).CastShadow(castShadow);
+}
+
+bool Panel::IsShadowCasting() const
+{
+  return GetImpl(*this).IsShadowCasting();
+}
+
+void Panel::ReceiveShadow(bool receiveShadow)
+{
+  GetImpl(*this).ReceiveShadow(receiveShadow);
+}
+
+bool Panel::IsShadowReceiving() const
+{
+  return GetImpl(*this).IsShadowReceiving();
+}
+
+} // namespace Dali::Scene3D
diff --git a/dali-scene3d/public-api/controls/panel/panel.h b/dali-scene3d/public-api/controls/panel/panel.h
new file mode 100644 (file)
index 0000000..7a9c065
--- /dev/null
@@ -0,0 +1,314 @@
+#ifndef DALI_SCENE3D_PANEL_H
+#define DALI_SCENE3D_PANEL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control.h>
+#include <dali/public-api/common/dali-common.h>
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal DALI_INTERNAL
+{
+class Panel;
+}
+
+/**
+ * @addtogroup dali_toolkit_controls_panel
+ * @{
+ */
+
+/**
+ * @brief Panel is a control to show 2D UI on 3D Scene.
+ * 2D UI contents set on this Panel are rendered on a screen panel that is placed on 3D scene.
+ * Each Panel has a single plane with defined resolution.
+ * The plane is always placed at center to fit within the boundaries of the panel while maintaining the aspect ratio of the resolution.
+ *
+ * @SINCE_2_3.38
+ * @code
+ *
+ * Panel panel = Panel::New();
+ * panel.SetProperty(Dali::Actor::Property::SIZE, Vector2(width, height));
+ * panel.SetPanelResolution(Vector2(resolutionWidth, resolutionHeight));
+ * panel.SetContent(rootContentActor);
+ * window.Add(panel);
+ *
+ * @endcode
+ * @note 2D UI Content can be added on Panel, but the result that another 3D SceneView is added on Panel is not guaranteed.
+ */
+class DALI_SCENE3D_API Panel : public Dali::Toolkit::Control
+{
+public:
+  /**
+   * @brief Enumeration for the start and end property ranges for panel.
+   * @SINCE_2_3.38
+   */
+  enum PropertyRange
+  {
+    PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1, ///< @SINCE_2_3.38
+    PROPERTY_END_INDEX   = PROPERTY_START_INDEX + 1000,             ///< Reserve property indices @SINCE_2_3.38
+
+    ANIMATABLE_PROPERTY_START_INDEX = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX,       ///< @SINCE_2_3.38
+    ANIMATABLE_PROPERTY_END_INDEX   = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX + 1000 ///< Reserve animatable property indices, @SINCE_2_3.38
+  };
+
+  /**
+   * @brief Enumeration for the instance of properties belonging to the Panel class.
+   * @SINCE_2_3.38
+   */
+  struct Property
+  {
+    enum
+    {
+      /**
+       * @brief Use Transparent background or not. Default value is false
+       * @details type Property::BOOLEAN.
+       * @SINCE_2_3.38
+       * @note If this property is true, the plane cannot make shadow by Light.
+       */
+      TRANSPARENT = PROPERTY_START_INDEX,
+
+      /**
+       * @brief Property to define whether the content is rendered as double sided or not.
+       * Default value is false.
+       * If this property is true, the plane is drawn mirrored horizontally when viewed from behind the Panel.
+       * @details Type Property::BOOLEAN.
+       * @SINCE_2_3.38
+       */
+      DOUBLE_SIDED,
+
+      /**
+       * @brief Property to use back face plane.
+       * If this property is true, an opaque plane will be displayed when viewed from behind the Panel.
+       * Default value is true.
+       * Default color is white.
+       * @note This can only be used if the TRANSPARENT property is false.
+       * @note Even if DOUBLE_SIDED property is true, the content won't be drawn on the back side, if this property is also true.
+       *
+       * @details Type Property::BOOLEAN.
+       * @SINCE_2_3.38
+       */
+      USE_BACK_FACE_PLANE,
+
+      /**
+       * @brief Color of back face plane.
+       * Default color is white (Vector3(1.0f, 1.0f, 1.0f)).
+       *
+       * @details Type Property::VECTOR3.
+       * @SINCE_2_3.38
+       */
+      BACK_FACE_PLANE_COLOR,
+    };
+  };
+
+  /**
+   * @brief Create an initialized Panel.
+   *
+   * @SINCE_2_3.38
+   * @return A handle to a newly allocated Dali resource
+   */
+  static Panel New();
+
+  /**
+   * @brief Creates an uninitialized Panel.
+   *
+   * Only derived versions can be instantiated. Calling member
+   * functions with an uninitialized Dali::Object is not allowed.
+   *
+   * @SINCE_2_3.38
+   */
+  Panel();
+
+  /**
+   * @brief Destructor.
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   *
+   * @SINCE_2_3.38
+   */
+  ~Panel();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @SINCE_2_3.38
+   * @param[in] panel Handle to an object
+   */
+  Panel(const Panel& panel);
+
+  /**
+   * @brief Move constructor
+   *
+   * @SINCE_2_3.38
+   * @param[in] rhs A reference to the moved handle
+   */
+  Panel(Panel&& rhs) noexcept;
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @SINCE_2_3.38
+   * @param[in] panel Handle to an object
+   * @return reference to this
+   */
+  Panel& operator=(const Panel& panel);
+
+  /**
+   * @brief Move assignment
+   *
+   * @SINCE_2_3.38
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this
+   */
+  Panel& operator=(Panel&& rhs) noexcept;
+
+  /**
+   * @brief Downcasts an Object handle to Panel.
+   *
+   * If handle points to a Panel, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2_3.38
+   * @param[in] handle Handle to an object
+   * @return Handle to a Panel or an uninitialized handle
+   */
+  static Panel DownCast(BaseHandle handle);
+
+  /**
+   * @brief Sets defined resolution to the Panel.
+   *
+   * The resolution is independent from the Panel size property.
+   * The resolution defines a plane that the 2D UI scene will be rendered.
+   * And the shape of the panel plane is defined by aspect ratio of the input resolution.
+   * @note The plane is cleared by white color.
+   *
+   * @SINCE_2_3.38
+   * @param[in] resolution The resolution defines panel plane.
+   */
+  void SetPanelResolution(const Vector2& resolution);
+
+  /**
+   * @brief Retrieves panel resolution
+   *
+   * @SINCE_2_3.38
+   * @return Resolution of the panel plane.
+   */
+  Vector2 GetPanelResolution() const;
+
+  /**
+   * @brief Sets root Actor of 2D UI content.
+   * The content is rendered on the plane of the Panel by using Framebuffer object.
+   *
+   * @SINCE_2_3.38
+   * @param[in] rootActor root Actor of 2D UI content.
+   * @note If new root Actor is set by this method again when a root Actor is already added,
+   * the Panel is cleared internally before the new root Actor is set.
+   */
+  void SetContent(Dali::Actor rootActor);
+
+  /**
+   * @brief Retrieves root Actor of 2D UI content.
+   *
+   * @SINCE_2_3.38
+   * @return root Actor of 2D UI content.
+   * @note If any Actor other than the root Actor has been added to the Panel
+   * with different way from SetContent method, this method might return an incorrect actor.
+   */
+  Dali::Actor GetContent() const;
+
+  /**
+   * @brief Clears the content of the panel.
+   *
+   * @SINCE_2_3.38
+   * @note The root Actor is removed from this this Panel.
+   */
+  void ClearPanel();
+
+  /**
+   * @brief Sets whether this Panel casts shadow or not.
+   * If it is true, this panel is drawn on Shadow Map.
+   *
+   * @SINCE_2_3.38
+   * @param[in] castShadow Whether this Panel casts shadow or not.
+   * @note This method affects all of the child ModelNode.
+   * However, same property of each child ModelNode does not changed respectively and it not changes parent's property.
+   */
+  void CastShadow(bool castShadow);
+
+  /**
+   * @brief Retrieves whether the Panel casts shadow or not for Light.
+   *
+   * @SINCE_2_3.38
+   * @return True if this panel casts shadow.
+   * @note IBL does not cast any shadow.
+   */
+  bool IsShadowCasting() const;
+
+  /**
+   * @brief Sets whether this Panel receives shadow or not.
+   * If it is true, shadows are drawn on this panel.
+   *
+   * @SINCE_2_3.38
+   * @param[in] receiveShadow Whether this Panel receives shadow or not.
+   * @note This method affects all of the child ModelNode.
+   * However, same property of each child ModelNode does not changed respectively and it not changes parent's property.
+   */
+  void ReceiveShadow(bool receiveShadow);
+
+  /**
+   * @brief Retrieves whether the Panel receives shadow or not for Light.
+   *
+   * @SINCE_2_3.38
+   * @return True if this panel receives shadow.
+   */
+  bool IsShadowReceiving() const;
+
+public: // Not intended for application developers
+  /// @cond internal
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   *
+   * @param[in] implementation The Control implementation
+   */
+  DALI_INTERNAL Panel(Internal::Panel& implementation);
+
+  /**
+   * @brief Allows the creation of this Control from an Internal::CustomActor pointer.
+   *
+   * @param[in] internal A pointer to the internal CustomActor
+   */
+  DALI_INTERNAL Panel(Dali::Internal::CustomActor* internal);
+  /// @endcond
+};
+
+/**
+ * @}
+ */
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_PANEL_H
index 8a1efb9..754cf10 100644 (file)
@@ -102,6 +102,16 @@ void SceneView::SelectCamera(const std::string& name)
   GetImpl(*this).SelectCamera(name);
 }
 
+void SceneView::StartCameraTransition(uint32_t index, float durationSeconds, Dali::AlphaFunction alphaFunction)
+{
+  GetImpl(*this).StartCameraTransition(index, durationSeconds, alphaFunction);
+}
+
+void SceneView::StartCameraTransition(std::string name, float durationSeconds, Dali::AlphaFunction alphaFunction)
+{
+  GetImpl(*this).StartCameraTransition(name, durationSeconds, alphaFunction);
+}
+
 void SceneView::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
 {
   GetImpl(*this).SetImageBasedLightSource(diffuseUrl, specularUrl, scaleFactor);
@@ -192,6 +202,21 @@ Quaternion SceneView::GetSkyboxOrientation() const
   return GetImpl(*this).GetSkyboxOrientation();
 }
 
+int32_t SceneView::Capture(Dali::CameraActor camera, const Vector2& size)
+{
+  return GetImpl(*this).Capture(camera, size);
+}
+
+SceneView::CaptureFinishedSignalType& SceneView::CaptureFinishedSignal()
+{
+  return GetImpl(*this).CaptureFinishedSignal();
+}
+
+SceneView::CameraTransitionFinishedSignalType& SceneView::CameraTransitionFinishedSignal()
+{
+  return GetImpl(*this).CameraTransitionFinishedSignal();
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index ff3d1c2..4e72412 100644 (file)
@@ -20,7 +20,9 @@
 
 // EXTERNAL INCLUDES
 #include <dali-toolkit/public-api/controls/control.h>
+#include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali/public-api/actors/camera-actor.h>
+#include <dali/public-api/animation/animation.h>
 #include <dali/public-api/common/dali-common.h>
 
 // INTERNAL INCLUDES
@@ -87,6 +89,7 @@ class SceneView;
  * And since SceneView is a Control, it can be placed together with other 2D UI components in the DALi window.
  *
  * @note We support to render model well only if glsl version is higher than 300.
+ * @note We do not support Toolkit::RenderEffect when UseFrameBuffer(false).
  *
  * @SINCE_2_1.38
  * @code
@@ -150,6 +153,21 @@ public:
   };
 
 public:
+
+  /**
+   * @brief Typedef for capture finished signals sent by this class.
+   *
+   * @SINCE_2_3.37
+   */
+  typedef Signal<void(SceneView, int32_t, const Dali::Toolkit::ImageUrl&)> CaptureFinishedSignalType;
+
+  /**
+   * @brief Typedef for camera transition finished signals sent by this class.
+   *
+   * @SINCE_2_3.37
+   */
+  typedef Signal<void(SceneView)> CameraTransitionFinishedSignalType;
+
   /**
    * @brief Create an initialized SceneView.
    *
@@ -295,6 +313,7 @@ public:
    *
    * @SINCE_2_1.38
    * @param[in] index Index of CameraActor to be used as a selected camera.
+   * @note If the Camera is not added in this Scene, this method adds it on SceneView root.
    */
   void SelectCamera(uint32_t index);
 
@@ -303,10 +322,39 @@ public:
    *
    * @SINCE_2_1.38
    * @param[in] name string keyword of CameraActor to be used as a selected camera.
+   * @note If the Camera is not added in this Scene, this method adds it on SceneView root.
    */
   void SelectCamera(const std::string& name);
 
   /**
+   * @brief Starts camera transition from currently selected camera to a camera of index.
+   * Camera Position, Orientation and FieldOfView(Orthogrpahic Size) are smoothly animated.
+   *
+   * @SINCE_2_3.37
+   * @param[in] index Index of destination Camera of Camera transition.
+   * @param[in] durationSeconds The duration in seconds.
+   * @param[in] alphaFunction The alpha function to apply.
+   * @note The selected camera is switched to the Camera of the index when the transition is finished.
+   * During camera transition, Selected Camera should not be changed by using SelectCamera() or StartCameraTransition() method.
+   * During camera transition, Camera properties of Selected Camera should not be changed.
+   */
+  void StartCameraTransition(uint32_t index, float durationSeconds, Dali::AlphaFunction alphaFunction = AlphaFunction::DEFAULT);
+
+  /**
+   * @brief Starts camera transition from currently selected camera to a camera of index.
+   * Camera Position, Orientation and FieldOfView(Orthogrpahic Size) are smoothly animated.
+   *
+   * @SINCE_2_3.37
+   * @param[in] name string keyword of destination Camera of Camera transition.
+   * @param[in] durationSeconds The duration in seconds.
+   * @param[in] alphaFunction The alpha function to apply.
+   * @note The selected camera is switched to the Camera of the input name when the transition is finished.
+   * During camera transition, Selected Camera should not be changed by using SelectCamera() or StartCameraTransition() method.
+   * During camera transition, Camera properties of Selected Camera should not be changed.
+   */
+  void StartCameraTransition(std::string name, float durationSeconds, Dali::AlphaFunction alphaFunction = AlphaFunction::DEFAULT);
+
+  /**
    * @brief Sets Image Based Light Source to apply it on the all Models those added on this SceneView.
    *
    * @SINCE_2_1.38
@@ -352,6 +400,7 @@ public:
    * @brief Sets whether to use FBO or not for the SceneView.
    * If useFramebuffer is true, rendering result of SceneView is drawn on FBO and it is mapping on this SceneView plane.
    * If useFramebuffer is false, each item in SceneView is rendered on window directly.
+   * Note that Toolkit::RenderEffect is not supported in this case.
    * Default is false.
    *
    * @SINCE_2_1.38
@@ -484,6 +533,36 @@ public:
    */
   Quaternion GetSkyboxOrientation() const;
 
+  /**
+   * @brief Requests to capture this SceneView with the Camera.
+   *
+   * @SINCE_2_3.25
+   * @param[in] camera CameraActor to be used for capture.
+   * @param[in] size captured size.
+   * @note The input camera should not be used for any other purpose during Capture.
+   * (Simultaneous usage elsewhere may result in incorrect rendering.)
+   * @note The camera is required to be added in this SceneView. (But should not be a selected camera.)
+   * @note If the SceneView is disconnected from Scene, the left capture requests are canceled.
+   * @return capture id that id unique value to distinguish each requiest.
+   */
+  int32_t Capture(Dali::CameraActor camera, const Vector2& size);
+
+  /**
+   * @brief Get capture finished signal.
+   *
+   * @SINCE_2_3.25
+   * @return finished signal instance.
+   */
+  CaptureFinishedSignalType& CaptureFinishedSignal();
+
+  /**
+   * @brief Get camera transition finished signal.
+   *
+   * @SINCE_2_3.37
+   * @return camera transition finished signal instance.
+   */
+  CameraTransitionFinishedSignalType& CameraTransitionFinishedSignal();
+
 public: // Not intended for application developers
   /// @cond internal
   /**
index 0c394df..4e153f6 100644 (file)
@@ -5,6 +5,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_public_api_dir}/algorithm/path-finder.cpp
        ${scene3d_public_api_dir}/algorithm/path-finder-waypoint.cpp
        ${scene3d_public_api_dir}/controls/model/model.cpp
+       ${scene3d_public_api_dir}/controls/panel/panel.cpp
        ${scene3d_public_api_dir}/controls/scene-view/scene-view.cpp
        ${scene3d_public_api_dir}/light/light.cpp
        ${scene3d_public_api_dir}/loader/alpha-function-helper.cpp
index 9c74f23..65a3271 100644 (file)
 
 namespace
 {
-#define TOKEN_STRING(x) #x
-std::string GetDaliImagePath()
+static constexpr float DEFAULT_INTENSITY = 1.0f;
+
+/**
+ * @brief Request to load default brdf pixel data at worker thread.
+ */
+static void RequestLoadBrdfPixelData()
 {
-  return (nullptr == DALI_IMAGE_DIR) ? Dali::EnvironmentVariable::GetEnvironmentVariable(TOKEN_STRING(DALI_IMAGE_DIR)) : DALI_IMAGE_DIR;
+  /// GetDefaultBrdfPixelData() will load the brdf pixel data if we don't load it before.
+  [[maybe_unused]] auto ret = Dali::Scene3D::Internal::ImageResourceLoader::GetDefaultBrdfPixelData();
 }
 
-static constexpr float DEFAULT_INTENSITY = 1.0f;
-
 } // unnamed namespace
 
 namespace Dali::Scene3D::Loader
 {
-namespace
-{
-const char* PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME = "brdfLUT.png";
-}
-PixelData EnvironmentDefinition::mBrdfPixelData;
-Texture   EnvironmentDefinition::mBrdfTexture;
-bool      EnvironmentDefinition::mIsBrdfLoaded = false;
-
-Dali::Texture EnvironmentDefinition::GetBrdfTexture()
-{
-  if(!mBrdfTexture)
-  {
-    if(!mIsBrdfLoaded)
-    {
-      LoadBrdfTexture();
-    }
-    mBrdfTexture = Texture::New(TextureType::TEXTURE_2D, mBrdfPixelData.GetPixelFormat(), mBrdfPixelData.GetWidth(), mBrdfPixelData.GetHeight());
-    mBrdfTexture.Upload(mBrdfPixelData);
-  }
-  return mBrdfTexture;
-}
-
 EnvironmentDefinition::RawData
 EnvironmentDefinition::LoadRaw(const std::string& environmentsPath)
 {
@@ -89,7 +70,7 @@ EnvironmentDefinition::LoadRaw(const std::string& environmentsPath)
 
   if(mUseBrdfTexture)
   {
-    LoadBrdfTexture();
+    RequestLoadBrdfPixelData();
   }
   return raw;
 }
@@ -113,7 +94,7 @@ EnvironmentDefinition::Textures EnvironmentDefinition::Load(RawData&& raw)
 
   if(mUseBrdfTexture)
   {
-    textures.mBrdf = GetBrdfTexture();
+    textures.mBrdf = Dali::Scene3D::Internal::ImageResourceLoader::GetDefaultBrdfTexture();
   }
   return textures;
 }
@@ -123,21 +104,4 @@ float EnvironmentDefinition::GetDefaultIntensity()
   return DEFAULT_INTENSITY;
 }
 
-void EnvironmentDefinition::LoadBrdfTexture()
-{
-  static Dali::Mutex mutex;
-  {
-    Mutex::ScopedLock lock(mutex);
-    if(!mIsBrdfLoaded)
-    {
-      Devel::PixelBuffer pixelBuffer = LoadImageFromFile(GetDaliImagePath() + PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME);
-      if(pixelBuffer)
-      {
-        mBrdfPixelData = Devel::PixelBuffer::Convert(pixelBuffer);
-        mIsBrdfLoaded  = true;
-      }
-    }
-  }
-}
-
 } // namespace Dali::Scene3D::Loader
\ No newline at end of file
index 39aa53a..99931d7 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef DALI_SCENE3D_LOADER_ENVIRONMENT_DEFINITION_H
 #define DALI_SCENE3D_LOADER_ENVIRONMENT_DEFINITION_H
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -65,8 +65,6 @@ struct DALI_SCENE3D_API EnvironmentDefinition
   EnvironmentDefinition(EnvironmentDefinition&&) = default;
   EnvironmentDefinition& operator=(EnvironmentDefinition&&) = default;
 
-  static Dali::Texture GetBrdfTexture();
-
   /**
    * @brief Loads raw pixel data for the given diffuse and specular maps.
    * @SINCE_2_0.7
@@ -90,11 +88,6 @@ struct DALI_SCENE3D_API EnvironmentDefinition
    */
   static float GetDefaultIntensity();
 
-private:
-  /// @cond internal
-  static void LoadBrdfTexture();
-  /// @endcond internal
-
 public: // DATA
   std::string              mDiffuseMapPath;
   std::string              mSpecularMapPath;
@@ -103,11 +96,6 @@ public: // DATA
   Vector3                  mYDirection      = Vector3::ONE;
   float                    mIblIntensity    = 1.0f;
   bool                     mUseBrdfTexture  = false;
-
-private:
-  static PixelData mBrdfPixelData;
-  static Texture   mBrdfTexture;
-  static bool      mIsBrdfLoaded;
 };
 
 } // namespace Dali::Scene3D::Loader
index 81b42fd..0691c35 100644 (file)
@@ -38,7 +38,7 @@ const float MILLISECONDS_TO_SECONDS = 0.001f;
 \r
 struct BlendShape\r
 {\r
-  std::vector<std::vector<float>> mKeys{{}};\r
+  std::vector<std::vector<float>> mKeys{};\r
   std::string_view                mNodeName{};\r
   uint32_t                        mNumberOfMorphTarget{0u};\r
   std::string_view                mVersion{};\r
@@ -49,7 +49,7 @@ struct BlendShape
 struct FacialAnimation\r
 {\r
   std::string_view        mName{};\r
-  std::vector<BlendShape> mBlendShapes{{}};\r
+  std::vector<BlendShape> mBlendShapes{};\r
   std::string_view        mVersion{};\r
   uint32_t                mNumberOfShapes{0u};\r
   std::vector<uint32_t>   mTime{};\r
@@ -165,6 +165,12 @@ Dali::Scene3D::Loader::AnimationDefinition LoadFacialAnimationInternal(json::uni
       animatedProperty.mPropertyName = weightPropertyStream.str();\r
 \r
       animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
+      // Make initial progress value follow the first parameter, if their is nothing defined at 0'th duration.\r
+      if(DALI_UNLIKELY(facialAnimation.mNumberOfFrames > 0 && facialAnimation.mTime[0] != 0))\r
+      {\r
+        animatedProperty.mKeyFrames.Add(0.0f, blendShape.mKeys[0][0]);\r
+      }\r
+\r
       for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
       {\r
         const float progress = Dali::EqualsZero(animationDefinition.GetDuration()) ? 0.0f : MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
index 4d33ef5..149a2c3 100644 (file)
 #include <dali-scene3d/public-api/loader/ktx-loader.h>
 
 // EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/pixel-data-integ.h>
 #include <dali/public-api/rendering/texture.h>
+#include <dali/devel-api/adaptor-framework/file-stream.h>
 #include <fstream>
 #include <memory>
 
@@ -188,26 +190,33 @@ bool ConvertPixelFormat(const uint32_t ktxPixelFormat, Pixel::Format& format)
 
 bool LoadKtxData(const std::string& path, EnvironmentMapData& environmentMapData)
 {
-  std::fstream fp(path, std::ios::in | std::ios::binary);
-  if(fp.is_open() == false)
+  Dali::FileStream fileStream(path, FileStream::READ | FileStream::BINARY);
+  auto&            stream = fileStream.GetStream();
+  if(!stream.good() || !stream.rdbuf()->in_avail())
   {
+    DALI_LOG_ERROR("Load ktx data failed, path : %s\n", path.c_str());
     return false;
   }
 
   KtxFileHeader header;
-  if(fp.read(reinterpret_cast<char*>(&header), sizeof(KtxFileHeader)).good() == false)
+  stream.clear();
+  stream.seekg(0u, stream.beg);
+  if(stream.read(reinterpret_cast<char*>(&header), sizeof(KtxFileHeader)).good() == false)
   {
+    DALI_LOG_ERROR("Unable to read ktx header in file, %s\n", path.c_str());
     return false;
   }
 
   if(!header.IsIdentifierValid())
   {
+    DALI_LOG_ERROR("KTX Header Identifier is not valid, %s\n", path.c_str());
     return false;
   }
 
   // Skip the key-values:
-  if(fp.seekg(static_cast<std::streamoff>(static_cast<std::size_t>(header.bytesOfKeyValueData)), fp.cur).good() == false)
+  if(stream.seekg(static_cast<std::streamoff>(static_cast<std::size_t>(header.bytesOfKeyValueData)), stream.cur).good() == false)
   {
+    DALI_LOG_ERROR("Unable to skip key-values in KTX file, %s\n", path.c_str());
     return false;
   }
 
@@ -230,8 +239,9 @@ bool LoadKtxData(const std::string& path, EnvironmentMapData& environmentMapData
   for(uint32_t mipmapLevel = 0u; mipmapLevel < header.numberOfMipmapLevels; ++mipmapLevel)
   {
     uint32_t byteSize = 0u;
-    if(fp.read(reinterpret_cast<char*>(&byteSize), sizeof(byteSize)).good() == false)
+    if(stream.read(reinterpret_cast<char*>(&byteSize), sizeof(byteSize)).good() == false)
     {
+      DALI_LOG_ERROR("Unable to read byteSize from KTX stream, %s\n", path.c_str());
       return false;
     }
 
@@ -245,8 +255,9 @@ bool LoadKtxData(const std::string& path, EnvironmentMapData& environmentMapData
       for(uint32_t face = 0u; face < header.numberOfFaces; ++face)
       {
         std::unique_ptr<uint8_t, void (*)(uint8_t*)> img(new uint8_t[byteSize], FreeBuffer);
-        if(fp.read(reinterpret_cast<char*>(img.get()), static_cast<std::streamsize>(static_cast<size_t>(byteSize))).good() == false)
+        if(stream.read(reinterpret_cast<char*>(img.get()), static_cast<std::streamsize>(static_cast<size_t>(byteSize))).good() == false)
         {
+          DALI_LOG_ERROR("Unable to read data from KTX stream, %s\n", path.c_str());
           return false;
         }
         environmentMapData.mPixelData[face][mipmapLevel] = Dali::Integration::NewPixelDataWithReleaseAfterUpload(img.release(), byteSize, header.pixelWidth, header.pixelHeight, 0u, daliformat, PixelData::DELETE_ARRAY);
index b61005b..27a460b 100644 (file)
@@ -22,7 +22,9 @@
 #include <dali-toolkit/devel-api/builder/base64-encoding.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/pixel-data-integ.h>
+#include <dali/integration-api/trace.h>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/common/image-resource-loader.h>
@@ -39,6 +41,8 @@ const Matrix3 TextureDefinition::DEFAULT_TRANSFORM = Matrix3::IDENTITY;
 
 namespace
 {
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_MODEL_PERFORMANCE_MARKER, false);
+
 constexpr SamplerFlags::Type FILTER_MODES_FROM_DALI[]{
   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
   SamplerFlags::FILTER_LINEAR,
@@ -89,11 +93,21 @@ Dali::PixelData LoadImageResource(const std::string& resourcePath,
   Dali::PixelData pixelData;
   if(!textureDefinition.mTextureBuffer.empty())
   {
+    DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOAD_IMAGE_FROM_BUFFER", [&](std::ostringstream& oss)
+                                            { oss << "[s:" << textureDefinition.mTextureBuffer.size() << "]"; });
     Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(textureDefinition.mTextureBuffer.data(), textureDefinition.mTextureBuffer.size(), textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
     if(pixelBuffer)
     {
       pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
     }
+    DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOAD_IMAGE_FROM_BUFFER", [&](std::ostringstream& oss)
+                                          {
+      oss << "[";
+      if(pixelData)
+      {
+        oss << "d:" << pixelData.GetWidth() << "x" << pixelData.GetHeight() << " f:" << pixelData.GetPixelFormat() << " ";
+      }
+      oss << "s:" << textureDefinition.mTextureBuffer.size() << "]"; });
   }
   else if(textureDefinition.mImageUri.find(EMBEDDED_DATA_PREFIX.data()) == 0 && textureDefinition.mImageUri.find(EMBEDDED_DATA_IMAGE_MEDIA_TYPE.data(), EMBEDDED_DATA_PREFIX.length()) == EMBEDDED_DATA_PREFIX.length())
   {
@@ -106,11 +120,21 @@ Dali::PixelData LoadImageResource(const std::string& resourcePath,
       Dali::Toolkit::DecodeBase64FromString(data, buffer);
       uint32_t bufferSize = buffer.size();
 
+      DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOAD_IMAGE_FROM_BUFFER", [&](std::ostringstream& oss)
+                                              { oss << "[embedded s:" << bufferSize << "]"; });
       Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(reinterpret_cast<uint8_t*>(buffer.data()), bufferSize, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
       if(pixelBuffer)
       {
         pixelData = Dali::Devel::PixelBuffer::Convert(pixelBuffer, true);
       }
+      DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_MODEL_LOAD_IMAGE_FROM_BUFFER", [&](std::ostringstream& oss)
+                                            {
+        oss << "[";
+        if(pixelData)
+        {
+          oss << "d:" << pixelData.GetWidth() << "x" << pixelData.GetHeight() << " f:" << pixelData.GetPixelFormat() << " ";
+        }
+        oss << "embedded s:" << bufferSize << "]"; });
     }
   }
   else
@@ -201,7 +225,8 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath)
 
   // Load textures
   auto iTexture   = mTextureStages.begin();
-  auto checkStage = [&](uint32_t flags) {
+  auto checkStage = [&](uint32_t flags)
+  {
     return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
   };
 
@@ -252,7 +277,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath)
       buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
       buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
       buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
-      raw.mTextures.push_back({Dali::Integration::NewPixelDataWithReleaseAfterUpload(buffer, bufferSize, 1, 1, 0, format, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
+      raw.mTextures.push_back({Dali::PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
 
     // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
@@ -384,7 +409,8 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
 
 bool MaterialDefinition::CheckTextures(uint32_t flags) const
 {
-  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) { return MaskMatch(ts.mSemantic, flags); }) != mTextureStages.end();
+  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts)
+                      { return MaskMatch(ts.mSemantic, flags); }) != mTextureStages.end();
 }
 
 } // namespace Loader
index 6728790..a417fd0 100644 (file)
@@ -44,7 +44,7 @@ struct LoadAccessorInputs
   MeshDefinition::RawData&  rawData;
   MeshDefinition::Accessor& accessor;
   uint32_t                  flags;
-  std::fstream&             fileStream;
+  std::iostream*            fileStream;
   std::string&              meshPath;
   BufferDefinition::Vector& buffers;
 };
@@ -54,7 +54,7 @@ struct LoadAccessorListInputs
   MeshDefinition::RawData&               rawData;
   std::vector<MeshDefinition::Accessor>& accessors;
   uint32_t                               flags;
-  std::fstream&                          fileStream;
+  std::iostream*                         fileStream;
   std::string&                           meshPath;
   BufferDefinition::Vector&              buffers;
 };
@@ -816,10 +816,10 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::vector<MeshDefiniti
   }
 }
 
-std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
+std::iostream& GetAvailableData(std::iostream* meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
 {
-  auto& stream  = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream();
-  availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri();
+  auto& stream  = meshStream ? *meshStream : buffer.GetBufferStream();
+  availablePath = meshStream ? meshPath : buffer.GetUri();
   return stream;
 }
 
@@ -855,6 +855,7 @@ void LoadIndices(LoadAccessorInputs indicesInput)
       auto&       stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
       if(!ReadAccessor(indicesInput.accessor, stream, reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data())))
       {
+        DALI_LOG_ERROR("Failed to read indices from %s\n", path.c_str());
         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
       }
     }
@@ -871,6 +872,7 @@ void LoadIndices(LoadAccessorInputs indicesInput)
       auto&       stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
       if(!ReadAccessor(indicesInput.accessor, stream, u8s))
       {
+        DALI_LOG_ERROR("Failed to read indices from %s\n", path.c_str());
         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
       }
 
@@ -894,6 +896,7 @@ void LoadIndices(LoadAccessorInputs indicesInput)
       auto&       stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
       if(!ReadAccessor(indicesInput.accessor, stream, reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data())))
       {
+        DALI_LOG_ERROR("Failed to read indices from %s\n", path.c_str());
         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indicesInput.accessor from '" << path << "'.";
       }
     }
@@ -1211,7 +1214,7 @@ void LoadColors(LoadAccessorListInputs colorsInput)
       colorsInput.rawData.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
     }
   }
-  else
+  else if(!colorsInput.rawData.mAttribs.empty())
   {
     std::vector<uint8_t> buffer(colorsInput.rawData.mAttribs[0].mNumElements * sizeof(Vector4));
     auto                 colors = reinterpret_cast<Vector4*>(buffer.data());
@@ -1225,7 +1228,7 @@ void LoadColors(LoadAccessorListInputs colorsInput)
   }
 }
 
-void LoadBlendShapes(MeshDefinition::RawData& rawData, std::vector<MeshDefinition::BlendShape>& blendShapes, MeshDefinition::Blob& blendShapeHeader, BlendShapes::Version blendShapeVersion, uint32_t numberOfVertices, std::fstream& fileStream, BufferDefinition::Vector& buffers)
+void LoadBlendShapes(MeshDefinition::RawData& rawData, std::vector<MeshDefinition::BlendShape>& blendShapes, MeshDefinition::Blob& blendShapeHeader, BlendShapes::Version blendShapeVersion, uint32_t numberOfVertices, std::istream* fileStream, BufferDefinition::Vector& buffers)
 {
   // Calculate the Blob for the blend shapes.
   MeshDefinition::Blob blendShapesBlob;
@@ -1269,10 +1272,10 @@ void LoadBlendShapes(MeshDefinition::RawData& rawData, std::vector<MeshDefinitio
       CalculateTextureSize(totalTextureSize, textureWidth, textureHeight);
       calculateGltf2BlendShapes = true;
     }
-    else
+    else if(fileStream)
     {
       uint16_t header[2u];
-      ReadBlob(blendShapeHeader, fileStream, reinterpret_cast<uint8_t*>(header));
+      ReadBlob(blendShapeHeader, *fileStream, reinterpret_cast<uint8_t*>(header));
       textureWidth  = header[0u];
       textureHeight = header[1u];
     }
@@ -1294,16 +1297,16 @@ void LoadBlendShapes(MeshDefinition::RawData& rawData, std::vector<MeshDefinitio
 
       if(blendShapesBlob.IsDefined())
       {
-        if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer))
+        if(fileStream && ReadBlob(blendShapesBlob, *fileStream, geometryBuffer))
         {
           unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
         }
       }
 
       // Read the unnormalize factors.
-      if(unnormalizeFactorBlob.IsDefined())
+      if(unnormalizeFactorBlob.IsDefined() && fileStream)
       {
-        ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast<uint8_t*>(&rawData.mBlendShapeUnnormalizeFactor[0u]));
+        ReadBlob(unnormalizeFactorBlob, *fileStream, reinterpret_cast<uint8_t*>(&rawData.mBlendShapeUnnormalizeFactor[0u]));
       }
     }
     rawData.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
@@ -1473,20 +1476,24 @@ MeshDefinition::RawData
 MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers)
 {
   RawData raw;
-  if(IsQuad())
+  if (IsQuad())
   {
     return raw;
   }
 
   std::string meshPath;
   meshPath = modelsPath + mUri;
-  std::fstream fileStream;
-  if(!mUri.empty())
+
+  std::unique_ptr<Dali::FileStream> daliFileStream(nullptr);
+  std::iostream* fileStream = nullptr;
+  if (!mUri.empty())
   {
-    fileStream.open(meshPath, std::ios::in | std::ios::binary);
-    if(!fileStream.is_open())
+    daliFileStream.reset(new Dali::FileStream(meshPath, FileStream::READ | FileStream::BINARY));
+    fileStream = &daliFileStream->GetStream();
+    if(!fileStream->good() || !fileStream->rdbuf()->in_avail())
     {
       DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str());
+      fileStream = nullptr;
     }
   }
 
index 013b4f3..48a222a 100644 (file)
@@ -115,12 +115,18 @@ void NodeDefinition::Renderable::ReflectResources(IResourceReflector& reflector)
 
 void NodeDefinition::Renderable::OnCreate(const NodeDefinition& nodeDefinition, CreateParams& params, ModelNode& node) const
 {
+  // TODO : Need to keep this default geometry only 1 times per each adaptor.
+  Geometry defaultGeometry = Geometry::New();
+  CreateRenderer(nodeDefinition, params, defaultGeometry, node);
+}
+
+void NodeDefinition::Renderable::CreateRenderer(const NodeDefinition& nodeDefinition, CreateParams& params, Geometry& geometry, ModelNode& node) const
+{
   DALI_ASSERT_DEBUG(mShaderIdx != INVALID_INDEX);
   auto&  resources = params.mResources;
   Shader shader    = resources.mShaders[mShaderIdx].second;
 
-  static Geometry defaultGeometry = Geometry::New();
-  Renderer        renderer        = Renderer::New(defaultGeometry, shader);
+  Renderer renderer = Renderer::New(geometry, shader);
 
   RendererState::Apply(resources.mShaders[mShaderIdx].first.mRendererState, renderer);
 
@@ -257,7 +263,12 @@ void ModelRenderable::ReflectResources(IResourceReflector& reflector)
 void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const
 {
   DALI_ASSERT_DEBUG(mMeshIdx != INVALID_INDEX);
+
+  auto& resources = params.mResources;
+  auto& mesh      = resources.mMeshes[mMeshIdx];
+
   ShaderOption::HashType shaderOptionHash{0u};
+  Renderer               renderer;
   if(mShaderIdx == INVALID_INDEX)
   {
     ShaderOption option = params.mShaderManager->ProduceShaderOption(params.mResources.mMaterials[mMaterialIdx].first,
@@ -265,8 +276,7 @@ void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinit
     shaderOptionHash    = option.GetOptionHash();
     Shader shader       = params.mShaderManager->ProduceShader(option);
 
-    static Geometry defaultGeometry = Geometry::New();
-    Renderer        renderer        = Renderer::New(defaultGeometry, shader);
+    renderer = Renderer::New(mesh.second.geometry, shader);
 
     RendererState::Apply(params.mShaderManager->GetRendererState(params.mResources.mMaterials[mMaterialIdx].first), renderer);
     Internal::GetImplementation(node).UpdateShader(params.mShaderManager);
@@ -274,16 +284,11 @@ void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinit
   }
   else
   {
-    Renderable::OnCreate(nodeDefinition, params, node);
+    Renderable::CreateRenderer(nodeDefinition, params, mesh.second.geometry, node);
+    DALI_ASSERT_ALWAYS(node.GetRendererCount() > 0u && "CreateRenderer failed!");
+    renderer = node.GetRendererAt(node.GetRendererCount() - 1u);
   }
 
-  auto& resources = params.mResources;
-  auto& mesh      = resources.mMeshes[mMeshIdx];
-
-  auto     renderer = node.GetRendererAt(node.GetRendererCount() - 1u);
-  Geometry geometry = mesh.second.geometry;
-  renderer.SetGeometry(geometry);
-
   TextureSet textures = resources.mMaterials[mMaterialIdx].second;
   // Set the blend shape texture.
   if(mesh.second.blendShapeGeometry)
index c1cea09..729ca6e 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef DALI_SCENE3D_LOADER_NODE_DEFINITION_H_
 #define DALI_SCENE3D_LOADER_NODE_DEFINITION_H_
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -180,6 +180,9 @@ public: // TYPES
     virtual void RegisterResources(IResourceReceiver& receiver) const;
     virtual void ReflectResources(IResourceReflector& reflector);
     virtual void OnCreate(const NodeDefinition& nodeDefinition, CreateParams& params, ModelNode& node) const;
+
+  protected:
+    void CreateRenderer(const NodeDefinition& nodeDefinition, CreateParams& params, Geometry& geometry, ModelNode& node) const;
   };
 
   struct CustomizationDefinition
index 8cd505e..b6978e2 100644 (file)
@@ -50,6 +50,7 @@ ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
   mDefines(other.mDefines),
   mHints(other.mHints),
   mUniforms(other.mUniforms),
+  mShadowOptionHash(other.mShadowOptionHash),
   mUseBuiltInShader(other.mUseBuiltInShader)
 {
 }
@@ -193,12 +194,23 @@ Shader ShaderDefinition::Load(RawData&& raw) const
   map[0]["fragment"]      = raw.mFragmentShaderSource;
   map[0]["renderPassTag"] = 0;
   map[0]["hints"]         = static_cast<Shader::Hint::Value>(hints);
-  map[0]["name"]          = "SCENE3D_PBR";
 
   map[1]["vertex"]        = raw.mShadowVertexShaderSource;
   map[1]["fragment"]      = raw.mShadowFragmentShaderSource;
   map[1]["renderPassTag"] = 10;
-  map[1]["name"]          = "SCENE3D_SHADOW_MAP";
+
+  if(mUseBuiltInShader)
+  {
+    std::ostringstream oss;
+    oss << "_0x" << std::hex << mShadowOptionHash;
+    map[0]["name"] = std::string("SCENE3D_PBR") + oss.str();
+    map[1]["name"] = std::string("SCENE3D_SHADOW_MAP") + oss.str();
+  }
+  else
+  {
+    map[0]["name"] = "SCENE3D_CUSTOM";
+    map[1]["name"] = "SCENE3D_CUSTOM_SHADOW";
+  }
 
   Property::Array array;
   array.PushBack(map[0]);
index 743d343..af2a640 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef DALI_SCENE3D_LOADER_SHADER_DEFINITION_H
 #define DALI_SCENE3D_LOADER_SHADER_DEFINITION_H
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -81,7 +81,9 @@ public: // DATA
   std::vector<ShaderOption::MacroDefinition> mMacros;
   std::vector<std::string>                   mHints;
   Property::Map                              mUniforms;
-  bool                                       mUseBuiltInShader{false};
+
+  uint64_t mShadowOptionHash{0u};
+  bool     mUseBuiltInShader{false};
 };
 
 } // namespace Dali::Scene3D::Loader
index 776092c..4625e18 100644 (file)
@@ -236,6 +236,7 @@ Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
     DALI_LOG_INFO(gLogFilter, Debug::Concise, "Creating new shader: hash: %lx\n", hash);
     ShaderDefinition shaderDef;
     shaderDef.mUseBuiltInShader = true;
+    shaderDef.mShadowOptionHash = hash;
 
     shaderOption.GetDefines(shaderDef.mDefines);
     shaderDef.mMacros                  = shaderOption.GetMacroDefinitions();
@@ -413,6 +414,17 @@ void ShaderManager::RemoveLightConstraint(uint32_t lightIndex)
 
 void ShaderManager::SetShadowUniformToShader(Dali::Shader shader)
 {
+  uint32_t index = mImpl->mLights.size();
+  for(uint32_t i = 0; i < mImpl->mLights.size(); ++i)
+  {
+    if(mImpl->mLights[i] == mImpl->mShadowLight)
+    {
+      index = i;
+      break;
+    }
+  }
+
+  shader.RegisterProperty("uShadowLightIndex", index < mImpl->mLights.size() ? static_cast<int32_t>(index) : -1);
   shader.RegisterProperty("uShadowIntensity", mImpl->mShadowLight.GetShadowIntensity());
   shader.RegisterProperty("uShadowBias", mImpl->mShadowLight.GetShadowBias());
   shader.RegisterProperty("uEnableShadowSoftFiltering", static_cast<int>(mImpl->mShadowLight.IsShadowSoftFilteringEnabled()));
index 9120b65..3b2aa17 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 // EXTERNAL INCLUDES
 #include <dali/public-api/animation/constraints.h>
 #include <dali/public-api/common/vector-wrapper.h>
+#include <dali/devel-api/adaptor-framework/file-loader.h>
 #include <stdarg.h>
 #include <cstring>
 #include <fstream>
@@ -85,12 +86,12 @@ std::string FormatString(const char* format, ...)
 
 std::string LoadTextFile(const char* path, bool* fail)
 {
-  std::ifstream inFile(path);
-  if(inFile)
+  std::streampos     fileSize;
+  Dali::Vector<char> fileContent;
+
+  if(FileLoader::ReadFile(path, fileSize, fileContent, FileLoader::TEXT))
   {
-    std::istreambuf_iterator<char> eos;
-    std::istreambuf_iterator<char> i(inFile.rdbuf());
-    return std::string(i, eos);
+    return std::string(fileContent.Begin(), fileSize);
   }
   else if(fail)
   {
index ab8856e..72ecf34 100644 (file)
@@ -243,7 +243,7 @@ public: // Public Method
    * @brief Sets whether this ModelNode casts shadow or not.
    * If it is true, this ModelNode is drawn on Shadow Map.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @param[in] castShadow Whether this ModelNode casts shadow or not.
    * @note This method affects only for this ModelNode.
    */
@@ -252,7 +252,7 @@ public: // Public Method
   /**
    * @brief Retrieves whether the ModelNode cast shadow or not for Light.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @return True if this ModelNode cast shadow.
    * @note IBL does not cast any shadow.
    */
@@ -262,7 +262,7 @@ public: // Public Method
    * @brief Sets whether this ModelNode receives shadow or not.
    * If it is true, shadows are drawn on this ModelNode.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @param[in] receiveShadow Whether this ModelNode receives shadow or not.
    * @note This method affects only for this ModelNode.
    */
@@ -271,7 +271,7 @@ public: // Public Method
   /**
    * @brief Retrieves whether the ModelNode receives shadow or not for Light.
    *
-   * @SINCE_2_3.99
+   * @SINCE_2_3.37
    * @return True if this ModelNode receives shadow.
    */
   bool IsShadowReceiving() const;
index edee27e..f80d0ac 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -61,6 +61,9 @@
 
 #include <dali-toolkit/public-api/styling/style-manager.h>
 
+#include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
+#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
+
 #include <dali-toolkit/public-api/text/text-enumerations.h>
 
 #include <dali-toolkit/public-api/transition/fade-transition.h>
index ecc56de..683e070 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_ASSET_MANAGER_DEVEL_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
 #include <string>
 
 // INTERNAL INCLUDES
@@ -30,7 +31,7 @@ namespace Toolkit
 /**
  * @brief Retrieves the file system path of the assets.
  */
-class AssetManager
+class DALI_TOOLKIT_API AssetManager
 {
 public:
   /**
index a00cd35..db2c5da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -83,6 +83,11 @@ void CanvasView::RemoveAllDrawables()
   Dali::Toolkit::GetImpl(*this).RemoveAllDrawables();
 }
 
+void CanvasView::RequestRasterization()
+{
+  Dali::Toolkit::GetImpl(*this).RequestRasterization();
+}
+
 CanvasView::CanvasView(Internal::CanvasView& implementation)
 : Control(implementation)
 {
index 026d3fa..db5c900 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_CANVAS_VIEW_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -87,10 +87,19 @@ public:
       /**
        * @brief Whether to load the canvas synchronously.
        * @details Name "synchronousLoading", type Property::BOOLEAN.
+       * @note Default is true.
        */
       SYNCHRONOUS_LOADING,
+
+      /**
+       * @brief Whether to request rasterize canvas manually or automatically
+       * @details Name "rasterizationRequestManually", type Property::BOOLEAN.
+       * @note Default is false.
+       */
+      RASTERIZATION_REQUEST_MANUALLY,
     };
   };
+
 public:
   /**
    * @brief Creates an uninitialized CanvasView.
@@ -180,6 +189,11 @@ public:
    */
   void RemoveAllDrawables();
 
+  /**
+   * @brief Request to rasterize the CanvasView.
+   */
+  void RequestRasterization();
+
 public: // Not intended for application developers
   /// @cond internal
   /**
index 7e6981b..66ddc33 100644 (file)
@@ -41,6 +41,10 @@ namespace Dali::Toolkit::DevelControl
 {
 namespace
 {
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_CONTROL_ACCESSIBLE");
+#endif
+
 constexpr const char* ATTR_IMG_SRC_KEY = "imgSrc";
 
 std::string GetLocaleText(std::string string, const char* domain = "dali-toolkit")
@@ -108,6 +112,95 @@ std::string FetchImageSrc(const Toolkit::ImageView& imageView)
   return {};
 }
 
+bool IsAtspiRole(int32_t rawRole)
+{
+  return rawRole >= static_cast<int32_t>(Dali::Accessibility::Role::INVALID) && rawRole < static_cast<int32_t>(Dali::Accessibility::Role::MAX_COUNT);
+}
+
+bool IsRoleV2(int32_t rawRole)
+{
+  return rawRole >= static_cast<int32_t>(ROLE_START_INDEX) && rawRole < static_cast<int32_t>(AccessibilityRole::MAX_COUNT);
+}
+
+#define TO_V1_ROLE_TYPE(v2RoleType, v1RoleType) \
+  case AccessibilityRole::v2RoleType:           \
+  {                                             \
+    return Role::v1RoleType;                    \
+  }
+#define TO_SAME_ROLE_TYPE(roleType) \
+  case AccessibilityRole::roleType: \
+  {                                 \
+    return Role::roleType;          \
+  }
+
+Dali::Accessibility::Role ConvertV2RoleToAtspiRole(AccessibilityRole role)
+{
+  using Dali::Accessibility::Role;
+  switch(role)
+  {
+    TO_V1_ROLE_TYPE(ADJUSTABLE, SLIDER)
+    TO_SAME_ROLE_TYPE(ALERT)
+    TO_V1_ROLE_TYPE(BUTTON, PUSH_BUTTON)
+    TO_SAME_ROLE_TYPE(CHECK_BOX)
+    TO_SAME_ROLE_TYPE(COMBO_BOX)
+    TO_V1_ROLE_TYPE(CONTAINER, FILLER)
+    TO_SAME_ROLE_TYPE(DIALOG)
+    TO_SAME_ROLE_TYPE(ENTRY)
+    TO_SAME_ROLE_TYPE(HEADER)
+    TO_SAME_ROLE_TYPE(IMAGE)
+    TO_SAME_ROLE_TYPE(LINK)
+    TO_SAME_ROLE_TYPE(LIST)
+    TO_SAME_ROLE_TYPE(LIST_ITEM)
+    TO_SAME_ROLE_TYPE(MENU)
+    TO_SAME_ROLE_TYPE(MENU_BAR)
+    TO_SAME_ROLE_TYPE(MENU_ITEM)
+    TO_V1_ROLE_TYPE(NONE, UNKNOWN)
+    TO_SAME_ROLE_TYPE(PASSWORD_TEXT)
+    TO_SAME_ROLE_TYPE(POPUP_MENU)
+    TO_SAME_ROLE_TYPE(PROGRESS_BAR)
+    TO_SAME_ROLE_TYPE(RADIO_BUTTON)
+    TO_SAME_ROLE_TYPE(SCROLL_BAR)
+    TO_SAME_ROLE_TYPE(SPIN_BUTTON)
+    TO_V1_ROLE_TYPE(TAB, PAGE_TAB)
+    TO_V1_ROLE_TYPE(TAB_LIST, PAGE_TAB_LIST)
+    TO_SAME_ROLE_TYPE(TEXT)
+    TO_SAME_ROLE_TYPE(TOGGLE_BUTTON)
+    TO_SAME_ROLE_TYPE(TOOL_BAR)
+    default:
+    {
+      return Role::UNKNOWN;
+    }
+  }
+}
+
+Dali::Accessibility::Role ConvertRawRoleToAtspiRole(int32_t rawRole)
+{
+  if(IsAtspiRole(rawRole))
+  {
+    return static_cast<Dali::Accessibility::Role>(rawRole);
+  }
+  else if(IsRoleV2(rawRole))
+  {
+    return ConvertV2RoleToAtspiRole(static_cast<AccessibilityRole>(rawRole));
+  }
+  else
+  {
+    return Dali::Accessibility::Role::UNKNOWN;
+  }
+}
+
+bool IsModalRole(int32_t rawRole)
+{
+  return IsRoleV2(rawRole) && (static_cast<AccessibilityRole>(rawRole) == AccessibilityRole::ALERT ||
+                               static_cast<AccessibilityRole>(rawRole) == AccessibilityRole::DIALOG ||
+                               static_cast<AccessibilityRole>(rawRole) == AccessibilityRole::POPUP_MENU);
+}
+
+bool IsHighlightableRole(int32_t rawRole)
+{
+  return IsRoleV2(rawRole) && static_cast<AccessibilityRole>(rawRole) != AccessibilityRole::NONE;
+}
+
 } // unnamed namespace
 
 ControlAccessible::ControlAccessible(Dali::Actor self)
@@ -127,9 +220,9 @@ std::string ControlAccessible::GetName() const
   {
     controlImpl.mAccessibilityGetNameSignal.Emit(name);
   }
-  else if(!controlImpl.mAccessibilityName.empty())
+  else if(!controlImpl.mAccessibilityProps.name.empty())
   {
-    name = controlImpl.mAccessibilityName;
+    name = controlImpl.mAccessibilityProps.name;
   }
   else if(auto raw = GetNameRaw(); !raw.empty())
   {
@@ -140,11 +233,6 @@ std::string ControlAccessible::GetName() const
     name = Self().GetProperty<std::string>(Actor::Property::NAME);
   }
 
-  if(!controlImpl.mAccessibilityTranslationDomain.empty())
-  {
-    return GetLocaleText(name, controlImpl.mAccessibilityTranslationDomain.c_str());
-  }
-
   return GetLocaleText(name);
 }
 
@@ -165,20 +253,15 @@ std::string ControlAccessible::GetDescription() const
   {
     controlImpl.mAccessibilityGetDescriptionSignal.Emit(description);
   }
-  else if(!controlImpl.mAccessibilityDescription.empty())
+  else if(!controlImpl.mAccessibilityProps.description.empty())
   {
-    description = controlImpl.mAccessibilityDescription;
+    description = controlImpl.mAccessibilityProps.description;
   }
   else
   {
     description = GetDescriptionRaw();
   }
 
-  if(!controlImpl.mAccessibilityTranslationDomain.empty())
-  {
-    return GetLocaleText(description, controlImpl.mAccessibilityTranslationDomain.c_str());
-  }
-
   return GetLocaleText(description);
 }
 
@@ -187,9 +270,15 @@ std::string ControlAccessible::GetDescriptionRaw() const
   return {};
 }
 
+std::string ControlAccessible::GetValue() const
+{
+  return Self().GetProperty<std::string>(Toolkit::DevelControl::Property::ACCESSIBILITY_VALUE);
+}
+
 Dali::Accessibility::Role ControlAccessible::GetRole() const
 {
-  return Self().GetProperty<Dali::Accessibility::Role>(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE);
+  int32_t rawRole = Self().GetProperty<int32_t>(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE);
+  return ConvertRawRoleToAtspiRole(rawRole);
 }
 
 std::string ControlAccessible::GetLocalizedRoleName() const
@@ -225,6 +314,27 @@ bool ControlAccessible::IsShowing()
   return true;
 }
 
+void ControlAccessible::ApplyAccessibilityProps(Dali::Accessibility::States& states)
+{
+  using Dali::Accessibility::State;
+  auto control = Dali::Toolkit::Control::DownCast(Self());
+
+  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
+  Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
+
+  // Apply states
+  const auto& props       = controlImpl.mAccessibilityProps;
+  states[State::ENABLED]  = props.states[AccessibilityState::ENABLED];
+  states[State::SELECTED] = props.states[AccessibilityState::SELECTED];
+  states[State::CHECKED]  = props.states[AccessibilityState::CHECKED];
+  states[State::BUSY]     = props.states[AccessibilityState::BUSY];
+  states[State::EXPANDED] = props.states[AccessibilityState::EXPANDED];
+
+  // Apply traits
+  states[State::MODAL]         = props.isModal || IsModalRole(props.role);
+  states[State::HIGHLIGHTABLE] = props.isHighlightable || IsHighlightableRole(props.role);
+}
+
 Dali::Accessibility::States ControlAccessible::CalculateStates()
 {
   using Dali::Accessibility::State;
@@ -232,15 +342,15 @@ Dali::Accessibility::States ControlAccessible::CalculateStates()
   Dali::Actor                 self = Self();
   Dali::Accessibility::States states;
 
-  states[State::FOCUSABLE]     = self.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE);
-  states[State::FOCUSED]       = Toolkit::KeyboardFocusManager::Get().GetCurrentFocusActor() == self;
-  states[State::HIGHLIGHTABLE] = self.GetProperty<bool>(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE);
-  states[State::HIGHLIGHTED]   = IsHighlighted();
-  states[State::ENABLED]       = true;
-  states[State::SENSITIVE]     = (Dali::DevelActor::IsHittable(self) && Dali::DevelActor::GetTouchRequired(self));
-  states[State::VISIBLE]       = self.GetProperty<bool>(Actor::Property::VISIBLE);
-  states[State::SHOWING]       = IsShowing();
-  states[State::DEFUNCT]       = !self.GetProperty(Dali::DevelActor::Property::CONNECTED_TO_SCENE).Get<bool>();
+  states[State::FOCUSABLE]   = self.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE);
+  states[State::FOCUSED]     = Toolkit::KeyboardFocusManager::Get().GetCurrentFocusActor() == self;
+  states[State::HIGHLIGHTED] = IsHighlighted();
+  states[State::SENSITIVE]   = (Dali::DevelActor::IsHittable(self) && Dali::DevelActor::GetTouchRequired(self));
+  states[State::VISIBLE]     = self.GetProperty<bool>(Actor::Property::VISIBLE);
+  states[State::SHOWING]     = IsShowing();
+  states[State::DEFUNCT]     = !self.GetProperty(Dali::DevelActor::Property::CONNECTED_TO_SCENE).Get<bool>();
+
+  ApplyAccessibilityProps(states);
 
   return states;
 }
@@ -313,7 +423,7 @@ bool ControlAccessible::IsHidden() const
   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
 
-  return controlImpl.mAccessibilityHidden;
+  return controlImpl.mAccessibilityProps.isHidden;
 }
 
 bool ControlAccessible::GrabFocus()
@@ -359,6 +469,8 @@ void ControlAccessible::RegisterPropertySetSignal()
   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
   controlImpl.RegisterAccessibilityPropertySetSignal();
+
+  mStatesSnapshot = controlImpl.mAccessibilityProps.states;
 }
 
 void ControlAccessible::UnregisterPropertySetSignal()
@@ -367,6 +479,8 @@ void ControlAccessible::UnregisterPropertySetSignal()
   Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
   Internal::Control::Impl& controlImpl     = Internal::Control::Impl::Get(internalControl);
   controlImpl.UnregisterAccessibilityPropertySetSignal();
+
+  mStatesSnapshot = {};
 }
 
 bool ControlAccessible::GrabHighlight()
@@ -514,9 +628,23 @@ std::vector<Dali::Accessibility::Relation> ControlAccessible::GetRelationSet()
   return DevelControl::GetAccessibilityRelations(control);
 }
 
+bool ControlAccessible::IsScrollable() const
+{
+  return Self().GetProperty<bool>(Toolkit::DevelControl::Property::ACCESSIBILITY_SCROLLABLE);
+}
+
 bool ControlAccessible::ScrollToChild(Actor child)
 {
-  return false;
+  auto control = Dali::Toolkit::Control::DownCast(Self());
+  bool success = false;
+
+  if(!DevelControl::AccessibilityActionSignal(control).Empty())
+  {
+    success = DevelControl::AccessibilityActionSignal(control).Emit({Accessibility::ActionType::SCROLL_TO_CHILD, child});
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Performed AccessibilityAction: scrollToChild, success : %d\n", success);
+  }
+
+  return success;
 }
 
 Dali::Property::Index ControlAccessible::GetNamePropertyIndex()
@@ -539,4 +667,43 @@ Vector2 ControlAccessible::GetLastPosition() const
   return mLastPosition;
 }
 
+void ControlAccessible::OnStatePropertySet(AccessibilityStates newStates)
+{
+  int32_t rawRole = Self().GetProperty<int32_t>(Property::ACCESSIBILITY_ROLE);
+  if(IsRoleV2(rawRole))
+  {
+    AccessibilityRole role = static_cast<AccessibilityRole>(rawRole);
+
+    if(newStates[AccessibilityState::CHECKED] != mStatesSnapshot[AccessibilityState::CHECKED] &&
+       (role == AccessibilityRole::CHECK_BOX || role == AccessibilityRole::RADIO_BUTTON || role == AccessibilityRole::TOGGLE_BUTTON))
+    {
+      EmitStateChanged(Accessibility::State::CHECKED, newStates[AccessibilityState::CHECKED]);
+    }
+
+    if(newStates[AccessibilityState::SELECTED] != mStatesSnapshot[AccessibilityState::SELECTED] &&
+       (role == AccessibilityRole::BUTTON || role == AccessibilityRole::LIST_ITEM || role == AccessibilityRole::MENU_ITEM))
+    {
+      EmitStateChanged(Accessibility::State::SELECTED, newStates[AccessibilityState::SELECTED]);
+    }
+  }
+  else
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "With V1 role, state change events are emitted manually by the app component.");
+  }
+
+  mStatesSnapshot = newStates;
+}
+
+bool ControlAccessible::IsModal(Actor actor)
+{
+  bool isModalPropertySet = actor.GetProperty<bool>(Property::ACCESSIBILITY_IS_MODAL);
+  if(isModalPropertySet)
+  {
+    return true;
+  }
+
+  int32_t rawRole = actor.GetProperty<int32_t>(Property::ACCESSIBILITY_ROLE);
+  return IsModalRole(rawRole);
+}
+
 } // namespace Dali::Toolkit::DevelControl
index 4b3d3d6..2c43259 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_CONTROL_ACCESSIBLE_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 namespace Dali::Toolkit::DevelControl
 {
 /**
+ * @brief Represents current state of a control.
+ */
+enum class AccessibilityState : uint32_t
+{
+  ENABLED = 0,
+  SELECTED,
+  CHECKED,
+  BUSY,
+  EXPANDED,
+  MAX_COUNT
+};
+using AccessibilityStates = Accessibility::EnumBitSet<AccessibilityState, AccessibilityState::MAX_COUNT>;
+
+constexpr const uint32_t ROLE_START_INDEX = 200;
+/**
+ * @brief AccessibilityRole represents the purpose of a control.
+ */
+enum class AccessibilityRole : uint32_t
+{
+  ADJUSTABLE = ROLE_START_INDEX,
+  ALERT,
+  BUTTON,
+  CHECK_BOX,
+  COMBO_BOX,
+  CONTAINER,
+  DIALOG,
+  ENTRY,
+  HEADER,
+  IMAGE,
+  LINK,
+  LIST,
+  LIST_ITEM,
+  MENU,
+  MENU_BAR,
+  MENU_ITEM,
+  NONE,
+  PASSWORD_TEXT,
+  POPUP_MENU,
+  PROGRESS_BAR,
+  RADIO_BUTTON,
+  SCROLL_BAR,
+  SPIN_BUTTON,
+  TAB,
+  TAB_LIST,
+  TEXT,
+  TOGGLE_BUTTON,
+  TOOL_BAR,
+  MAX_COUNT
+};
+
+/**
  * @brief Represents the Accessible object for Dali::Toolkit::Control and derived classes
  *
  * You can create a derived class (and override Control::CreateAccessibleObject)
@@ -105,6 +156,11 @@ public:
   virtual std::string GetDescriptionRaw() const;
 
   /**
+   * @copydoc Dali::Accessibility::Accessible::GetValue()
+   */
+  std::string GetValue() const override;
+
+  /**
    * @copydoc Dali::Accessibility::Accessible::GetRole()
    */
   Dali::Accessibility::Role GetRole() const override;
@@ -190,6 +246,11 @@ public:
   std::vector<Dali::Accessibility::Relation> GetRelationSet() override;
 
   /**
+   * @copydoc Dali::Accessibility::Component::IsScrollable()
+   */
+  bool IsScrollable() const override;
+
+  /**
    * @copydoc Dali::Accessibility::Accessible::GetStates()
    */
   virtual Dali::Accessibility::States CalculateStates();
@@ -221,6 +282,27 @@ public:
    * @return The Last object position
    */
   Vector2 GetLastPosition() const;
+
+  /**
+   * @brief Handles AcessibilityState property change; Only called when the control is highlighted.
+   */
+  void OnStatePropertySet(AccessibilityStates newStates);
+
+  /**
+   * @brief Returns true if given actor is considered as modal by propeties set.
+   */
+  static bool IsModal(Actor actor);
+
+private:
+  /**
+   * @brief Appliys relavant accessibility properties to AT-SPI states.
+   */
+  void ApplyAccessibilityProps(Dali::Accessibility::States& states);
+
+  /**
+   * @brief Grabs snapshot of previous state when the control is highlighted.
+   */
+  AccessibilityStates mStatesSnapshot;
 };
 
 } // namespace Dali::Toolkit::DevelControl
index 1de25f4..72e7ab1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -185,11 +185,16 @@ Toolkit::DevelControl::AccessibilityDoGestureSignalType& AccessibilityDoGestureS
   return GetControlImplementation(control).mAccessibilityDoGestureSignal;
 }
 
+Toolkit::DevelControl::AccessibilityActionSignalType& AccessibilityActionSignal(Toolkit::Control control)
+{
+  return GetControlImplementation(control).mAccessibilityActionSignal;
+}
+
 void AppendAccessibilityRelation(Toolkit::Control control, Dali::Actor destination, Dali::Accessibility::RelationType relation)
 {
   if(auto destinationAccessible = Accessibility::Accessible::Get(destination))
   {
-    GetControlImplementation(control).mAccessibilityRelations[relation].insert(destinationAccessible);
+    GetControlImplementation(control).mAccessibilityProps.relations[relation].insert(destinationAccessible);
   }
 }
 
@@ -197,7 +202,7 @@ void RemoveAccessibilityRelation(Toolkit::Control control, Dali::Actor destinati
 {
   if(auto destinationAccessible = Accessibility::Accessible::Get(destination))
   {
-    auto& relations = GetControlImplementation(control).mAccessibilityRelations;
+    auto& relations = GetControlImplementation(control).mAccessibilityProps.relations;
 
     relations[relation].erase(destinationAccessible);
 
@@ -210,7 +215,7 @@ void RemoveAccessibilityRelation(Toolkit::Control control, Dali::Actor destinati
 
 std::vector<Accessibility::Relation> GetAccessibilityRelations(Toolkit::Control control)
 {
-  const auto&                          relations = GetControlImplementation(control).mAccessibilityRelations;
+  const auto&                          relations = GetControlImplementation(control).mAccessibilityProps.relations;
   std::vector<Accessibility::Relation> result;
 
   for(auto& relation : relations)
@@ -226,7 +231,7 @@ std::vector<Accessibility::Relation> GetAccessibilityRelations(Toolkit::Control
 
 void ClearAccessibilityRelations(Toolkit::Control control)
 {
-  GetControlImplementation(control).mAccessibilityRelations.clear();
+  GetControlImplementation(control).mAccessibilityProps.relations.clear();
 }
 
 void AppendAccessibilityAttribute(Toolkit::Control control, const std::string& key, const std::string& value)
@@ -256,7 +261,7 @@ Dali::Accessibility::ReadingInfoTypes GetAccessibilityReadingInfoType(Toolkit::C
 
 bool ClearAccessibilityHighlight(Toolkit::Control control)
 {
-  auto* controlAccessible = GetControlImplementation(control).GetAccessibleObject();
+  auto controlAccessible = GetControlImplementation(control).GetAccessibleObject();
   if(DALI_LIKELY(controlAccessible))
   {
     return controlAccessible->ClearHighlight();
@@ -266,7 +271,7 @@ bool ClearAccessibilityHighlight(Toolkit::Control control)
 
 bool GrabAccessibilityHighlight(Toolkit::Control control)
 {
-  auto* controlAccessible = GetControlImplementation(control).GetAccessibleObject();
+  auto controlAccessible = GetControlImplementation(control).GetAccessibleObject();
   if(DALI_LIKELY(controlAccessible))
   {
     return controlAccessible->GrabHighlight();
@@ -276,7 +281,7 @@ bool GrabAccessibilityHighlight(Toolkit::Control control)
 
 Dali::Accessibility::States GetAccessibilityStates(Toolkit::Control control)
 {
-  auto* controlAccessible = GetControlImplementation(control).GetAccessibleObject();
+  auto controlAccessible = GetControlImplementation(control).GetAccessibleObject();
   if(DALI_LIKELY(controlAccessible))
   {
     return controlAccessible->GetStates();
@@ -286,7 +291,7 @@ Dali::Accessibility::States GetAccessibilityStates(Toolkit::Control control)
 
 void NotifyAccessibilityStateChange(Toolkit::Control control, Dali::Accessibility::States states, bool recurse)
 {
-  auto* controlAccessible = GetControlImplementation(control).GetAccessibleObject();
+  auto controlAccessible = GetControlImplementation(control).GetAccessibleObject();
   if(DALI_LIKELY(controlAccessible))
   {
     controlAccessible->NotifyAccessibilityStateChange(std::move(states), recurse);
@@ -308,6 +313,35 @@ bool IsCreateAccessibleEnabled(Toolkit::Control control)
   return GetControlImplementation(control).IsCreateAccessibleEnabled();
 }
 
+void EmitAccessibilityStateChanged(Dali::Actor actor, Accessibility::State state, int newValue)
+{
+  auto accessible = Accessibility::Accessible::GetOwningPtr(actor);
+  if(DALI_LIKELY(accessible))
+  {
+    auto control = Toolkit::Control::DownCast(actor);
+    if(DALI_LIKELY(control))
+    {
+      if(state == Accessibility::State::SHOWING)
+      {
+        bool isModal = ControlAccessible::IsModal(control);
+        if(isModal)
+        {
+          if(newValue == 1)
+          {
+            Accessibility::Bridge::GetCurrentBridge()->RegisterDefaultLabel(accessible);
+          }
+          else
+          {
+            Accessibility::Bridge::GetCurrentBridge()->UnregisterDefaultLabel(accessible);
+          }
+        }
+      }
+    }
+
+    accessible->EmitStateChanged(state, newValue, 0);
+  }
+}
+
 } // namespace DevelControl
 
 } // namespace Toolkit
index 465041a..3fe85d9 100644 (file)
@@ -68,6 +68,9 @@ typedef Signal<void(std::string&)> AccessibilityGetDescriptionSignalType;
 /// @brief AccessibilityDoGesture signal type.
 typedef Signal<void(std::pair<Dali::Accessibility::GestureInfo, bool>&)> AccessibilityDoGestureSignalType;
 
+/// @brief AccessibilityAction signal type.
+typedef Signal<bool(const Dali::Accessibility::ActionInfo&)> AccessibilityActionSignalType;
+
 enum State
 {
   NORMAL,
@@ -161,7 +164,7 @@ enum
   ACCESSIBILITY_DESCRIPTION,
 
   /**
-   * @brief Current translation domain for accessibility clients.
+   * @brief Deprecated. Current translation domain for accessibility clients.
    * @details Name "accessibilityTranslationDomain", type Property::STRING.
    */
   ACCESSIBILITY_TRANSLATION_DOMAIN,
@@ -169,9 +172,8 @@ enum
   /**
    * @brief Role being performed in accessibility hierarchy.
    * @details Name "accessibilityRole", type Property::INTEGER.
-   * @note Property is stored as INTEGER, however its interpretaton
-   * depend on enumeration Dali::Accessibility::Role and should be read and written
-   * with usage of enumerated values.
+   * @note It gets integer value of AccessibilityRole enum then interprets to Dali::Accessibility::Role when requested by AT-SPI.
+   *  Note that setting Dali::Accessibility::Role value is still accepted for backward compatibility but not preferred.
    * @see Dali::Accessibility::Role
    */
   ACCESSIBILITY_ROLE,
@@ -223,6 +225,30 @@ enum
    * It will also appear in the AT-SPI tree under the key "automationId".
    */
   AUTOMATION_ID,
+
+  /**
+   * @brief The accessibility value represented by the control. For example, "60%" for a slider object.
+   * @details Name "accessibilityValue", type Property::STRING.
+   */
+  ACCESSIBILITY_VALUE,
+
+  /**
+   * @brief Indicates the accessibility services treat the control as scrollable.
+   * @details Name "accessibilityScrollable", type Property::BOOLEAN.
+   */
+  ACCESSIBILITY_SCROLLABLE,
+
+  /**
+   * @brief Bitset integer of AccessibilityState which describes the current state of a control.
+   * @details Name "accessibilityStates", type Property::INTEGER.
+   */
+  ACCESSIBILITY_STATES,
+
+  /**
+   * @brief Indicates the accessibility services treat the controla as modal.
+   * @details Name "accessibilityIsModal", type Property::BOOLEAN.
+   */
+  ACCESSIBILITY_IS_MODAL,
 };
 
 } // namespace Property
@@ -492,6 +518,12 @@ DALI_TOOLKIT_API AccessibilityGetDescriptionSignalType& AccessibilityGetDescript
 DALI_TOOLKIT_API AccessibilityDoGestureSignalType& AccessibilityDoGestureSignal(Toolkit::Control control);
 
 /**
+ * @brief The signal is emitted when accessibility client call "DoAction" or "DoActionName" method via IPC mechanism.
+ * @return The signal to connect to
+ */
+DALI_TOOLKIT_API AccessibilityActionSignalType& AccessibilityActionSignal(Toolkit::Control control);
+
+/**
  * @brief The method allows connection with other actor with usage of concrete accessibility relation type.
  *
  * @param control object to append attribute to
@@ -624,6 +656,15 @@ DALI_TOOLKIT_API void EnableCreateAccessible(Toolkit::Control control, bool enab
  */
 DALI_TOOLKIT_API bool IsCreateAccessibleEnabled(Toolkit::Control control);
 
+/**
+ * @brief The method to emit accessibility state-changed event to accessibility clients
+ *
+ * @param actor The actor that has accessibility state change.
+ * @param state  The accessibility state.
+ * @param newValue new value to set.
+ */
+DALI_TOOLKIT_API void EmitAccessibilityStateChanged(Dali::Actor actor, Dali::Accessibility::State state, int newValue);
+
 } // namespace DevelControl
 
 } // namespace Toolkit
index 48a4567..12c16af 100644 (file)
@@ -35,6 +35,21 @@ TextFitChangedSignalType& TextFitChangedSignal(TextLabel textLabel)
   return GetImpl(textLabel).TextFitChangedSignal();
 }
 
+AsyncTextRenderedSignalType& AsyncTextRenderedSignal(TextLabel textLabel)
+{
+  return GetImpl(textLabel).AsyncTextRenderedSignal();
+}
+
+AsyncNaturalSizeComputedSignalType& AsyncNaturalSizeComputedSignal(TextLabel textLabel)
+{
+  return GetImpl(textLabel).AsyncNaturalSizeComputedSignal();
+}
+
+AsyncHeightForWidthComputedSignalType& AsyncHeightForWidthComputedSignal(TextLabel textLabel)
+{
+  return GetImpl(textLabel).AsyncHeightForWidthComputedSignal();
+}
+
 Vector<Vector2> GetTextSize(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex)
 {
   return GetImpl(textLabel).GetTextSize(startIndex, endIndex);
@@ -85,6 +100,31 @@ bool IsRemoveBackInset(TextLabel textLabel)
   return GetImpl(textLabel).IsRemoveBackInset();
 }
 
+void RequestAsyncRenderWithFixedSize(TextLabel textLabel, float width, float height)
+{
+  GetImpl(textLabel).RequestAsyncRenderWithFixedSize(width, height);
+}
+
+void RequestAsyncRenderWithFixedWidth(TextLabel textLabel, float width, float heightConstraint)
+{
+  GetImpl(textLabel).RequestAsyncRenderWithFixedWidth(width, heightConstraint);
+}
+
+void RequestAsyncRenderWithConstraint(TextLabel textLabel, float widthConstraint, float heightConstraint)
+{
+  GetImpl(textLabel).RequestAsyncRenderWithConstraint(widthConstraint, heightConstraint);
+}
+
+void RequestAsyncNaturalSize(TextLabel textLabel)
+{
+  GetImpl(textLabel).RequestAsyncNaturalSize();
+}
+
+void RequestAsyncHeightForWidth(TextLabel textLabel, float width)
+{
+  GetImpl(textLabel).RequestAsyncHeightForWidth(width);
+}
+
 } // namespace DevelTextLabel
 
 } // namespace Toolkit
index 4c00662..50acf5e 100644 (file)
@@ -30,6 +30,20 @@ namespace Toolkit
 {
 namespace DevelTextLabel
 {
+namespace Render
+{
+/**
+ * @brief Enumerations specifying the render mode of text.
+ */
+enum Mode
+{
+  SYNC = 0,     ///< default, synchronous text loading.
+  ASYNC_AUTO,   ///< automatically requests an asynchronous text load in OnRelayout.
+  ASYNC_MANUAL  ///< users should manually request rendering using the async text method.
+};
+
+} // namespace Render
+
 namespace Property
 {
 enum Type
@@ -236,6 +250,31 @@ enum Type
    * @details Name "cutout", type Property::BOOLEAN.
    */
   CUTOUT,
+
+  /**
+   * @brief The enumerations used to specify whether to render mode of the text.
+   * @details Name "renderMode", type Property::INTEGER.
+   * @note
+   * Render::Mode
+   * SYNC         : default, synchronous text loading.
+   * ASYNC_AUTO   : automatically requests an asynchronous text load in OnRelayout.
+   * ASYNC_MANUAL : users should manually request rendering using the async text method.
+   */
+  RENDER_MODE,
+
+  /**
+   * @brief Whether the last rendering result is a manual render.
+   * @details Name "manualRender", type Property::BOOLEAN.
+   * @note If it's false, the render result was automatically requested by OnRelayout.
+   */
+  MANUAL_RENDERED,
+
+  /**
+   * @brief Number of lines after latest asynchronous computing or rendering of text.
+   * @details Name "asyncLineCount", type Property::INTERGER.
+   * @note This property is read-only.
+   */
+  ASYNC_LINE_COUNT,
 };
 
 } // namespace Property
@@ -358,6 +397,57 @@ DALI_TOOLKIT_API void SetRemoveBackInset(TextLabel textLabel, const bool remove)
 DALI_TOOLKIT_API bool IsRemoveBackInset(TextLabel textLabel);
 
 /**
+ * @brief A method that requests asynchronous rendering of text with a fixed size.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] width The width of text to render.
+ * @param[in] height The height of text to render.
+ */
+DALI_TOOLKIT_API void RequestAsyncRenderWithFixedSize(TextLabel textLabel, float width, float height);
+
+/**
+ * @brief Requests asynchronous text rendering with a fixed width.
+ * The height is determined by the content of the text when rendered with the given width.
+ * The result will be the same as the height returned by GetHeightForWidth.
+ * If the heightConstraint is given, the maximum height will be the heightConstraint.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] width The width of text to render.
+ * @param[in] heightConstraint The maximum available height of text to render.
+ */
+DALI_TOOLKIT_API void RequestAsyncRenderWithFixedWidth(TextLabel textLabel, float width, float heightConstraint);
+
+/**
+ * @brief Requests asynchronous rendering with the maximum available width using the given widthConstraint.
+ *
+ * If the width of the text content is smaller than the widthConstraint, the width will be determined by the width of the text.
+ * If the width of the text content is larger than the widthConstraint, the width will be determined by the widthConstraint.
+ * The height is determined by the content of the text when rendered with the given width.
+ * In this case, the result will be the same as the height returned by GetHeightForWidth.
+ * If the heightConstraint is given, the maximum height will be the heightConstraint.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] widthConstraint The maximum available width of text to render.
+ * @param[in] heightConstraint The maximum available height of text to render.
+ */
+DALI_TOOLKIT_API void RequestAsyncRenderWithConstraint(TextLabel textLabel, float widthConstraint, float heightConstraint);
+
+/**
+ * @brief Requests asynchronous text natural size computation.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ */
+DALI_TOOLKIT_API void RequestAsyncNaturalSize(TextLabel textLabel);
+
+/**
+ * @brief Requests asynchronous computation of the height of the text based on the given width.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] width The width of text to compute.
+ */
+DALI_TOOLKIT_API void RequestAsyncHeightForWidth(TextLabel textLabel, float width);
+
+/**
  * @brief Anchor clicked signal type.
  *
  * @note Signal
@@ -372,6 +462,33 @@ using AnchorClickedSignalType = Signal<void(TextLabel, const char*, uint32_t)>;
 using TextFitChangedSignalType = Signal<void(TextLabel)>;
 
 /**
+ * @brief Async text rendered signal type.
+ *
+ * @note Signal
+ *  - float : rendered width.
+ *  - float : rendered height.
+ */
+using AsyncTextRenderedSignalType = Signal<void(TextLabel, float, float)>;
+
+/**
+ * @brief Async natural size computed signal type.
+ *
+ * @note Signal
+ *  - float : computed width.
+ *  - float : computed height.
+ */
+using AsyncNaturalSizeComputedSignalType = Signal<void(TextLabel, float, float)>;
+
+/**
+ * @brief Async height for width computed signal type.
+ *
+ * @note Signal
+ *  - float : computed width.
+ *  - float : computed height.
+ */
+using AsyncHeightForWidthComputedSignalType = Signal<void(TextLabel, float, float)>;
+
+/**
  * @brief This signal is emitted when the anchor is clicked.
  *
  * A callback of the following type may be connected:
@@ -395,6 +512,42 @@ DALI_TOOLKIT_API AnchorClickedSignalType& AnchorClickedSignal(TextLabel textLabe
  */
 DALI_TOOLKIT_API TextFitChangedSignalType& TextFitChangedSignal(TextLabel textLabel);
 
+/**
+ * @brief This signal is emitted when the async text rendered.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName(TextLabel textLabel);
+ * @endcode
+ * @param[in] textLabel The instance of TextLabel.
+ * @return The signal to connect to.
+ */
+DALI_TOOLKIT_API AsyncTextRenderedSignalType& AsyncTextRenderedSignal(TextLabel textLabel);
+
+/**
+ * @brief This signal is emitted when the async natural size computed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName(TextLabel textLabel);
+ * @endcode
+ * @param[in] textLabel The instance of TextLabel.
+ * @return The signal to connect to.
+ */
+DALI_TOOLKIT_API AsyncNaturalSizeComputedSignalType& AsyncNaturalSizeComputedSignal(TextLabel textLabel);
+
+/**
+ * @brief This signal is emitted when the async height for width computed.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void YourCallbackName(TextLabel textLabel);
+ * @endcode
+ * @param[in] textLabel The instance of TextLabel.
+ * @return The signal to connect to.
+ */
+DALI_TOOLKIT_API AsyncHeightForWidthComputedSignalType& AsyncHeightForWidthComputedSignal(TextLabel textLabel);
+
 } // namespace DevelTextLabel
 
 } // namespace Toolkit
index 63d9cca..7900fc1 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -84,6 +84,11 @@ WebView WebView::DownCast(BaseHandle handle)
   return Control::DownCast<WebView, Internal::WebView>(handle);
 }
 
+void WebView::ChangeOrientation(int orientation)
+{
+  return Dali::Toolkit::GetImpl(*this).ChangeOrientation(orientation);
+}
+
 Dali::Toolkit::WebSettings* WebView::GetSettings() const
 {
   return Dali::Toolkit::GetImpl(*this).GetSettings();
@@ -224,6 +229,11 @@ void WebView::AddJavaScriptMessageHandler(const std::string& exposedObjectName,
   Dali::Toolkit::GetImpl(*this).AddJavaScriptMessageHandler(exposedObjectName, handler);
 }
 
+void WebView::AddJavaScriptEntireMessageHandler(const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptEntireMessageHandlerCallback handler)
+{
+  Dali::Toolkit::GetImpl(*this).AddJavaScriptEntireMessageHandler(exposedObjectName, handler);
+}
+
 void WebView::RegisterJavaScriptAlertCallback(Dali::WebEnginePlugin::JavaScriptAlertCallback callback)
 {
   Dali::Toolkit::GetImpl(*this).RegisterJavaScriptAlertCallback(callback);
@@ -264,6 +274,11 @@ bool WebView::CreateHitTestAsynchronously(int32_t x, int32_t y, Dali::WebEngineH
   return Dali::Toolkit::GetImpl(*this).CreateHitTestAsynchronously(x, y, mode, callback);
 }
 
+void WebView::ExitFullscreen()
+{
+  Dali::Toolkit::GetImpl(*this).ExitFullscreen();
+}
+
 void WebView::ClearHistory()
 {
   Dali::Toolkit::GetImpl(*this).ClearHistory();
@@ -379,6 +394,11 @@ void WebView::RegisterNavigationPolicyDecidedCallback(Dali::WebEnginePlugin::Web
   Dali::Toolkit::GetImpl(*this).RegisterNavigationPolicyDecidedCallback(callback);
 }
 
+void WebView::RegisterNewWindowPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback callback)
+{
+  Dali::Toolkit::GetImpl(*this).RegisterNewWindowPolicyDecidedCallback(callback);
+}
+
 void WebView::RegisterNewWindowCreatedCallback(Dali::WebEnginePlugin::WebEngineNewWindowCreatedCallback callback)
 {
   Dali::Toolkit::GetImpl(*this).RegisterNewWindowCreatedCallback(callback);
@@ -409,6 +429,21 @@ void WebView::RegisterContextMenuHiddenCallback(Dali::WebEnginePlugin::WebEngine
   Dali::Toolkit::GetImpl(*this).RegisterContextMenuHiddenCallback(callback);
 }
 
+void WebView::RegisterFullscreenEnteredCallback(Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback callback)
+{
+  Dali::Toolkit::GetImpl(*this).RegisterFullscreenEnteredCallback(callback);
+}
+
+void WebView::RegisterFullscreenExitedCallback(Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback callback)
+{
+  Dali::Toolkit::GetImpl(*this).RegisterFullscreenExitedCallback(callback);
+}
+
+void WebView::RegisterTextFoundCallback(Dali::WebEnginePlugin::WebEngineTextFoundCallback callback)
+{
+  Dali::Toolkit::GetImpl(*this).RegisterTextFoundCallback(callback);
+}
+
 void WebView::GetPlainTextAsynchronously(Dali::WebEnginePlugin::PlainTextReceivedCallback callback)
 {
   Dali::Toolkit::GetImpl(*this).GetPlainTextAsynchronously(callback);
index 0ee8af7..6b5c5dd 100755 (executable)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_WEB_VIEW_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -270,6 +270,11 @@ public:
   static WebView DownCast(BaseHandle handle);
 
   /**
+   * @brief Change orientation.
+   */
+  void ChangeOrientation(int orientation);
+
+  /**
    * @brief Get WebSettings of WebEngine.
    */
   Dali::Toolkit::WebSettings* GetSettings() const;
@@ -482,6 +487,14 @@ public:
   void AddJavaScriptMessageHandler(const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptMessageHandlerCallback handler);
 
   /**
+   * @brief Inject a JavaScript object with a message handler into the WebView.
+   *
+   * @param[in] exposedObjectName The name of exposed object
+   * @param[in] handler The callback function
+   */
+  void AddJavaScriptEntireMessageHandler(const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptEntireMessageHandlerCallback handler);
+
+  /**
    * @brief Register alert callback for javascript.
    *
    * @param[in] callback The callback function to be called by the JavaScript runtime.
@@ -543,6 +556,11 @@ public:
   bool CreateHitTestAsynchronously(int32_t x, int32_t y, Dali::WebEngineHitTest::HitTestMode mode, Dali::WebEnginePlugin::WebEngineHitTestCreatedCallback callback);
 
   /**
+   * @brief Exit fullscreen.
+   */
+  void ExitFullscreen();
+
+  /**
    * @brief Clear the history of Web.
    */
   void ClearHistory();
@@ -710,6 +728,13 @@ public:
   void RegisterNavigationPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNavigationPolicyDecidedCallback callback);
 
   /**
+   * @brief Callback to be called when new window policy would be decided.
+   *
+   * @param[in] callback
+   */
+  void RegisterNewWindowPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback callback);
+
+  /**
    * @brief Callback to be called when a new window would be created.
    *
    * @param[in] callback
@@ -752,6 +777,27 @@ public:
   void RegisterContextMenuHiddenCallback(Dali::WebEnginePlugin::WebEngineContextMenuHiddenCallback callback);
 
   /**
+   * @brief Callback to be called when fullscreen would be entered.
+   *
+   * @param[in] callback
+   */
+  void RegisterFullscreenEnteredCallback(Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback callback);
+
+  /**
+   * @brief Callback to be called when fullscreen would be exited.
+   *
+   * @param[in] callback
+   */
+  void RegisterFullscreenExitedCallback(Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback callback);
+
+  /**
+   * @brief Callback to be called when text would be found.
+   *
+   * @param[in] callback
+   */
+  void RegisterTextFoundCallback(Dali::WebEnginePlugin::WebEngineTextFoundCallback callback);
+
+  /**
    * @brief Get a plain text of current web page asynchronously.
    *
    * @param[in] callback The callback function called asynchronously.
index 43d805b..e260f49 100755 (executable)
@@ -84,6 +84,7 @@ SET( devel_api_src_files
   ${devel_api_src_dir}/visual-factory/transition-data.cpp
   ${devel_api_src_dir}/visual-factory/visual-factory.cpp
   ${devel_api_src_dir}/visual-factory/visual-base.cpp
+  ${devel_api_src_dir}/visual-factory/precompile-shader-option.cpp
   ${devel_api_src_dir}/controls/gaussian-blur-view/gaussian-blur-view.cpp
   ${devel_api_src_dir}/drag-drop-detector/drag-and-drop-detector.cpp
   ${devel_api_src_dir}/text/text-geometry-devel.cpp
@@ -100,6 +101,11 @@ SET( devel_api_accessibility-manager_header_files
   ${devel_api_src_dir}/accessibility-manager/accessibility-manager.h
 )
 
+SET( devel_api_asset-manager_header_files
+  ${devel_api_src_dir}/asset-manager/asset-manager.h
+)
+
+
 SET( devel_api_controls_header_files
   ${devel_api_src_dir}/controls/canvas-view/canvas-view.h
   ${devel_api_src_dir}/controls/control-accessible.h
@@ -178,6 +184,7 @@ SET( devel_api_visual_factory_header_files
   ${devel_api_src_dir}/visual-factory/transition-data.h
   ${devel_api_src_dir}/visual-factory/visual-factory.h
   ${devel_api_src_dir}/visual-factory/visual-base.h
+  ${devel_api_src_dir}/visual-factory/precompile-shader-option.h
 )
 
 SET( devel_api_visuals_header_files
@@ -311,6 +318,7 @@ SET( SOURCES ${SOURCES}
 SET( DEVEL_API_HEADERS ${DEVEL_API_HEADERS}
   ${devel_api_header_files}
   ${devel_api_accessibility-manager_header_files}
+  ${devel_api_asset-manager_header_files}
   ${devel_api_alignment_header_files}
   ${devel_api_controls_header_files}
   ${devel_api_bloom_view_header_files}
index ace107e..5169135 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ TextureSet RemoveTexture(const std::string& textureUrl)
 {
   auto  visualFactory = Toolkit::VisualFactory::Get();
   auto& textureMgr    = GetImplementation(visualFactory).GetTextureManager();
-  return textureMgr.RemoveExternalTexture(textureUrl);
+  return textureMgr.RemoveExternalTextureByUrl(textureUrl);
 }
 
 } // namespace TextureManager
index 98c37ab..483fc3f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -151,6 +151,7 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
   const uint8_t*       utf8                 = NULL; // pointer to the first character of the text (encoded in utf8)
   Length               textSize             = 0u;   // The length of the utf8 string.
 
+  FontClient&        fontClient              = internalDataModel.fontClient;
   Length&            numberOfCharacters      = internalDataModel.numberOfCharacters;
   Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
   Text::ModelPtr&    textModel               = internalDataModel.textModel;
@@ -217,10 +218,8 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
 
   lineBreakInfo.Resize(numberOfCharacters, LINE_NO_BREAK);
 
-  SetLineBreakInfo(utf32Characters,
-                   0u,
-                   numberOfCharacters,
-                   lineBreakInfo);
+  TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
+  SetLineBreakInfo(segmentation, utf32Characters, 0u, numberOfCharacters, lineBreakInfo);
 
   ////////////////////////////////////////////////////////////////////////////////
   // Retrieve the script runs.
@@ -290,7 +289,8 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
 
   // Validates the fonts. If there is a character with no assigned font it sets a default one.
   // After this call, fonts are validated.
-  multilanguageSupport.ValidateFonts(utf32Characters,
+  multilanguageSupport.ValidateFonts(fontClient,
+                                     utf32Characters,
                                      scripts,
                                      fontDescriptionRuns,
                                      defaultFontDescription,
@@ -306,7 +306,10 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
 
   bidirectionalInfo.Reserve(1u);
 
-  SetBidirectionalInfo(utf32Characters,
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
+  SetBidirectionalInfo(bidirectionalSupport,
+                       utf32Characters,
                        scripts,
                        lineBreakInfo,
                        0u,
@@ -317,7 +320,8 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
   if(hasBidirectionalText)
   {
     // Only set the character directions if there is right to left characters.
-    GetCharactersDirection(bidirectionalInfo,
+    GetCharactersDirection(bidirectionalSupport,
+                           bidirectionalInfo,
                            numberOfCharacters,
                            0u,
                            numberOfCharacters,
@@ -326,7 +330,8 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
     // This paragraph has right to left text. Some characters may need to be mirrored.
     // TODO: consider if the mirrored string can be stored as well.
 
-    internalDataModel.isTextMirrored = GetMirroredText(utf32Characters,
+    internalDataModel.isTextMirrored = GetMirroredText(bidirectionalSupport,
+                                                       utf32Characters,
                                                        directions,
                                                        bidirectionalInfo,
                                                        0u,
@@ -359,8 +364,12 @@ void ShapeText(TextAbstraction::TextRenderer::Parameters& rendererParameters, Ve
 
   newParagraphGlyphs.Reserve(1u);
 
+  TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
+
   // Shapes the text.
-  ShapeText(textToShape,
+  ShapeText(shaping,
+            fontClient,
+            textToShape,
             lineBreakInfo,
             scripts,
             validFonts,
@@ -1052,8 +1061,12 @@ Size LayoutText(const RendererParameters& textParameters, TextAbstraction::TextR
 
   textModel->mLineWrapMode          = Text::LineWrap::WORD;
   textModel->mIgnoreSpacesAfterText = false;
+
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
   Text::Layout::Parameters layoutParameters(internalDataModel.textLayoutArea,
-                                            textModel);
+                                            textModel,
+                                            fontClient,
+                                            bidirectionalSupport);
 
   // Whether the last character is a new paragraph character.
   const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : textModel->mLogicalModel->mText;
index fed5316..280eedf 100644 (file)
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2023 Samsung Electronics Co., Ltd.
+* Copyright (c) 2024 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
 #include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/internal/visuals/npatch-data.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-data.h>
 
 namespace Dali
 {
diff --git a/dali-toolkit/devel-api/visual-factory/precompile-shader-option.cpp b/dali-toolkit/devel-api/visual-factory/precompile-shader-option.cpp
new file mode 100644 (file)
index 0000000..39e924a
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/devel-api/visual-factory/precompile-shader-option.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+namespace
+{
+  // TYPE
+  const char* TOKEN_TYPE("shaderType");
+  const char* TOKEN_TYPE_IMAGE("image");
+  const char* TOKEN_TYPE_TEXT("text");
+  const char* TOKEN_TYPE_COLOR("color");
+  const char* TOKEN_TYPE_MODEL_3D("3d");
+  const char* TOKEN_TYPE_NPATCH("npatch");
+  const char* TOKEN_TYPE_CUSTOM("custom");
+
+  // OPTION
+  const char* TOKEN_OPTION("shaderOption");
+  const char* TOKEN_OPTION_ROUNDED_CORNER("ROUNDED_CORNER");
+  const char* TOKEN_OPTION_BORDERLINE("BORDERLINE");
+  const char* TOKEN_OPTION_BLUR_EDGE("BLUR_EDGE");
+  const char* TOKEN_OPTION_CUTOUT("CUTOUT");
+  const char* TOKEN_OPTION_ATLAS_DEFAULT("ATLAS_DEFAULT");
+  const char* TOKEN_OPTION_ATLAS_CUSTOM("ATLAS_CUSTOM");
+  const char* TOKEN_OPTION_MASKING("MASKING");
+  const char* TOKEN_OPTION_YUV_TO_RGB("YUV_TO_RGB");
+  const char* TOKEN_OPTION_YUV_AND_RGB("YUV_AND_RGB");
+  const char* TOKEN_OPTION_MULTI_COLOR("MULTI_COLOR");
+  const char* TOKEN_OPTION_STYLES("STYLES");
+  const char* TOKEN_OPTION_OVERLAY("OVERLAY");
+  const char* TOKEN_OPTION_EMOJI("EMOJI");
+  const char* TOKEN_OPTION_STRETCH_X("xStretchCount");
+  const char* TOKEN_OPTION_STRETCH_Y("yStretchCount");
+
+
+  // CUSTOM
+  const char* TOKEN_CUSTOM_VERTEX("vertexShader");
+  const char* TOKEN_CUSTOM_FRAMENT("fragmentShader");
+  const char* TOKEN_CUSTOM_NAME("shaderName");
+}
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+PrecompileShaderOption::PrecompileShaderOption(const Property::Map& shaderOption)
+: mShaderType(ShaderType::UNKNOWN),
+  mShaderOptions(),
+  mShaderName(""),
+  mVertexShader(""),
+  mFragmentShader(""),
+  mNpatchXStretchCount(0),
+  mNpatchYStretchCount(0)
+{
+  ConvertShaderMap(shaderOption);
+}
+
+void PrecompileShaderOption::ConvertShaderMap(const Property::Map& shaderOption)
+{
+  for(unsigned int shaderIdx = 0; shaderIdx < shaderOption.Count(); ++shaderIdx)
+  {
+    const KeyValuePair pair(shaderOption.GetKeyValue(shaderIdx));
+    if(pair.first.type == Property::Key::INDEX)
+    {
+        continue; // We don't consider index keys.
+    }
+
+    const std::string&     key(pair.first.stringKey);
+    const Property::Value& value(pair.second);
+
+    if(key == TOKEN_TYPE)
+    {
+      if(value.GetType() == Property::STRING)
+      {
+        auto shaderType = value.Get<std::string>();
+        if(shaderType == TOKEN_TYPE_IMAGE)
+        {
+          mShaderType = ShaderType::IMAGE;
+        }
+        else if(shaderType == TOKEN_TYPE_TEXT)
+        {
+          mShaderType = ShaderType::TEXT;
+        }
+        else if(shaderType == TOKEN_TYPE_COLOR)
+        {
+          mShaderType = ShaderType::COLOR;
+        }
+        else if(shaderType == TOKEN_TYPE_MODEL_3D)
+        {
+          mShaderType = ShaderType::MODEL_3D;
+        }
+        else if(shaderType == TOKEN_TYPE_NPATCH)
+        {
+          mShaderType = ShaderType::NPATCH;
+        }
+        else if(shaderType == TOKEN_TYPE_CUSTOM)
+        {
+          mShaderType = ShaderType::CUSTOM;
+        }
+        else
+        {
+          mShaderType = ShaderType::UNKNOWN;
+        }
+      }
+
+      if(mShaderType == ShaderType::UNKNOWN)
+      {
+        DALI_LOG_ERROR("Can't find proper type.");
+        break;
+      }
+    }
+    else if(key == TOKEN_OPTION)
+    {
+      Property::Map optionMap = value.Get<Property::Map>();
+      for(size_t optionMapIdx = 0; optionMapIdx < optionMap.Count(); ++optionMapIdx)
+      {
+        const KeyValuePair optionPair(optionMap.GetKeyValue(optionMapIdx));
+
+        if(optionPair.first.type == Property::Key::INDEX)
+        {
+          continue; // We don't consider index keys.
+        }
+
+        const std::string&     optionKey(optionPair.first.stringKey);
+
+        if(optionKey == TOKEN_OPTION_ROUNDED_CORNER)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::ROUNDED_CORNER);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_BORDERLINE)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::BORDERLINE);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_CUTOUT)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::CUTOUT);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_ATLAS_DEFAULT)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::ATLAS_DEFAULT);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_ATLAS_CUSTOM)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::ATLAS_CUSTOM);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_BLUR_EDGE)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::BLUR_EDGE);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_MASKING)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::MASKING);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_YUV_TO_RGB)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::YUV_TO_RGB);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_YUV_AND_RGB)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::YUV_AND_RGB);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_MULTI_COLOR)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::MULTI_COLOR);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_STYLES)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::STYLES);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_OVERLAY)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::OVERLAY);
+          }
+        }
+        else if(optionKey == TOKEN_OPTION_EMOJI)
+        {
+          if(optionPair.second.Get<bool>())
+          {
+            mShaderOptions.push_back(Flag::EMOJI);
+          }
+        }
+        else
+        {
+          DALI_LOG_WARNING("Can't find this flag[%s] \n",optionKey.c_str());
+        }
+      }
+    }
+    else if(key == TOKEN_CUSTOM_VERTEX)
+    {
+      if(value.GetType() == Property::STRING)
+      {
+        mVertexShader = value.Get<std::string>();
+      }
+    }
+    else if(key == TOKEN_CUSTOM_FRAMENT)
+    {
+      if(value.GetType() == Property::STRING)
+      {
+        mFragmentShader = value.Get<std::string>();
+      }
+    }
+    else if(key == TOKEN_CUSTOM_NAME)
+    {
+      if(value.GetType() == Property::STRING)
+      {
+        mShaderName = value.Get<std::string>();
+      }
+    }
+    else if(key == TOKEN_OPTION_STRETCH_X)
+    {
+      if(value.GetType() == Property::INTEGER)
+      {
+        mNpatchXStretchCount = value.Get<int>();
+      }
+    }
+    else if(key == TOKEN_OPTION_STRETCH_Y)
+    {
+      if(value.GetType() == Property::INTEGER)
+      {
+        mNpatchYStretchCount = value.Get<int>();
+      }
+    }
+  }
+}
+
+PrecompileShaderOption::ShaderType PrecompileShaderOption::GetShaderType() const
+{
+  return mShaderType;
+}
+
+std::vector<PrecompileShaderOption::Flag> PrecompileShaderOption::GetShaderOptions() const
+{
+  return mShaderOptions;
+}
+
+std::string PrecompileShaderOption::GetShaderName() const
+{
+  return mShaderName;
+}
+
+std::string PrecompileShaderOption::GetVertexShader() const
+{
+  return mVertexShader;
+}
+
+std::string PrecompileShaderOption::GetFragmentShader() const
+{
+  return mFragmentShader;
+}
+
+uint32_t PrecompileShaderOption::GetNpatchXStretchCount() const
+{
+  return mNpatchXStretchCount;
+}
+
+uint32_t PrecompileShaderOption::GetNpatchYStretchCount()  const
+{
+  return mNpatchYStretchCount;
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/devel-api/visual-factory/precompile-shader-option.h b/dali-toolkit/devel-api/visual-factory/precompile-shader-option.h
new file mode 100644 (file)
index 0000000..4dc46e0
--- /dev/null
@@ -0,0 +1,209 @@
+#ifndef DALI_TOOLKIT_PRECOMPILE_SHADER_OPTION_H_
+#define DALI_TOOLKIT_PRECOMPILE_SHADER_OPTION_H_
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/object/property-map.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <memory>
+#include <string>
+#include <string_view>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+
+/**
+ * @brief PrecompiledShaderOption is a class for precompiled shader option.
+ *
+ * Below is the available shader list for precompile
+ * -----------------------------------------------------------------
+ * COLOR_SHADER,
+ * COLOR_SHADER_ROUNDED_CORNER,
+ * COLOR_SHADER_BORDERLINE,
+ * COLOR_SHADER_ROUNDED_BORDERLINE,
+ * COLOR_SHADER_BLUR_EDGE,
+ * COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE,
+ * COLOR_SHADER_CUTOUT,
+ * COLOR_SHADER_CUTOUT_ROUNDED_CORNER,
+ * COLOR_SHADER_CUTOUT_BORDERLINE,
+ * COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE,
+ * COLOR_SHADER_CUTOUT_BLUR_EDGE,
+ * COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE,
+ * IMAGE_SHADER,
+ * IMAGE_SHADER_ROUNDED_CORNER,
+ * IMAGE_SHADER_BORDERLINE,
+ * IMAGE_SHADER_ROUNDED_BORDERLINE,
+ * IMAGE_SHADER_MASKING,
+ * IMAGE_SHADER_ROUNDED_CORNER_MASKING,
+ * IMAGE_SHADER_BORDERLINE_MASKING,
+ * IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING,
+ * IMAGE_SHADER_ATLAS_DEFAULT_WRAP,
+ * IMAGE_SHADER_ATLAS_CUSTOM_WRAP,
+ * IMAGE_SHADER_YUV_TO_RGB,
+ * IMAGE_SHADER_ROUNDED_CORNER_YUV_TO_RGB,
+ * IMAGE_SHADER_BORDERLINE_YUV_TO_RGB,
+ * IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_TO_RGB,
+ * IMAGE_SHADER_YUV_AND_RGB,
+ * IMAGE_SHADER_ROUNDED_CORNER_YUV_AND_RGB,
+ * IMAGE_SHADER_BORDERLINE_YUV_AND_RGB,
+ * IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_AND_RGB,
+ * NATIVE_IMAGE_SHADER,
+ * NATIVE_IMAGE_SHADER_ROUNDED_CORNER,
+ * NATIVE_IMAGE_SHADER_BORDERLINE,
+ * NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE,
+ * NATIVE_IMAGE_SHADER_MASKING,
+ * NATIVE_IMAGE_SHADER_ROUNDED_CORNER_MASKING,
+ * NATIVE_IMAGE_SHADER_BORDERLINE_MASKING,
+ * NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING,
+ * NATIVE_IMAGE_SHADER_ATLAS_DEFAULT_WRAP,
+ * NATIVE_IMAGE_SHADER_ATLAS_CUSTOM_WRAP,
+ * NINE_PATCH_SHADER,
+ * NINE_PATCH_MASK_SHADER,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_OVERLAY,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_OVERLAY,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_OVERLAY_AND_EMOJI,
+ * TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_OVERLAY_AND_EMOJI,
+ * TEXT_SHADER_MULTI_COLOR_TEXT,
+ * TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE,
+ * TEXT_SHADER_MULTI_COLOR_TEXT_WITH_OVERLAY,
+ * TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE_AND_OVERLAY,
+ * -----------------------------------------------------------------
+ *
+ * Please refer to precompile-shader-option.cpp if you want to check string key for each shader flag
+ */
+class DALI_TOOLKIT_API PrecompileShaderOption
+{
+public:
+  enum class ShaderType
+  {
+    UNKNOWN = 0,
+    COLOR,
+    IMAGE,
+    TEXT,
+    NPATCH,
+    MODEL_3D, // ToDO: Need to add more options
+    CUSTOM,
+  };
+
+  enum class Flag
+  {
+    ROUNDED_CORNER = 0,
+    BORDERLINE,
+    BLUR_EDGE,
+    CUTOUT,
+    ATLAS_DEFAULT,
+    ATLAS_CUSTOM,
+    MASKING,
+    YUV_TO_RGB,
+    YUV_AND_RGB,
+    NINE_PATCH,
+    MULTI_COLOR,
+    STYLES,
+    OVERLAY,
+    EMOJI,
+    NATIVE,
+    STRETCH_X,
+    STRETCH_Y,
+  };
+
+  PrecompileShaderOption(const Property::Map& shaderOption);
+  PrecompileShaderOption(const PrecompileShaderOption& rhs);
+  PrecompileShaderOption& operator=(const PrecompileShaderOption& rhs);
+
+  using ShaderOptions= std::vector<Flag>;
+
+public:
+  /**
+   * @brief Convert the shader option
+   * @param[in] shaderOption The shader option property map
+   */
+  void ConvertShaderMap(const Property::Map& shaderOption);
+
+  /**
+   * @brief Get the shader type
+   *
+   * @return The shader type
+   */
+  ShaderType GetShaderType() const;
+
+  /**
+   * @brief Get the shader optinons
+   *
+   * @return The shader options
+   */
+  ShaderOptions GetShaderOptions() const;
+
+  /**
+   * @brief Get the name of shader
+   * @return The shader name
+   */
+  std::string GetShaderName() const;
+
+  /**
+   * @brief Get the vertex shader
+   *
+   * @return The vertex shader
+   */
+  std::string GetVertexShader() const;
+
+  /**
+   * @brief Get the fragment shader
+   *
+   * @return The fragment shader
+   */
+  std::string GetFragmentShader() const;
+
+  /**
+   * @brief Get the XStretchCount for npatch
+   *
+   * @return The NpatchXStretchCount
+   */
+  uint32_t GetNpatchXStretchCount() const;
+
+    /**
+   * @brief Get the YStretchCount for npatch
+   *
+   * @return The NpatchYStretchCount
+   */
+  uint32_t GetNpatchYStretchCount() const;
+
+private:
+  ShaderType mShaderType;
+  std::vector<Flag> mShaderOptions;
+  std::string mShaderName;
+  std::string mVertexShader;
+  std::string mFragmentShader;
+  uint32_t mNpatchXStretchCount;
+  uint32_t mNpatchYStretchCount;
+};
+
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_PRECOMPILE_SHADER_OPTION_H_
index 94f7617..9a21f90 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -94,6 +94,16 @@ void Visual::Base::CreatePropertyMap(Dali::Property::Map& map) const
   GetImplementation(*this).CreatePropertyMap(map);
 }
 
+void Visual::Base::DoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
+{
+  GetImplementation(*this).DoAction(actionId, attributes);
+}
+
+void Visual::Base::DoActionExtension(const Dali::Property::Index actionId, const Dali::Any& attributes)
+{
+  GetImplementation(*this).DoActionExtension(actionId, attributes);
+}
+
 Visual::Type Visual::Base::GetType() const
 {
   return GetImplementation(*this).GetType();
index dc31868..0025e65 100644 (file)
@@ -19,6 +19,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/actors/actor.h>
+#include <dali/public-api/object/any.h>
 #include <dali/public-api/object/base-handle.h>
 
 // INTERNAL INCLUDES
@@ -194,6 +195,22 @@ public:
   void CreatePropertyMap(Dali::Property::Map& map) const;
 
   /**
+   * @brief Performs an action on the visual with the given action id and attributes.
+   *
+   * @param[in] actionId The id of the action to perform this API only takes an Index
+   * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
+   */
+  void DoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes);
+
+  /**
+   * @brief Performs an action on the visual with the given action id and attributes.
+   *
+   * @param[in] actionId The id of the action to perform this API only takes an Index
+   * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
+   */
+  void DoActionExtension(const Dali::Property::Index actionId, const Dali::Any& attributes);
+
+  /**
    * @brief Get the type of this visual.
    *
    * @return The the type of this visual.
index 368f8d5..54fb3f3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -96,11 +96,21 @@ Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyMap)
   return GetImplementation(*this).CreateVisual(propertyMap);
 }
 
+Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyMap, CreationOptions creationOptions)
+{
+  return GetImplementation(*this).CreateVisual(propertyMap, creationOptions);
+}
+
 Visual::Base VisualFactory::CreateVisual(const std::string& url, ImageDimensions size)
 {
   return GetImplementation(*this).CreateVisual(url, size);
 }
 
+Visual::Base VisualFactory::CreateVisual(const std::string& url, ImageDimensions size, CreationOptions creationOptions)
+{
+  return GetImplementation(*this).CreateVisual(url, size, creationOptions);
+}
+
 void VisualFactory::SetPreMultiplyOnLoad(bool preMultiply)
 {
   GetImplementation(*this).SetPreMultiplyOnLoad(preMultiply);
@@ -111,11 +121,26 @@ bool VisualFactory::GetPreMultiplyOnLoad() const
   return GetImplementation(*this).GetPreMultiplyOnLoad();
 }
 
+void VisualFactory::SetDefaultCreationOptions(VisualFactory::CreationOptions creationOptions)
+{
+  GetImplementation(*this).SetDefaultCreationOptions(creationOptions);
+}
+
+VisualFactory::CreationOptions VisualFactory::GetDefaultCreationOptions() const
+{
+  return GetImplementation(*this).GetDefaultCreationOptions();
+}
+
 void VisualFactory::DiscardVisual(Visual::Base visual)
 {
   GetImplementation(*this).DiscardVisual(visual);
 }
 
+bool VisualFactory::AddPrecompileShader(const Property::Map& map)
+{
+  return GetImplementation(*this).AddPrecompileShader(map);
+}
+
 void VisualFactory::UsePreCompiledShader()
 {
   GetImplementation(*this).UsePreCompiledShader();
index cc62c2b..2ce5e8b 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VISUAL_FACTORY_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -53,6 +53,13 @@ class VisualFactory;
 class DALI_TOOLKIT_API VisualFactory : public BaseHandle
 {
 public:
+  enum CreationOptions
+  {
+    NONE = 0,
+
+    IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY = 1 << 0, ///< Load static images only when we use the image visual.
+  };
+
   /**
    * @brief Create or retrieve VisualFactory singleton.
    *
@@ -99,6 +106,16 @@ public:
   Visual::Base CreateVisual(const Property::Map& propertyMap);
 
   /**
+   * @brief Request the visual with some options
+   *
+   * @param[in] propertyMap The map contains the properties required by the visual.
+   *            The content of the map determines the type of visual that will be returned.
+   * @param[in] creationOptions The creation option.
+   * @return The handle to the created visual
+   */
+  Visual::Base CreateVisual(const Property::Map& propertyMap, CreationOptions creationOptions);
+
+  /**
    * @brief Request the visual to render the given resource at the url.
    *
    * @param[in] url The URL to the resource to be rendered.
@@ -108,6 +125,16 @@ public:
   Visual::Base CreateVisual(const std::string& url, ImageDimensions size);
 
   /**
+   * @brief Request the visual to render the given resource at the url with some options.
+   *
+   * @param[in] url The URL to the resource to be rendered.
+   * @param[in] size The width and height to fit the loaded image to.
+   * @param[in] creationOptions The creation option.
+   * @return The pointer pointing to the visual
+   */
+  Visual::Base CreateVisual(const std::string& url, ImageDimensions size, CreationOptions creationOptions);
+
+  /**
    * @brief Enable or disable premultiplying alpha in images and image visuals.
    *
    * The default is to enable pre-multiplication on load.
@@ -127,6 +154,20 @@ public:
   bool GetPreMultiplyOnLoad() const;
 
   /**
+   * @brief Set the default creation options when we skip the creation options parameter.
+   *
+   * @param[in] creationOptions The default creation options for the visual factory.
+   */
+  void SetDefaultCreationOptions(CreationOptions creationOptions);
+
+  /**
+   * @brief Set the default creation options when we skip the creation options parameter.
+   *
+   * @return The default creation options for the visual factory.
+   */
+  CreationOptions GetDefaultCreationOptions() const;
+
+  /**
    * @brief Discard visual base. It will keep reference of visual until idle callback called.
    *
    * @param[in] visual Discarded visual base.
@@ -134,6 +175,61 @@ public:
   void DiscardVisual(Visual::Base visual);
 
   /**
+   * @brief Adds a list of pre-compiled shaders to the visual factory.
+   *
+   * This function allows you to add the desired precompile shader to the list.
+   * you can set it through PropertyMap.
+   * you need to know the values for setting well to use them, so please refer to the explanation below.
+   *
+   * shaderType: Set the desired shader type. dali provides these type: "image","text","color","3d" and "custom"
+   * shaderOption: Set the desired shader option. dali provides these flag: dali provides a lot of shader options, so user need to check proper shader option.
+   * vertexShader: Set the vertext shader that user want. this is for custom shader.
+   * fragmentShader: Set the fragment shader that user want. this is for custom shader.
+   * shaderName: if user want to set shader name, use this. this is for custom shader.(optional)
+   *
+   *(example)
+   * // Precompile image and Custom shader
+   * // Image Case
+   *   Property::Map imageShader;
+   *   imageShader["shaderType"]   = "image";
+   *   imageShader["shaderOption"] = Property::Map().Add("YUV_AND_RGB", true);
+   *   imageShader["shaderName"]   = "IMAGE_SHADER_ROUNDED_CORNER_YUV_TO_RGB";
+   *
+   * // Text Case
+   *   Property::Map textShader;
+   *   textShader["shaderType"]   = "text";
+   *   textShader["shaderOption"] = Property::Map()
+   *                                       .Add("MULTI_COLOR", true)
+   *                                       .Add("OVERLAY", true);
+   * // Color Case
+   *   Property::Map colorShader;
+   *   colorShader["shaderType"]   = "color";
+   *   colorShader["shaderOption"] = Property::Map()
+   *                                   .Add("CUTOUT", true)
+   *                                   .Add("BORDERLINE", true);
+   * // Custom Case
+   *   Property::Map customShader;
+   *   customShader["shaderType"]      = "custom";
+   *   customShader["vertexShader"]    = "..."; // input vertexShader code
+   *   customShader["fragementShader"] = "..."; // input fragementShader code
+   *   customShader["shaderName"]      = "MyCustomShader";
+   *
+   *   // Add to Precompile Shader
+   *   VisualFactory factory = VisualFactory::Get();
+   *   factory.AddPrecompileShader(imageShader);
+   *   factory.AddPrecompileShader(textShader);
+   *   factory.AddPrecompileShader(colorShader);
+   *   factory.AddPrecompileShader(customShader);
+   *   factory.UsePreCompiledShader();
+   *
+   *  If you want to detail information about shader option. please refer to precompil-shader-option.h.
+   *
+   *  @param[in] map The shader information for precompile
+   *  @return True if shader is successfully added to the precompiled list, false otherwise.
+   */
+  bool AddPrecompileShader(const Property::Map& map);
+
+  /**
    * @brief Compile the visual shader in advance. Afterwards,
    * when a visual using a new shader is requested, the pre-compiled shader is used.
    *
index bae7111..4d5ed48 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_COLOR_VISUAL_PROPERTIES_DEVEL_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -46,12 +46,34 @@ enum
    * @note Optional.
    * @note The default is 0.
    * @note The visual size increases by the blur radius.
+   * @note We cannot use blur radius and borderline properties at the same time.
    */
   BLUR_RADIUS = MIX_COLOR + 2,
+
+  /**
+   * @brief Policy of cutout the color render result.
+   * @details Name "cutoutPolicy", type Property::INTEGER.
+   * @note Optional.
+   * @note The default is CutoutPolicy::NONE.
+   */
+  CUTOUT_POLICY = MIX_COLOR + 3,
 };
 
 } // namespace Property
 
+/**
+ * @brief Enumeration for cutout policy.
+ */
+namespace CutoutPolicy
+{
+enum Type
+{
+  NONE,                           ///< Fully render the visual area (Default)
+  CUTOUT_VIEW,                    ///< Cutout the area of the view. It will use size of view.
+  CUTOUT_VIEW_WITH_CORNER_RADIUS, ///< Cutout the area of the view include visual's corner radius. It will use size of view.
+};
+} // namespace CutoutPolicy
+
 } // namespace DevelColorVisual
 
 } // namespace Toolkit
index 90e868e..a8c2c75 100644 (file)
@@ -210,7 +210,28 @@ enum Type
    * This flag is useful if given resource has low fps, so we don't need to render every frame.
    * @note It is used in the AnimatedVectorImageVisual. The default is false.
    */
-  NOTIFY_AFTER_RASTERIZATION = ORIENTATION_CORRECTION + 17
+  NOTIFY_AFTER_RASTERIZATION = ORIENTATION_CORRECTION + 17,
+
+  /**
+   * @brief Whether to synchronize image texture size to visual size.
+   * @details Name "synchronousSizing", type Property::BOOLEAN.
+   * If this property is true, ImageVisual ignores mDesiredSize.
+   * @note Used by the ImageVisual. The default is false.
+   */
+  SYNCHRONOUS_SIZING = ORIENTATION_CORRECTION + 18,
+
+  /**
+   * @brief Specifies a speed factor for the animated image frame.
+   * @details Name "frameSpeedFactor", type Property::FLOAT.
+   *
+   * The speed factor is a multiplier of the normal velocity of the animation. Values between [0,1] will
+   * slow down the animation and values above one will speed up the animation.
+   *
+   * @note The range of this value is clamped between [0.01f ~ 100.0f].
+   *       It might be supported out of bound, and negative value in future.
+   * @note It is used in the AnimatedImageVisual and AnimatedVectorImageVisual. The default is 1.0f.
+   */
+  FRAME_SPEED_FACTOR = ORIENTATION_CORRECTION + 19,
 };
 
 } //namespace Property
index 679a31f..12e2e21 100644 (file)
@@ -132,7 +132,8 @@ enum FittingMode
   OVER_FIT_KEEP_ASPECT_RATIO, ///< The visual should be scaled to fit, preserving aspect ratio. The visual will be filled without empty area, and outside is cropped away.
   CENTER,                     ///< The visual should keep original size of image. It is not scaled and not strecthed.
   FIT_HEIGHT,                 ///< The visual should be scaled to fit, preserving aspect ratio. Height is scaled proportionately to maintain aspect ratio. It will be deprecated.
-  FIT_WIDTH                   ///< The visual should be scaled to fit, preserving aspect ratio. Width is scaled proportionately to maintain aspect ratio. It will be deprecated.
+  FIT_WIDTH,                  ///< The visual should be scaled to fit, preserving aspect ratio. Width is scaled proportionately to maintain aspect ratio. It will be deprecated.
+  DONT_CARE                   ///< The visual should be not use fittingMode.
 };
 
 /**
index c4af9d4..0d41bd5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -429,7 +429,7 @@ struct SignalConnector<Actor>
   ConnectionTracker* mTracker;
   const std::string& mName;
 
-  SignalConnector<Actor>(ConnectionTracker* tracker, Actor& actor, const std::string& name)
+  SignalConnector(ConnectionTracker* tracker, Actor& actor, const std::string& name)
   : mActor(actor),
     mTracker(tracker),
     mName(name)
@@ -450,7 +450,7 @@ struct SignalConnector<PropertyNotification>
   PropertyNotification& mNotification;
   ConnectionTracker*    mTracker;
 
-  SignalConnector<PropertyNotification>(ConnectionTracker* tracker, PropertyNotification& notification)
+  SignalConnector(ConnectionTracker* tracker, PropertyNotification& notification)
   : mNotification(notification),
     mTracker(tracker)
   {
index b570c04..9dc5594 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -75,14 +75,14 @@ private:
   auto FindElementCaseInsensitive(std::string_view key) const
   {
     return std::find_if(
-      Begin(), End(), [key](auto& e) { return Dali::CaseInsensitiveStringCompare(e.key, key); });
+      Begin(), End(), [key](auto& e)
+      { return Dali::CaseInsensitiveStringCompare(e.key, key); });
   }
 
   auto FindElement(std::string_view key)
   {
-    return std::find_if(container.begin(), container.end(), [key](auto& e) {
-      return bool(key == e.key);
-    });
+    return std::find_if(container.begin(), container.end(), [key](auto& e)
+                        { return bool(key == e.key); });
   }
 
 public:
@@ -94,7 +94,7 @@ public:
   /**
    * Constructor
    */
-  Dictionary<EntryType>() = default;
+  Dictionary() = default;
 
   /**
    * Add a key value pair to the dictionary.
index b000325..a778371 100644 (file)
@@ -101,7 +101,7 @@ void CheckBoxButton::OnStateChange(State newState)
   // TODO: replace it with OnPropertySet hook once Button::Property::SELECTED will be consistently used
   if(newState == SELECTED_STATE || newState == UNSELECTED_STATE)
   {
-    auto* accessible = GetAccessibleObject();
+    auto accessible = GetAccessibleObject();
     if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
     {
       accessible->EmitStateChanged(Dali::Accessibility::State::CHECKED, newState == SELECTED_STATE ? 1 : 0, 0);
index 70e7261..4e26fad 100644 (file)
@@ -209,7 +209,7 @@ void PushButton::OnStateChange(State newState)
   // TODO: replace it with OnPropertySet hook once Button::Property::SELECTED will be consistently used
   if(newState == SELECTED_STATE || newState == UNSELECTED_STATE)
   {
-    auto* accessible = GetAccessibleObject();
+    auto accessible = GetAccessibleObject();
     if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
     {
       accessible->EmitStateChanged(Dali::Accessibility::State::PRESSED, newState == SELECTED_STATE ? 1 : 0, 0);
index d352f2b..3241ea0 100644 (file)
@@ -109,7 +109,7 @@ void RadioButton::OnStateChange(State newState)
   // TODO: replace it with OnPropertySet hook once Button::Property::SELECTED will be consistently used
   if(newState == SELECTED_STATE || newState == UNSELECTED_STATE)
   {
-    auto* accessible = GetAccessibleObject();
+    auto accessible = GetAccessibleObject();
     if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
     {
       accessible->EmitStateChanged(Dali::Accessibility::State::CHECKED, newState == SELECTED_STATE ? 1 : 0, 0);
index 5dd9b87..d292b0e 100644 (file)
@@ -414,7 +414,7 @@ void ToggleButton::OnStateChange(State newState)
   // TODO: replace it with OnPropertySet hook once Button::Property::SELECTED will be consistently used
   if(newState == SELECTED_STATE || newState == UNSELECTED_STATE)
   {
-    auto* accessible = GetAccessibleObject();
+    auto accessible = GetAccessibleObject();
     if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
     {
       accessible->EmitStateChanged(Dali::Accessibility::State::CHECKED, mCurrentToggleIndex ? 1 : 0, 0);
index 282ebb4..1b3cae7 100644 (file)
@@ -47,6 +47,7 @@ BaseHandle Create()
 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::CanvasView, Toolkit::Control, Create);
 DALI_PROPERTY_REGISTRATION(Toolkit, CanvasView, "viewBox", VECTOR2, VIEW_BOX)
 DALI_PROPERTY_REGISTRATION(Toolkit, CanvasView, "synchronousLoading", BOOLEAN, SYNCHRONOUS_LOADING)
+DALI_PROPERTY_REGISTRATION(Toolkit, CanvasView, "rasterizationRequestManually", BOOLEAN, RASTERIZATION_REQUEST_MANUALLY)
 DALI_TYPE_REGISTRATION_END()
 } // anonymous namespace
 
@@ -58,17 +59,22 @@ CanvasView::CanvasView(const Vector2& viewBox)
   mTexture(),
   mTextureSet(),
   mSize(viewBox),
-  mIsSynchronous(true)
+  mIsSynchronous(true),
+  mManualRasterization(false),
+  mProcessorRegistered(false)
 {
 }
 
 CanvasView::~CanvasView()
 {
-  if(Adaptor::IsAvailable())
+  if(Adaptor::IsAvailable() && mProcessorRegistered)
   {
-    Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
-    mRasterizingTask.Reset();
-    Adaptor::Get().UnregisterProcessor(*this, true);
+    if(mRasterizingTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
+      mRasterizingTask.Reset();
+    }
+    Adaptor::Get().UnregisterProcessorOnce(*this, true);
   }
 }
 
@@ -94,7 +100,8 @@ void CanvasView::OnInitialize()
 
   Self().SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::IMAGE);
 
-  Adaptor::Get().RegisterProcessor(*this, true);
+  // Request rasterization once at very first time.
+  RequestRasterization();
 }
 
 void CanvasView::OnRelayout(const Vector2& size, RelayoutContainer& container)
@@ -147,6 +154,15 @@ void CanvasView::SetProperty(BaseObject* object, Property::Index propertyIndex,
         }
         break;
       }
+      case Toolkit::CanvasView::Property::RASTERIZATION_REQUEST_MANUALLY:
+      {
+        bool isRasterizationManually;
+        if(value.Get(isRasterizationManually))
+        {
+          canvasViewImpl.SetRasterizationRequestManually(isRasterizationManually);
+        }
+        break;
+      }
     }
   }
 }
@@ -173,6 +189,11 @@ Property::Value CanvasView::GetProperty(BaseObject* object, Property::Index prop
         value = canvasViewImpl.IsSynchronous();
         break;
       }
+      case Toolkit::CanvasView::Property::RASTERIZATION_REQUEST_MANUALLY:
+      {
+        value = canvasViewImpl.IsRasterizationRequestManually();
+        break;
+      }
     }
   }
   return value;
@@ -180,27 +201,39 @@ Property::Value CanvasView::GetProperty(BaseObject* object, Property::Index prop
 
 void CanvasView::Process(bool postProcessor)
 {
+  mProcessorRegistered = false;
+
   if(mCanvasRenderer && mCanvasRenderer.IsCanvasChanged() && mSize.width > 0 && mSize.height > 0)
   {
     AddRasterizationTask();
   }
+
+  // If we are not doing manual rasterization, register processor once again.
+  // TODO : Could we reqest it only if IsCanvasChagned() is true?
+  if(!mManualRasterization)
+  {
+    RequestRasterization();
+  }
 }
 
 void CanvasView::AddRasterizationTask()
 {
-  mRasterizingTask = new CanvasRendererRasterizingTask(mCanvasRenderer, MakeCallback(this, &CanvasView::ApplyRasterizedImage));
-
-  if(mCanvasRenderer.Commit())
+  if(mCanvasRenderer && mCanvasRenderer.Commit())
   {
     if(mIsSynchronous)
     {
-      mRasterizingTask->Process();
-      ApplyRasterizedImage(mRasterizingTask);
-      mRasterizingTask.Reset(); // We don't need it anymore.
+      CanvasRendererRasterizingTaskPtr rasterizingTask = new CanvasRendererRasterizingTask(mCanvasRenderer, MakeCallback(this, &CanvasView::ApplyRasterizedImage));
+      rasterizingTask->Process();
+      ApplyRasterizedImage(rasterizingTask);
+      rasterizingTask.Reset(); // We don't need it anymore.
     }
     else
     {
-      AsyncTaskManager::Get().AddTask(mRasterizingTask);
+      if(!mRasterizingTask)
+      {
+        mRasterizingTask = new CanvasRendererRasterizingTask(mCanvasRenderer, MakeCallback(this, &CanvasView::ApplyRasterizedImage));
+        AsyncTaskManager::Get().AddTask(mRasterizingTask);
+      }
     }
   }
 }
@@ -230,10 +263,13 @@ void CanvasView::ApplyRasterizedImage(CanvasRendererRasterizingTaskPtr task)
     }
   }
 
-  mRasterizingTask.Reset(); // We don't need it anymore
+  if(task == mRasterizingTask)
+  {
+    mRasterizingTask.Reset(); // We don't need it anymore
+  }
 
   //If there are accumulated changes to CanvasRenderer during Rasterize, Rasterize once again.
-  if(!mIsSynchronous && mCanvasRenderer && mCanvasRenderer.IsCanvasChanged())
+  if(!mIsSynchronous && !mManualRasterization && mCanvasRenderer && mCanvasRenderer.IsCanvasChanged())
   {
     AddRasterizationTask();
   }
@@ -266,6 +302,15 @@ bool CanvasView::RemoveAllDrawables()
   return false;
 }
 
+void CanvasView::RequestRasterization()
+{
+  if(!mProcessorRegistered && Adaptor::IsAvailable())
+  {
+    mProcessorRegistered = true;
+    Adaptor::Get().RegisterProcessorOnce(*this, true);
+  }
+}
+
 bool CanvasView::SetViewBox(const Vector2& viewBox)
 {
   if(mCanvasRenderer && mCanvasRenderer.SetViewBox(viewBox))
@@ -289,11 +334,28 @@ void CanvasView::SetSynchronous(const bool isSynchronous)
   mIsSynchronous = isSynchronous;
 }
 
-const bool CanvasView::IsSynchronous()
+const bool CanvasView::IsSynchronous() const
 {
   return mIsSynchronous;
 }
 
+void CanvasView::SetRasterizationRequestManually(const bool isRasterizationManually)
+{
+  if(mManualRasterization != isRasterizationManually)
+  {
+    mManualRasterization = isRasterizationManually;
+    if(!mManualRasterization)
+    {
+      RequestRasterization();
+    }
+  }
+}
+
+const bool CanvasView::IsRasterizationRequestManually() const
+{
+  return mManualRasterization;
+}
+
 } // namespace Internal
 } // namespace Toolkit
 } // namespace Dali
index 6f0fa11..82f7ff3 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_CANVAS_VIEW_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -90,6 +90,11 @@ public:
    */
   bool RemoveAllDrawables();
 
+  /**
+   * @copydoc Toolkit::Control::CanvasView::RequestRasterization
+   */
+  void RequestRasterization();
+
 private: // From Control
   /**
    * @copydoc Control::OnRelayout
@@ -131,7 +136,21 @@ private: // From Control
    *
    * @return Returns true if synchronously.
    */
-  const bool IsSynchronous();
+  const bool IsSynchronous() const;
+
+  /**
+   * @brief Set to request rasterize canvas manually or automatically
+   *
+   * @param isRasterizationManually True if rasterize canvas manually.
+   */
+  void SetRasterizationRequestManually(const bool isRasterizationManually);
+
+  /**
+   * @brief Whether to request rasterize canvas manually or automatically
+   *
+   * @return Returns true if rasterize canvas manually.
+   */
+  const bool IsRasterizationRequestManually() const;
 
   /**
    * @bried Rasterize the canvas, and add it to the view.
@@ -172,7 +191,9 @@ private:
   TextureSet                       mTextureSet;
   Vector2                          mSize;
   CanvasRendererRasterizingTaskPtr mRasterizingTask;
-  bool                             mIsSynchronous;
+  bool                             mIsSynchronous : 1;
+  bool                             mManualRasterization : 1;
+  bool                             mProcessorRegistered : 1;
 };
 
 } // namespace Internal
index 4e99e5c..9ded0d2 100644 (file)
@@ -45,6 +45,7 @@
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali-toolkit/public-api/align-enumerations.h>
 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
 #include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
@@ -76,6 +77,7 @@ const Scripting::StringEnum ControlStateTable[] = {
   {"DISABLED", Toolkit::DevelControl::DISABLED},
 };
 const unsigned int ControlStateTableCount = sizeof(ControlStateTable) / sizeof(ControlStateTable[0]);
+const Vector4      FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
 namespace
 {
@@ -240,7 +242,7 @@ void DiscardVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVis
   Toolkit::Visual::Base visual = (*sourceIter)->visual;
   if(visual)
   {
-    if(Stage::IsInstalled())
+    if(DALI_LIKELY(Dali::Adaptor::IsAvailable()))
     {
       Toolkit::VisualFactory::Get().DiscardVisual(visual);
     }
@@ -256,24 +258,56 @@ void DiscardVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVis
  * @param[in] attributes The attributes with which to perfrom this action.
  * @return true if action has been accepted by this control
  */
-const char* ACTION_ACCESSIBILITY_ACTIVATED         = "accessibilityActivated";
-const char* ACTION_ACCESSIBILITY_READING_CANCELLED = "ReadingCancelled";
-const char* ACTION_ACCESSIBILITY_READING_PAUSED    = "ReadingPaused";
-const char* ACTION_ACCESSIBILITY_READING_RESUMED   = "ReadingResumed";
-const char* ACTION_ACCESSIBILITY_READING_SKIPPED   = "ReadingSkipped";
-const char* ACTION_ACCESSIBILITY_READING_STOPPED   = "ReadingStopped";
+constexpr const char* ACTION_ACCESSIBILITY_ACTIVATE  = "activate";
+constexpr const char* ACTION_ACCESSIBILITY_ESCAPE    = "escape";
+constexpr const char* ACTION_ACCESSIBILITY_INCREMENT = "increment";
+constexpr const char* ACTION_ACCESSIBILITY_DECREMENT = "decrement";
 
-static bool DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
+// Legacy actions
+constexpr const char* ACTION_ACCESSIBILITY_READING_CANCELLED = "ReadingCancelled";
+constexpr const char* ACTION_ACCESSIBILITY_READING_PAUSED    = "ReadingPaused";
+constexpr const char* ACTION_ACCESSIBILITY_READING_RESUMED   = "ReadingResumed";
+constexpr const char* ACTION_ACCESSIBILITY_READING_SKIPPED   = "ReadingSkipped";
+constexpr const char* ACTION_ACCESSIBILITY_READING_STOPPED   = "ReadingStopped";
+
+bool PerformAccessibilityAction(Toolkit::Control control, const std::string& actionName, const Property::Map& attributes)
 {
-  bool ret = true;
+  using Dali::Accessibility::ActionType;
+  DALI_ASSERT_DEBUG(control);
+  DALI_ASSERT_DEBUG(!DevelControl::AccessibilityActionSignal(control).Empty());
 
-  Dali::BaseHandle handle(object);
+  ActionType action = ActionType::MAX_COUNT;
+  if(actionName == ACTION_ACCESSIBILITY_ACTIVATE)
+  {
+    action = ActionType::ACTIVATE;
+  }
+  else if(actionName == ACTION_ACCESSIBILITY_ESCAPE)
+  {
+    action = ActionType::ESCAPE;
+  }
+  else if(actionName == ACTION_ACCESSIBILITY_INCREMENT)
+  {
+    action = ActionType::INCREMENT;
+  }
+  else if(actionName == ACTION_ACCESSIBILITY_DECREMENT)
+  {
+    action = ActionType::DECREMENT;
+  }
 
-  Toolkit::Control control = Toolkit::Control::DownCast(handle);
+  if(action != ActionType::MAX_COUNT)
+  {
+    bool success = DevelControl::AccessibilityActionSignal(control).Emit({action, Dali::Actor{}});
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Performed AccessibilityAction: %s, success : %d\n", actionName.c_str(), success);
+    return success;
+  }
 
-  DALI_ASSERT_ALWAYS(control);
+  return false;
+}
 
-  if(0 == strcmp(actionName.c_str(), ACTION_ACCESSIBILITY_ACTIVATED) || actionName == "activate")
+bool PerformLegacyAccessibilityAction(Toolkit::Control control, const std::string& actionName)
+{
+  bool ret = true;
+  if(0 == strcmp(actionName.c_str(), ACTION_ACCESSIBILITY_ACTIVATE))
   {
     // if cast succeeds there is an implementation so no need to check
     if(!DevelControl::AccessibilityActivateSignal(control).Empty())
@@ -329,9 +363,42 @@ static bool DoAction(BaseObject* object, const std::string& actionName, const Pr
   {
     ret = false;
   }
+
+  if(ret)
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Performed Legacy AccessibilityAction: %s\n", actionName.c_str());
+  }
   return ret;
 }
 
+bool DoAccessibilityAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
+{
+  Dali::BaseHandle handle(object);
+
+  Toolkit::Control control = Toolkit::Control::DownCast(handle);
+
+  DALI_ASSERT_ALWAYS(control);
+
+  if(!DevelControl::AccessibilityActionSignal(control).Empty())
+  {
+    return PerformAccessibilityAction(control, actionName, attributes);
+  }
+
+  // Fall back to legacy action is no ActionSignal is connected
+  return PerformLegacyAccessibilityAction(control, actionName);
+}
+
+bool DoLegacyAccessibilityAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
+{
+  Dali::BaseHandle handle(object);
+
+  Toolkit::Control control = Toolkit::Control::DownCast(handle);
+
+  DALI_ASSERT_ALWAYS(control);
+
+  return PerformLegacyAccessibilityAction(control, actionName);
+}
+
 /**
  * Connects a callback function with the object's signals.
  * @param[in] object The object providing the signal.
@@ -433,13 +500,20 @@ SignalConnectorType registerSignal8(typeRegistration, SIGNAL_GET_NAME, &DoConnec
 SignalConnectorType registerSignal9(typeRegistration, SIGNAL_GET_DESCRIPTION, &DoConnectSignal);
 SignalConnectorType registerSignal10(typeRegistration, SIGNAL_DO_GESTURE, &DoConnectSignal);
 
-TypeAction registerAction1(typeRegistration, "activate", &DoAction);
-TypeAction registerAction2(typeRegistration, ACTION_ACCESSIBILITY_ACTIVATED, &DoAction);
-TypeAction registerAction3(typeRegistration, ACTION_ACCESSIBILITY_READING_SKIPPED, &DoAction);
-TypeAction registerAction4(typeRegistration, ACTION_ACCESSIBILITY_READING_CANCELLED, &DoAction);
-TypeAction registerAction5(typeRegistration, ACTION_ACCESSIBILITY_READING_STOPPED, &DoAction);
-TypeAction registerAction6(typeRegistration, ACTION_ACCESSIBILITY_READING_PAUSED, &DoAction);
-TypeAction registerAction7(typeRegistration, ACTION_ACCESSIBILITY_READING_RESUMED, &DoAction);
+// === Accessibility Actions === START
+TypeAction registerAction1(typeRegistration, ACTION_ACCESSIBILITY_ACTIVATE, &DoAccessibilityAction);
+TypeAction registerAction2(typeRegistration, ACTION_ACCESSIBILITY_ESCAPE, &DoAccessibilityAction);
+TypeAction registerAction3(typeRegistration, ACTION_ACCESSIBILITY_INCREMENT, &DoAccessibilityAction);
+TypeAction registerAction4(typeRegistration, ACTION_ACCESSIBILITY_DECREMENT, &DoAccessibilityAction);
+// === Accessibility Actions === END
+
+// === Legacy Accessibility Actions === START
+TypeAction registerAction5(typeRegistration, ACTION_ACCESSIBILITY_READING_SKIPPED, &DoLegacyAccessibilityAction);
+TypeAction registerAction6(typeRegistration, ACTION_ACCESSIBILITY_READING_CANCELLED, &DoLegacyAccessibilityAction);
+TypeAction registerAction7(typeRegistration, ACTION_ACCESSIBILITY_READING_STOPPED, &DoLegacyAccessibilityAction);
+TypeAction registerAction8(typeRegistration, ACTION_ACCESSIBILITY_READING_PAUSED, &DoLegacyAccessibilityAction);
+TypeAction registerAction9(typeRegistration, ACTION_ACCESSIBILITY_READING_RESUMED, &DoLegacyAccessibilityAction);
+// === Legacy Accessibility Actions === END
 
 DALI_TYPE_REGISTRATION_END()
 
@@ -495,19 +569,6 @@ static bool IsShowingGeometryOnScreen(Dali::Rect<> rect)
   return rect.width > 0 && rect.height > 0;
 }
 
-Dali::Accessibility::Accessible* ExternalAccessibleGetter(Dali::Actor actor)
-{
-  auto control = Toolkit::Control::DownCast(actor);
-  if(!control)
-  {
-    return nullptr;
-  }
-
-  auto& controlImpl = Toolkit::Internal::GetImplementation(control);
-
-  return controlImpl.GetAccessibleObject();
-}
-
 } // unnamed namespace
 
 // clang-format off
@@ -536,6 +597,10 @@ const PropertyRegistration Control::Impl::PROPERTY_23(typeRegistration, "accessi
 const PropertyRegistration Control::Impl::PROPERTY_24(typeRegistration, "clockwiseFocusableActorId",      Toolkit::DevelControl::Property::CLOCKWISE_FOCUSABLE_ACTOR_ID,     Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
 const PropertyRegistration Control::Impl::PROPERTY_25(typeRegistration, "counterClockwiseFocusableActorId", Toolkit::DevelControl::Property::COUNTER_CLOCKWISE_FOCUSABLE_ACTOR_ID, Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
 const PropertyRegistration Control::Impl::PROPERTY_26(typeRegistration, "automationId",                   Toolkit::DevelControl::Property::AUTOMATION_ID,                    Property::STRING,  &Control::Impl::SetProperty, &Control::Impl::GetProperty);
+const PropertyRegistration Control::Impl::PROPERTY_27(typeRegistration, "accessibilityValue",             Toolkit::DevelControl::Property::ACCESSIBILITY_VALUE,              Property::STRING,  &Control::Impl::SetProperty, &Control::Impl::GetProperty);
+const PropertyRegistration Control::Impl::PROPERTY_28(typeRegistration, "accessibilityScrollable",        Toolkit::DevelControl::Property::ACCESSIBILITY_SCROLLABLE,         Property::BOOLEAN, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
+const PropertyRegistration Control::Impl::PROPERTY_29(typeRegistration, "accessibilityStates",            Toolkit::DevelControl::Property::ACCESSIBILITY_STATES,             Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
+const PropertyRegistration Control::Impl::PROPERTY_30(typeRegistration, "accessibilityIsModal",           Toolkit::DevelControl::Property::ACCESSIBILITY_IS_MODAL,           Property::BOOLEAN, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
 
 // clang-format on
 
@@ -554,6 +619,7 @@ Control::Impl::Impl(Control& controlImpl)
   mStartingPinchScale(nullptr),
   mMargin(0, 0, 0, 0),
   mPadding(0, 0, 0, 0),
+  mSize(0, 0),
   mKeyEventSignal(),
   mKeyInputFocusGainedSignal(),
   mKeyInputFocusLostSignal(),
@@ -574,26 +640,56 @@ Control::Impl::Impl(Control& controlImpl)
   mIsKeyboardFocusGroup(false),
   mIsEmittingResourceReadySignal(false),
   mIdleCallbackRegistered(false),
-  mDispatchKeyEvents(true)
+  mDispatchKeyEvents(true),
+  mProcessorRegistered(false)
 {
-  Dali::Accessibility::Accessible::RegisterExternalAccessibleGetter(&ExternalAccessibleGetter);
+  Accessibility::Accessible::RegisterExternalAccessibleGetter([](Dali::Actor actor) -> std::pair<std::shared_ptr<Accessibility::Accessible>, bool> {
+    auto control = Toolkit::Control::DownCast(actor);
+    if(!control)
+    {
+      return {nullptr, true};
+    }
+
+    auto& controlImpl = Toolkit::Internal::GetImplementation(control);
+    if(controlImpl.mImpl->IsCreateAccessibleEnabled())
+    {
+      return {std::shared_ptr<DevelControl::ControlAccessible>(controlImpl.CreateAccessibleObject()), true};
+    }
+
+    return {nullptr, false};
+  });
+  mAccessibilityProps.states[DevelControl::AccessibilityState::ENABLED] = true;
 }
 
 Control::Impl::~Impl()
 {
-  for(auto&& iter : mVisuals)
+  while(!mVisuals.Empty())
   {
-    StopObservingVisual(iter->visual);
+    auto iter = mVisuals.End() - 1u;
+    StopObservingVisual((*iter)->visual);
+
+    // Discard removed visual. It will be destroyed at next Idle time.
+    DiscardVisual(iter, mVisuals);
   }
 
-  for(auto&& iter : mRemoveVisuals)
+  while(!mRemoveVisuals.Empty())
   {
-    StopObservingVisual(iter->visual);
+    auto removalIter = mRemoveVisuals.End() - 1u;
+    StopObservingVisual((*removalIter)->visual);
+
+    // Discard removed visual. It will be destroyed at next Idle time.
+    DiscardVisual(removalIter, mRemoveVisuals);
   }
 
   // All gesture detectors will be destroyed so no need to disconnect.
   delete mStartingPinchScale;
 
+  if(mProcessorRegistered && Adaptor::IsAvailable())
+  {
+    // Unregister the processor from the adaptor
+    Adaptor::Get().UnregisterProcessorOnce(*this, true);
+  }
+
   if(mIdleCallback && Adaptor::IsAvailable())
   {
     // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
@@ -622,7 +718,7 @@ void Control::Impl::CheckHighlightedObjectGeometry()
   {
     auto lastPosition   = accessible->GetLastPosition();
     auto accessibleRect = accessible->GetExtents(Dali::Accessibility::CoordinateType::WINDOW);
-    auto rect           = GetShowingGeometry(accessibleRect, accessible);
+    auto rect           = GetShowingGeometry(accessibleRect, accessible.get());
 
     switch(mAccessibilityLastScreenRelativeMoveType)
     {
@@ -718,24 +814,38 @@ void Control::Impl::UnregisterAccessibilityPropertySetSignal()
 
 void Control::Impl::OnAccessibilityPropertySet(Dali::Handle& handle, Dali::Property::Index index, const Dali::Property::Value& value)
 {
-  auto* accessible = GetAccessibleObject();
+  auto accessible = GetAccessibleObject();
   if(DALI_LIKELY(accessible))
   {
     if(mAccessibilityGetNameSignal.Empty())
     {
-      if(index == DevelControl::Property::ACCESSIBILITY_NAME || (mAccessibilityName.empty() && index == accessible->GetNamePropertyIndex()))
+      if(index == DevelControl::Property::ACCESSIBILITY_NAME || (mAccessibilityProps.name.empty() && index == accessible->GetNamePropertyIndex()))
       {
         accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::NAME);
+        return;
       }
     }
 
     if(mAccessibilityGetDescriptionSignal.Empty())
     {
-      if(index == DevelControl::Property::ACCESSIBILITY_DESCRIPTION || (mAccessibilityDescription.empty() && index == accessible->GetDescriptionPropertyIndex()))
+      if(index == DevelControl::Property::ACCESSIBILITY_DESCRIPTION || (mAccessibilityProps.description.empty() && index == accessible->GetDescriptionPropertyIndex()))
       {
         accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::DESCRIPTION);
+        return;
       }
     }
+
+    if(index == DevelControl::Property::ACCESSIBILITY_VALUE)
+    {
+      accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::VALUE);
+      return;
+    }
+
+    if(index == DevelControl::Property::ACCESSIBILITY_STATES)
+    {
+      accessible->OnStatePropertySet(mAccessibilityProps.states);
+      return;
+    }
   }
 }
 
@@ -1206,10 +1316,10 @@ void Control::Impl::AddTransitions(Dali::Animation&               animation,
             }
 
             animation.AnimateTo(Property(child, propertyIndex),
-                  animator->targetValue,
-                  animator->alphaFunction,
-                  TimePeriod(animator->timePeriodDelay,
-                            animator->timePeriodDuration));
+                                animator->targetValue,
+                                animator->alphaFunction,
+                                TimePeriod(animator->timePeriodDelay,
+                                           animator->timePeriodDuration));
           }
         }
       }
@@ -1248,14 +1358,14 @@ void Control::Impl::DoActionExtension(Dali::Property::Index visualIndex, Dali::P
 
 void Control::Impl::AppendAccessibilityAttribute(const std::string& key, const std::string value)
 {
-  Property::Value* checkedValue = mAccessibilityAttributes.Find(key);
+  Property::Value* checkedValue = mAccessibilityProps.extraAttributes.Find(key);
   if(checkedValue)
   {
-    mAccessibilityAttributes[key] = Property::Value(value);
+    mAccessibilityProps.extraAttributes[key] = Property::Value(value);
   }
   else
   {
-    mAccessibilityAttributes.Insert(key, value);
+    mAccessibilityProps.extraAttributes.Insert(key, value);
   }
 }
 
@@ -1448,7 +1558,7 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         std::string name;
         if(value.Get(name))
         {
-          controlImpl.mImpl->mAccessibilityName = name;
+          controlImpl.mImpl->mAccessibilityProps.name = std::move(name);
         }
         break;
       }
@@ -1458,27 +1568,17 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         std::string text;
         if(value.Get(text))
         {
-          controlImpl.mImpl->mAccessibilityDescription = text;
-        }
-        break;
-      }
-
-      case Toolkit::DevelControl::Property::ACCESSIBILITY_TRANSLATION_DOMAIN:
-      {
-        std::string text;
-        if(value.Get(text))
-        {
-          controlImpl.mImpl->mAccessibilityTranslationDomain = text;
+          controlImpl.mImpl->mAccessibilityProps.description = std::move(text);
         }
         break;
       }
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE:
       {
-        Dali::Accessibility::Role role;
+        int32_t role;
         if(value.Get(role))
         {
-          controlImpl.mImpl->mAccessibilityRole = role;
+          controlImpl.mImpl->mAccessibilityProps.role = role;
         }
         break;
       }
@@ -1488,7 +1588,7 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         bool highlightable;
         if(value.Get(highlightable))
         {
-          controlImpl.mImpl->mAccessibilityHighlightable = highlightable;
+          controlImpl.mImpl->mAccessibilityProps.isHighlightable = highlightable;
         }
         break;
       }
@@ -1498,7 +1598,7 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         const Property::Map* map = value.GetMap();
         if(map && !map->Empty())
         {
-          controlImpl.mImpl->mAccessibilityAttributes = *map;
+          controlImpl.mImpl->mAccessibilityProps.extraAttributes = *map;
         }
         break;
       }
@@ -1518,9 +1618,9 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         bool hidden;
         if(value.Get(hidden))
         {
-          controlImpl.mImpl->mAccessibilityHidden = hidden;
+          controlImpl.mImpl->mAccessibilityProps.isHidden = hidden;
 
-          auto* accessible = controlImpl.GetAccessibleObject();
+          auto accessible = controlImpl.GetAccessibleObject();
           if(DALI_LIKELY(accessible))
           {
             auto* parent = dynamic_cast<Dali::Accessibility::ActorAccessible*>(accessible->GetParent());
@@ -1556,7 +1656,47 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         std::string automationId;
         if(value.Get(automationId))
         {
-          controlImpl.mImpl->mAutomationId = automationId;
+          controlImpl.mImpl->mAccessibilityProps.automationId = std::move(automationId);
+        }
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_VALUE:
+      {
+        std::string accessibilityValue;
+        if(value.Get(accessibilityValue))
+        {
+          controlImpl.mImpl->mAccessibilityProps.value = std::move(accessibilityValue);
+        }
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_SCROLLABLE:
+      {
+        bool isScrollable;
+        if(value.Get(isScrollable))
+        {
+          controlImpl.mImpl->mAccessibilityProps.isScrollable = isScrollable;
+        }
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_STATES:
+      {
+        int32_t states;
+        if(value.Get(states))
+        {
+          controlImpl.mImpl->mAccessibilityProps.states = Toolkit::DevelControl::AccessibilityStates{static_cast<uint32_t>(states)};
+        }
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_IS_MODAL:
+      {
+        bool isModal;
+        if(value.Get(isModal))
+        {
+          controlImpl.mImpl->mAccessibilityProps.isModal = isModal;
         }
         break;
       }
@@ -1677,37 +1817,31 @@ Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index i
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_NAME:
       {
-        value = controlImpl.mImpl->mAccessibilityName;
+        value = controlImpl.mImpl->mAccessibilityProps.name;
         break;
       }
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_DESCRIPTION:
       {
-        value = controlImpl.mImpl->mAccessibilityDescription;
-        break;
-      }
-
-      case Toolkit::DevelControl::Property::ACCESSIBILITY_TRANSLATION_DOMAIN:
-      {
-        value = controlImpl.mImpl->mAccessibilityTranslationDomain;
+        value = controlImpl.mImpl->mAccessibilityProps.description;
         break;
       }
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE:
       {
-        value = Property::Value(controlImpl.mImpl->mAccessibilityRole);
+        value = controlImpl.mImpl->mAccessibilityProps.role;
         break;
       }
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE:
       {
-        value = controlImpl.mImpl->mAccessibilityHighlightable;
+        value = controlImpl.mImpl->mAccessibilityProps.isHighlightable;
         break;
       }
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_ATTRIBUTES:
       {
-        value = controlImpl.mImpl->mAccessibilityAttributes;
+        value = controlImpl.mImpl->mAccessibilityProps.extraAttributes;
         break;
       }
 
@@ -1719,7 +1853,7 @@ Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index i
 
       case Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN:
       {
-        value = controlImpl.mImpl->mAccessibilityHidden;
+        value = controlImpl.mImpl->mAccessibilityProps.isHidden;
         break;
       }
 
@@ -1737,7 +1871,31 @@ Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index i
 
       case Toolkit::DevelControl::Property::AUTOMATION_ID:
       {
-        value = controlImpl.mImpl->mAutomationId;
+        value = controlImpl.mImpl->mAccessibilityProps.automationId;
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_VALUE:
+      {
+        value = controlImpl.mImpl->mAccessibilityProps.value;
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_SCROLLABLE:
+      {
+        value = controlImpl.mImpl->mAccessibilityProps.isScrollable;
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_STATES:
+      {
+        value = static_cast<int32_t>(controlImpl.mImpl->mAccessibilityProps.states.GetRawData32());
+        break;
+      }
+
+      case Toolkit::DevelControl::Property::ACCESSIBILITY_IS_MODAL:
+      {
+        value = controlImpl.mImpl->mAccessibilityProps.isModal;
         break;
       }
     }
@@ -1748,16 +1906,16 @@ Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index i
 
 void Control::Impl::RemoveAccessibilityAttribute(const std::string& key)
 {
-  Property::Value* value = mAccessibilityAttributes.Find(key);
+  Property::Value* value = mAccessibilityProps.extraAttributes.Find(key);
   if(value)
   {
-    mAccessibilityAttributes[key] = Property::Value();
+    mAccessibilityProps.extraAttributes[key] = Property::Value();
   }
 }
 
 void Control::Impl::ClearAccessibilityAttributes()
 {
-  mAccessibilityAttributes.Clear();
+  mAccessibilityProps.extraAttributes.Clear();
 }
 
 void Control::Impl::SetAccessibilityReadingInfoType(const Dali::Accessibility::ReadingInfoTypes types)
@@ -1797,7 +1955,7 @@ void Control::Impl::SetAccessibilityReadingInfoType(const Dali::Accessibility::R
 Dali::Accessibility::ReadingInfoTypes Control::Impl::GetAccessibilityReadingInfoType() const
 {
   std::string value{};
-  auto        place = mAccessibilityAttributes.Find(READING_INFO_TYPE_ATTRIBUTE_NAME);
+  auto        place = mAccessibilityProps.extraAttributes.Find(READING_INFO_TYPE_ATTRIBUTE_NAME);
   if(place)
   {
     place->Get(value);
@@ -2225,35 +2383,38 @@ void Control::Impl::UpdateVisualProperties(const std::vector<std::pair<Dali::Pro
 
 void Control::Impl::EmitResourceReadySignal()
 {
-  if(!mIsEmittingResourceReadySignal)
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable())) ///< Avoid resource ready callback during shutting down
   {
-    // Guard against calls to emit the signal during the callback
-    mIsEmittingResourceReadySignal = true;
+    if(!mIsEmittingResourceReadySignal)
+    {
+      // Guard against calls to emit the signal during the callback
+      mIsEmittingResourceReadySignal = true;
 
-    // If the signal handler changes visual, it may become ready during this call & therefore this method will
-    // get called again recursively. If so, mIdleCallbackRegistered is set below, and we act on it after that secondary
-    // invocation has completed by notifying in an Idle callback to prevent further recursion.
-    Dali::Toolkit::Control handle(mControlImpl.GetOwner());
-    mResourceReadySignal.Emit(handle);
+      // If the signal handler changes visual, it may become ready during this call & therefore this method will
+      // get called again recursively. If so, mIdleCallbackRegistered is set below, and we act on it after that secondary
+      // invocation has completed by notifying in an Idle callback to prevent further recursion.
+      Dali::Toolkit::Control handle(mControlImpl.GetOwner());
+      mResourceReadySignal.Emit(handle);
 
-    mIsEmittingResourceReadySignal = false;
-  }
-  else
-  {
-    if(!mIdleCallbackRegistered)
+      mIsEmittingResourceReadySignal = false;
+    }
+    else
     {
-      mIdleCallbackRegistered = true;
-
-      // Add idler to emit the signal again
-      if(!mIdleCallback)
+      if(!mIdleCallbackRegistered)
       {
-        // The callback manager takes the ownership of the callback object.
-        mIdleCallback = MakeCallback(this, &Control::Impl::OnIdleCallback);
-        if(DALI_UNLIKELY(!Adaptor::Get().AddIdle(mIdleCallback, true)))
+        mIdleCallbackRegistered = true;
+
+        // Add idler to emit the signal again
+        if(!mIdleCallback)
         {
-          DALI_LOG_ERROR("Fail to add idle callback for control resource ready. Skip this callback.\n");
-          mIdleCallback           = nullptr;
-          mIdleCallbackRegistered = false;
+          // The callback manager takes the ownership of the callback object.
+          mIdleCallback = MakeCallback(this, &Control::Impl::OnIdleCallback);
+          if(DALI_UNLIKELY(!Adaptor::Get().AddIdle(mIdleCallback, true)))
+          {
+            DALI_LOG_ERROR("Fail to add idle callback for control resource ready. Skip this callback.\n");
+            mIdleCallback           = nullptr;
+            mIdleCallbackRegistered = false;
+          }
         }
       }
     }
@@ -2283,19 +2444,14 @@ bool Control::Impl::OnIdleCallback()
   return mIdleCallbackRegistered;
 }
 
-Toolkit::DevelControl::ControlAccessible* Control::Impl::GetAccessibleObject()
+std::shared_ptr<Toolkit::DevelControl::ControlAccessible> Control::Impl::GetAccessibleObject()
 {
-  if(mAccessibleCreatable && !mAccessibleObject)
-  {
-    mAccessibleObject.reset(mControlImpl.CreateAccessibleObject());
-  }
-
-  return mAccessibleObject.get();
+  return std::dynamic_pointer_cast<DevelControl::ControlAccessible>(Accessibility::Accessible::GetOwningPtr(mControlImpl.Self()));
 }
 
 bool Control::Impl::IsAccessibleCreated() const
 {
-  return !!mAccessibleObject;
+  return !!Accessibility::Bridge::GetCurrentBridge()->GetAccessible(mControlImpl.Self());
 }
 
 void Control::Impl::EnableCreateAccessible(bool enable)
@@ -2308,6 +2464,205 @@ bool Control::Impl::IsCreateAccessibleEnabled() const
   return mAccessibleCreatable;
 }
 
+void Control::Impl::ApplyFittingMode(const Vector2& size)
+{
+  Actor self = mControlImpl.Self();
+  for(RegisteredVisualContainer::Iterator iter = mVisuals.Begin(); iter != mVisuals.End(); iter++)
+  {
+    // Check whether the visual is empty and enabled
+    if((*iter)->visual && (*iter)->enabled)
+    {
+      Internal::Visual::Base& visualImpl = Toolkit::GetImplementation((*iter)->visual);
+
+      // If the current visual is using the transform property map, fittingMode will not be applied.
+      if(visualImpl.IsIgnoreFittingMode())
+      {
+        continue;
+      }
+
+      Visual::FittingMode fittingMode  = visualImpl.GetFittingMode();
+      Property::Map       transformMap = Property::Map();
+
+      // If the fittingMode is DONT_CARE, we don't need to apply fittingMode, just Set empty transformMap
+      if(fittingMode == Visual::FittingMode::DONT_CARE)
+      {
+        if(visualImpl.GetType() != Toolkit::Visual::Type::TEXT)
+        {
+          ((*iter)->visual).SetTransformAndSize(transformMap, size);
+        }
+        continue;
+      }
+
+      Extents padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+
+      bool zeroPadding = (padding == Extents());
+
+      Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>(
+        self.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+      if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
+      {
+        std::swap(padding.start, padding.end);
+      }
+
+      // remove padding from the size to know how much is left for the visual
+      Vector2 finalSize   = size - Vector2(padding.start + padding.end, padding.top + padding.bottom);
+      Vector2 finalOffset = Vector2(padding.start, padding.top);
+
+      // Reset PIXEL_AREA after using OVER_FIT_KEEP_ASPECT_RATIO
+      if(visualImpl.IsPixelAreaSetForFittingMode())
+      {
+        visualImpl.SetPixelAreaForFittingMode(FULL_TEXTURE_RECT);
+      }
+
+      if((!zeroPadding) || // If padding is not zero
+         (fittingMode != Visual::FittingMode::FILL))
+      {
+        visualImpl.SetTransformMapUsageForFittingMode(true);
+
+        Vector2 naturalSize;
+        // NaturalSize will not be used for FILL fitting mode, which is default.
+        // Skip GetNaturalSize
+        if(fittingMode != Visual::FittingMode::FILL)
+        {
+          ((*iter)->visual).GetNaturalSize(naturalSize);
+        }
+
+        // If FittingMode use FIT_WIDTH or FIT_HEIGTH, it need to change proper fittingMode
+        if(fittingMode == Visual::FittingMode::FIT_WIDTH || fittingMode == Visual::FittingMode::FIT_HEIGHT)
+        {
+          const float widthRatio  = !Dali::EqualsZero(naturalSize.width) ? (finalSize.width / naturalSize.width) : 0.0f;
+          const float heightRatio = !Dali::EqualsZero(naturalSize.height) ? (finalSize.height / naturalSize.height) : 0.0f;
+          if(widthRatio < heightRatio)
+          {
+            // Final size has taller form than natural size.
+            fittingMode = (fittingMode == Visual::FittingMode::FIT_WIDTH) ? Visual::FittingMode::FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO;
+          }
+          else
+          {
+            // Final size has wider form than natural size.
+            fittingMode = (fittingMode == Visual::FittingMode::FIT_WIDTH) ? Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::FIT_KEEP_ASPECT_RATIO;
+          }
+        }
+
+        // Calculate size for fittingMode
+        switch(fittingMode)
+        {
+          case Visual::FittingMode::FIT_KEEP_ASPECT_RATIO:
+          {
+            auto availableVisualSize = finalSize;
+
+            // scale to fit the padded area
+            finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0),
+                                               (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0));
+
+            // calculate final offset within the padded area
+            finalOffset += (availableVisualSize - finalSize) * .5f;
+
+            // populate the transform map
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
+            break;
+          }
+          case Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO:
+          {
+            auto availableVisualSize = finalSize;
+            finalSize                = naturalSize * std::max((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
+                                               (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
+
+            auto originalOffset = finalOffset;
+
+            if(!visualImpl.IsPixelAreaSetForFittingMode() && !Dali::EqualsZero(finalSize.width) && !Dali::EqualsZero(finalSize.height))
+            {
+              float   x           = abs((availableVisualSize.width - finalSize.width) / finalSize.width) * .5f;
+              float   y           = abs((availableVisualSize.height - finalSize.height) / finalSize.height) * .5f;
+              float   widthRatio  = 1.f - abs((availableVisualSize.width - finalSize.width) / finalSize.width);
+              float   heightRatio = 1.f - abs((availableVisualSize.height - finalSize.height) / finalSize.height);
+              Vector4 pixelArea   = Vector4(x, y, widthRatio, heightRatio);
+              visualImpl.SetPixelAreaForFittingMode(pixelArea);
+            }
+
+            // populate the transform map
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, originalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, availableVisualSize);
+            break;
+          }
+          case Visual::FittingMode::CENTER:
+          {
+            auto availableVisualSize = finalSize;
+            if(availableVisualSize.width > naturalSize.width && availableVisualSize.height > naturalSize.height)
+            {
+              finalSize = naturalSize;
+            }
+            else
+            {
+              finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
+                                                 (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
+            }
+
+            finalOffset += (availableVisualSize - finalSize) * .5f;
+
+            // populate the transform map
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
+            break;
+          }
+          case Visual::FittingMode::FILL:
+          {
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
+            break;
+          }
+          case Visual::FittingMode::FIT_WIDTH:
+          case Visual::FittingMode::FIT_HEIGHT:
+          case Visual::FittingMode::DONT_CARE:
+          {
+            // This FittingMode already converted
+            break;
+          }
+        }
+
+        // Set extra value for applying transformMap
+        transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
+                         Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
+          .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
+          .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN)
+          .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
+               Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE));
+      }
+      else if(visualImpl.IsTransformMapSetForFittingMode() && zeroPadding) // Reset offset to zero only if padding applied previously
+      {
+        visualImpl.SetTransformMapUsageForFittingMode(false);
+
+        // Reset the transform map
+        transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, Vector2::ZERO)
+          .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
+               Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE))
+          .Add(Toolkit::Visual::Transform::Property::SIZE, Vector2::ONE)
+          .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
+               Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE));
+      }
+
+      ((*iter)->visual).SetTransformAndSize(transformMap, size);
+    }
+  }
+}
+
+void Control::Impl::RegisterProcessorOnce()
+{
+  if(!mProcessorRegistered)
+  {
+    Adaptor::Get().RegisterProcessorOnce(*this, true);
+    mProcessorRegistered = true;
+  }
+}
+
+void Control::Impl::Process(bool postProcessor)
+{
+  // Call ApplyFittingMode
+  ApplyFittingMode(mSize);
+  mProcessorRegistered = false;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index ed38e3f..71c6d3c 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/accessibility.h>
+#include <dali/integration-api/processor-interface.h>
 #include <dali/public-api/object/property-notification.h>
 #include <dali/public-api/object/type-registry.h>
 #include <string>
@@ -33,6 +34,7 @@
 #include <dali-toolkit/internal/controls/tooltip/tooltip.h>
 #include <dali-toolkit/internal/visuals/visual-event-observer.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
 #include <dali-toolkit/public-api/visuals/visual-properties.h>
 #include <dali/devel-api/common/owner-container.h>
 #include <dali/integration-api/debug.h>
@@ -72,7 +74,7 @@ typedef Dali::OwnerContainer<RegisteredVisual*> RegisteredVisualContainer;
 /**
  * @brief Holds the Implementation for the internal control class
  */
-class Control::Impl : public ConnectionTracker, public Visual::EventObserver
+class Control::Impl : public ConnectionTracker, public Visual::EventObserver, public Integration::Processor
 {
   friend class Toolkit::DevelControl::ControlAccessible;
 
@@ -454,7 +456,7 @@ public:
   /**
    * @copydoc Dali::Toolkit::Internal::Control::GetAccessibleObject()
    */
-  Toolkit::DevelControl::ControlAccessible* GetAccessibleObject();
+  std::shared_ptr<Toolkit::DevelControl::ControlAccessible> GetAccessibleObject();
 
   /**
    * @copydoc Dali::Toolkit::DevelControl::IsAccessibleCreated()
@@ -471,6 +473,32 @@ public:
    */
   bool IsCreateAccessibleEnabled() const;
 
+  /**
+   * @brief Apply fittingMode
+   *
+   * @param[in] size The size of the control
+   */
+  void ApplyFittingMode(const Vector2& size);
+
+  /**
+   * @brief Register processor
+  */
+  void RegisterProcessorOnce();
+
+protected: // From processor-interface
+  /**
+   * @copydoc Dali::Integration::Processor::Process()
+   */
+  void Process(bool postProcessor) override;
+
+  /**
+   * @copydoc Dali::Integration::Processor::GetProcessorName()
+   */
+  std::string_view GetProcessorName() const override
+  {
+    return "ControlDataImpl";
+  }
+
 private:
   /**
    * Used as an alternative to boolean so that it is obvious whether a visual is enabled/disabled.
@@ -561,7 +589,6 @@ public:
   Control&            mControlImpl;
   DevelControl::State mState;
   std::string         mSubStateName;
-  Property::Map       mAccessibilityAttributes;
 
   int mLeftFocusableActorId;             ///< Actor ID of Left focusable control.
   int mRightFocusableActorId;            ///< Actor ID of Right focusable control.
@@ -573,9 +600,11 @@ public:
   RegisteredVisualContainer                 mVisuals; ///< Stores visuals needed by the control, non trivial type so std::vector used.
   std::string                               mStyleName;
   Vector4                                   mBackgroundColor;    ///< The color of the background visual
+  RenderEffect                              mRenderEffect;       ///< The render effect on this control
   Vector3*                                  mStartingPinchScale; ///< The scale when a pinch gesture starts, TODO: consider removing this
   Extents                                   mMargin;             ///< The margin values
   Extents                                   mPadding;            ///< The padding values
+  Vector2                                   mSize;               ///< The size of the control
   Toolkit::Control::KeyEventSignalType      mKeyEventSignal;
   Toolkit::Control::KeyInputFocusSignalType mKeyInputFocusGainedSignal;
   Toolkit::Control::KeyInputFocusSignalType mKeyInputFocusLostSignal;
@@ -594,19 +623,25 @@ public:
   Toolkit::DevelControl::AccessibilityGetDescriptionSignalType mAccessibilityGetDescriptionSignal;
   Toolkit::DevelControl::AccessibilityDoGestureSignalType      mAccessibilityDoGestureSignal;
 
-  std::string mAccessibilityName;
-  std::string mAccessibilityDescription;
-  std::string mAccessibilityTranslationDomain;
-  std::string mAutomationId;
-
-  bool mAccessibilityHighlightable = false;
-  bool mAccessibilityHidden        = false;
-  bool mAccessibleCreatable        = true;
+  Toolkit::DevelControl::AccessibilityActionSignalType mAccessibilityActionSignal;
 
-  Dali::Accessibility::Role mAccessibilityRole = Dali::Accessibility::Role::UNKNOWN;
-
-  std::map<Dali::Accessibility::RelationType, std::set<Accessibility::Accessible*>> mAccessibilityRelations;
-  std::unique_ptr<Toolkit::DevelControl::ControlAccessible>                         mAccessibleObject;
+  struct AccessibilityProps
+  {
+    std::string                                                                       name{};
+    std::string                                                                       description{};
+    std::string                                                                       value{};
+    std::string                                                                       automationId{};
+    int32_t                                                                           role{static_cast<int32_t>(Dali::Accessibility::Role::UNKNOWN)};
+    DevelControl::AccessibilityStates                                                 states{};
+    std::map<Dali::Accessibility::RelationType, std::set<Accessibility::Accessible*>> relations;
+    Property::Map                                                                     extraAttributes{};
+    bool                                                                              isHighlightable{false};
+    bool                                                                              isHidden{false};
+    bool                                                                              isScrollable{false};
+    bool                                                                              isModal{false};
+  } mAccessibilityProps;
+
+  bool mAccessibleCreatable = true;
 
   // Gesture Detection
   PinchGestureDetector     mPinchGestureDetector;
@@ -626,6 +661,7 @@ public:
   bool             mIsEmittingResourceReadySignal : 1;    ///< True during ResourceReady().
   bool             mIdleCallbackRegistered : 1;           ///< True if need to emit the resource ready signal again.
   bool             mDispatchKeyEvents : 1;                ///< Whether the actor emits key event signals
+  bool             mProcessorRegistered : 1;              ///< Whether the processor is registered.
 
   RegisteredVisualContainer mRemoveVisuals; ///< List of visuals that are being replaced by another visual once ready
 
@@ -656,6 +692,10 @@ public:
   static const PropertyRegistration PROPERTY_24;
   static const PropertyRegistration PROPERTY_25;
   static const PropertyRegistration PROPERTY_26;
+  static const PropertyRegistration PROPERTY_27;
+  static const PropertyRegistration PROPERTY_28;
+  static const PropertyRegistration PROPERTY_29;
+  static const PropertyRegistration PROPERTY_30;
 
 private:
   // Accessibility - notification for highlighted object to check if it is showing.
index 31b7c61..b358e8c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 // 2 modes:
 // 1st mode, this control has a tree of actors (use Add() to add children) that are rendered and blurred.
 // mRenderChildrenTask renders children to FB mRenderTargetForRenderingChildren
-// mHorizBlurTask renders mHorizBlurActor Actor showing FB mRenderTargetForRenderingChildren into FB mRenderTarget2
-// mVertBlurTask renders mVertBlurActor Actor showing FB mRenderTarget2 into FB mRenderTarget1
-// mCompositeTask renders mCompositingActor Actor showing FB mRenderTarget1 into FB mRenderTargetForRenderingChildren
+// mHorizontalBlurTask renders mHorizontalBlurActor Actor showing FB mRenderTargetForRenderingChildren into FB mRenderTarget2
+// mVerticalBlurTask renders mVerticalBlurActor Actor showing FB mRenderTarget2 into FB mRenderTarget1
+// mCompositeTask renders mCompositingActor Actor showing FB mRenderTarget1 into FB mBlurResultFrameBuffer
 //
 // 2nd mode, an image is blurred and rendered to a supplied target framebuffer
-// mHorizBlurTask renders mHorizBlurActor Actor showing mUserInputImage into FB mRenderTarget2
-// mVertBlurTask renders mVertBlurActor Actor showing mRenderTarget2 into FB mUserOutputRenderTarget
+// mHorizontalBlurTask renders mHorizontalBlurActor Actor showing mUserInputImage into FB mRenderTarget2
+// mVerticalBlurTask renders mVerticalBlurActor Actor showing mRenderTarget2 into FB mUserOutputRenderTarget
 //
 // Only this 2nd mode handles ActivateOnce
 
@@ -98,12 +98,23 @@ const float         GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE    = 0.5f
 
 const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f;
 
+constexpr uint32_t MAXIMUM_SAMPLES_SIZE = 335u;
+
+/**
+  * @brief Calculates gaussian weight
+  * @param[in] localOffset Input to the function
+  */
+inline float CalculateGaussianWeight(float localOffset, float sigma)
+{
+  return (1.0f / sqrt(2.0f * Dali::Math::PI * sigma)) * exp(-(localOffset / sigma * localOffset / sigma) * 0.5f);
+}
+
 } // namespace
 
 GaussianBlurView::GaussianBlurView()
 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
-  mNumSamples(GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES),
-  mBlurBellCurveWidth(0.001f),
+  mPixelRadius(GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES),
+  mBellCurveWidth(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH),
   mPixelFormat(GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT),
   mDownsampleWidthScale(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE),
   mDownsampleHeightScale(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE),
@@ -119,7 +130,6 @@ GaussianBlurView::GaussianBlurView()
   mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
   mActivated(false)
 {
-  SetBlurBellCurveWidth(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH);
 }
 
 GaussianBlurView::GaussianBlurView(const unsigned int  numSamples,
@@ -129,8 +139,8 @@ GaussianBlurView::GaussianBlurView(const unsigned int  numSamples,
                                    const float         downsampleHeightScale,
                                    bool                blurUserImage)
 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
-  mNumSamples(numSamples),
-  mBlurBellCurveWidth(0.001f),
+  mPixelRadius(numSamples),
+  mBellCurveWidth(blurBellCurveWidth),
   mPixelFormat(renderTargetPixelFormat),
   mDownsampleWidthScale(downsampleWidthScale),
   mDownsampleHeightScale(downsampleHeightScale),
@@ -146,7 +156,19 @@ GaussianBlurView::GaussianBlurView(const unsigned int  numSamples,
   mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
   mActivated(false)
 {
-  SetBlurBellCurveWidth(blurBellCurveWidth);
+  if(mPixelRadius == 0)
+  {
+    // We always assume that pixel radius is positive.
+    mPixelRadius = 1;
+  }
+  if(mPixelRadius > MAXIMUM_SAMPLES_SIZE)
+  {
+    float reduceFactor = static_cast<float>(MAXIMUM_SAMPLES_SIZE) / mPixelRadius;
+    mDownsampleWidthScale *= reduceFactor;
+    mDownsampleHeightScale *= reduceFactor;
+    mBellCurveWidth *= reduceFactor;
+    mPixelRadius = MAXIMUM_SAMPLES_SIZE;
+  }
 }
 
 GaussianBlurView::~GaussianBlurView()
@@ -186,7 +208,7 @@ void GaussianBlurView::SetUserImageAndOutputRenderTarget(Texture inputImage, Fra
 
   mUserInputImage = inputImage;
 
-  SetRendererTexture(mHorizBlurActor.GetRendererAt(0), inputImage);
+  SetRendererTexture(mHorizontalBlurActor.GetRendererAt(0), inputImage);
 
   mUserOutputRenderTarget = outputRenderTarget;
 }
@@ -195,7 +217,7 @@ FrameBuffer GaussianBlurView::GetBlurredRenderTarget() const
 {
   if(!mUserOutputRenderTarget)
   {
-    return mRenderTargetForRenderingChildren;
+    return mBlurResultFrameBuffer;
   }
 
   return mUserOutputRenderTarget;
@@ -226,7 +248,7 @@ void GaussianBlurView::OnInitialize()
   // Create shaders
 
   std::ostringstream fragmentStringStream;
-  fragmentStringStream << "#define NUM_SAMPLES " << mNumSamples << "\n";
+  fragmentStringStream << "#define NUM_SAMPLES " << (DALI_LIKELY(mPixelRadius > 1u) ? mPixelRadius : 2u) << "\n";
   fragmentStringStream << SHADER_GAUSSIAN_BLUR_VIEW_FRAG;
   std::string fragmentSource(fragmentStringStream.str());
 
@@ -234,17 +256,17 @@ void GaussianBlurView::OnInitialize()
   // Create actors
 
   // Create an actor for performing a horizontal blur on the texture
-  mHorizBlurActor = Actor::New();
-  mHorizBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mHorizontalBlurActor = Actor::New();
+  mHorizontalBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
 
   Renderer renderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
-  mHorizBlurActor.AddRenderer(renderer);
+  mHorizontalBlurActor.AddRenderer(renderer);
 
   // Create an actor for performing a vertical blur on the texture
-  mVertBlurActor = Actor::New();
-  mVertBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mVerticalBlurActor = Actor::New();
+  mVerticalBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
   renderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
-  mVertBlurActor.AddRenderer(renderer);
+  mVerticalBlurActor.AddRenderer(renderer);
 
   // Register a property that the user can control to fade the blur in / out via the GaussianBlurView object
   Actor self                 = Self();
@@ -291,8 +313,8 @@ void GaussianBlurView::OnInitialize()
   //////////////////////////////////////////////////////
   // Connect to actor tree
   Self().Add(mChildrenRoot);
-  mInternalRoot.Add(mHorizBlurActor);
-  mInternalRoot.Add(mVertBlurActor);
+  mInternalRoot.Add(mHorizontalBlurActor);
+  mInternalRoot.Add(mVerticalBlurActor);
   mInternalRoot.Add(mRenderDownsampledCamera);
 
   Self().SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::FILLER);
@@ -375,12 +397,12 @@ void GaussianBlurView::AllocateResources()
     mRenderFullSizeCamera.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, mTargetSize.height * cameraPosConstraintScale));
 
     // create offscreen buffer of new size to render our child actors to
-    mRenderTargetForRenderingChildren = FrameBuffer::New(mTargetSize.width, mTargetSize.height, FrameBuffer::Attachment::NONE);
-    Texture texture                   = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mTargetSize.width), unsigned(mTargetSize.height));
+    mRenderTargetForRenderingChildren = FrameBuffer::New(mDownsampledWidth, mDownsampledHeight, FrameBuffer::Attachment::NONE);
+    Texture texture                   = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mDownsampledWidth), unsigned(mDownsampledHeight));
     mRenderTargetForRenderingChildren.AttachColorTexture(texture);
 
     // Set actor for performing a horizontal blur
-    SetRendererTexture(mHorizBlurActor.GetRendererAt(0), mRenderTargetForRenderingChildren);
+    SetRendererTexture(mHorizontalBlurActor.GetRendererAt(0), mRenderTargetForRenderingChildren);
 
     // Create offscreen buffer for vert blur pass
     mRenderTarget1 = FrameBuffer::New(mDownsampledWidth, mDownsampledHeight, FrameBuffer::Attachment::NONE);
@@ -390,8 +412,13 @@ void GaussianBlurView::AllocateResources()
     // use the completed blur in the first buffer and composite with the original child actors render
     SetRendererTexture(mCompositingActor.GetRendererAt(0), mRenderTarget1);
 
+    // create offscreen buffer of new size to render composited result.
+    mBlurResultFrameBuffer = FrameBuffer::New(mTargetSize.width, mTargetSize.height, FrameBuffer::Attachment::NONE);
+    texture                = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mTargetSize.width), unsigned(mTargetSize.height));
+    mBlurResultFrameBuffer.AttachColorTexture(texture);
+
     // set up target actor for rendering result, i.e. the blurred image
-    SetRendererTexture(mTargetActor.GetRendererAt(0), mRenderTargetForRenderingChildren);
+    SetRendererTexture(mTargetActor.GetRendererAt(0), mBlurResultFrameBuffer);
   }
 
   // Create offscreen buffer for horiz blur pass
@@ -400,11 +427,11 @@ void GaussianBlurView::AllocateResources()
   mRenderTarget2.AttachColorTexture(texture);
 
   // size needs to match render target
-  mHorizBlurActor.SetProperty(Actor::Property::SIZE, Vector2(mDownsampledWidth, mDownsampledHeight));
+  mHorizontalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(mDownsampledWidth, mDownsampledHeight));
 
   // size needs to match render target
-  mVertBlurActor.SetProperty(Actor::Property::SIZE, Vector2(mDownsampledWidth, mDownsampledHeight));
-  SetRendererTexture(mVertBlurActor.GetRendererAt(0), mRenderTarget2);
+  mVerticalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(mDownsampledWidth, mDownsampledHeight));
+  SetRendererTexture(mVerticalBlurActor.GetRendererAt(0), mRenderTarget2);
 
   // set gaussian blur up for new sized render targets
   SetShaderConstants();
@@ -434,39 +461,39 @@ void GaussianBlurView::CreateRenderTasks()
   }
 
   // perform a horizontal blur targeting the second buffer
-  mHorizBlurTask = taskList.CreateTask();
-  mHorizBlurTask.SetSourceActor(mHorizBlurActor);
-  mHorizBlurTask.SetExclusive(true);
-  mHorizBlurTask.SetInputEnabled(false);
-  mHorizBlurTask.SetClearEnabled(true);
-  mHorizBlurTask.SetClearColor(mBackgroundColor);
-  mHorizBlurTask.SetCameraActor(mRenderDownsampledCamera);
-  mHorizBlurTask.SetFrameBuffer(mRenderTarget2);
+  mHorizontalBlurTask = taskList.CreateTask();
+  mHorizontalBlurTask.SetSourceActor(mHorizontalBlurActor);
+  mHorizontalBlurTask.SetExclusive(true);
+  mHorizontalBlurTask.SetInputEnabled(false);
+  mHorizontalBlurTask.SetClearEnabled(true);
+  mHorizontalBlurTask.SetClearColor(mBackgroundColor);
+  mHorizontalBlurTask.SetCameraActor(mRenderDownsampledCamera);
+  mHorizontalBlurTask.SetFrameBuffer(mRenderTarget2);
   if(mRenderOnce || (mRenderOnce && mBlurUserImage))
   {
-    mHorizBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+    mHorizontalBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
   }
 
   // use the second buffer and perform a horizontal blur targeting the first buffer
-  mVertBlurTask = taskList.CreateTask();
-  mVertBlurTask.SetSourceActor(mVertBlurActor);
-  mVertBlurTask.SetExclusive(true);
-  mVertBlurTask.SetInputEnabled(false);
-  mVertBlurTask.SetClearEnabled(true);
-  mVertBlurTask.SetClearColor(mBackgroundColor);
-  mVertBlurTask.SetCameraActor(mRenderDownsampledCamera);
+  mVerticalBlurTask = taskList.CreateTask();
+  mVerticalBlurTask.SetSourceActor(mVerticalBlurActor);
+  mVerticalBlurTask.SetExclusive(true);
+  mVerticalBlurTask.SetInputEnabled(false);
+  mVerticalBlurTask.SetClearEnabled(true);
+  mVerticalBlurTask.SetClearColor(mBackgroundColor);
+  mVerticalBlurTask.SetCameraActor(mRenderDownsampledCamera);
   if(mUserOutputRenderTarget)
   {
-    mVertBlurTask.SetFrameBuffer(mUserOutputRenderTarget);
+    mVerticalBlurTask.SetFrameBuffer(mUserOutputRenderTarget);
   }
   else
   {
-    mVertBlurTask.SetFrameBuffer(mRenderTarget1);
+    mVerticalBlurTask.SetFrameBuffer(mRenderTarget1);
   }
   if(mRenderOnce || (mRenderOnce && mBlurUserImage))
   {
-    mVertBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
-    mVertBlurTask.FinishedSignal().Connect(this, &GaussianBlurView::OnRenderTaskFinished);
+    mVerticalBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+    mVerticalBlurTask.FinishedSignal().Connect(this, &GaussianBlurView::OnRenderTaskFinished);
   }
 
   // use the completed blur in the first buffer and composite with the original child actors render
@@ -478,7 +505,7 @@ void GaussianBlurView::CreateRenderTasks()
     mCompositeTask.SetInputEnabled(false);
 
     mCompositeTask.SetCameraActor(mRenderFullSizeCamera);
-    mCompositeTask.SetFrameBuffer(mRenderTargetForRenderingChildren);
+    mCompositeTask.SetFrameBuffer(mBlurResultFrameBuffer);
 
     if(mRenderOnce)
     {
@@ -492,8 +519,8 @@ void GaussianBlurView::RemoveRenderTasks()
   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
 
   taskList.RemoveTask(mRenderChildrenTask);
-  taskList.RemoveTask(mHorizBlurTask);
-  taskList.RemoveTask(mVertBlurTask);
+  taskList.RemoveTask(mHorizontalBlurTask);
+  taskList.RemoveTask(mVerticalBlurTask);
   taskList.RemoveTask(mCompositeTask);
 }
 
@@ -524,6 +551,7 @@ void GaussianBlurView::Deactivate()
     // Note: render target resources are automatically freed since we set the Image::Unused flag
     mInternalRoot.Unparent();
     mRenderTargetForRenderingChildren.Reset();
+    mBlurResultFrameBuffer.Reset();
     mRenderTarget1.Reset();
     mRenderTarget2.Reset();
     RemoveRenderTasks();
@@ -532,77 +560,51 @@ void GaussianBlurView::Deactivate()
   }
 }
 
-void GaussianBlurView::SetBlurBellCurveWidth(float blurBellCurveWidth)
-{
-  // a value of zero leads to undefined Gaussian weights, do not allow user to do this
-  mBlurBellCurveWidth = std::max(blurBellCurveWidth, 0.001f);
-}
-
-float GaussianBlurView::CalcGaussianWeight(float x)
-{
-  return (1.0f / sqrt(2.0f * Math::PI * mBlurBellCurveWidth)) * exp(-(x * x) / (2.0f * mBlurBellCurveWidth * mBlurBellCurveWidth));
-}
-
 void GaussianBlurView::SetShaderConstants()
 {
-  Vector2*     uvOffsets;
-  float        ofs;
-  float*       weights;
-  float        w, totalWeights;
-  unsigned int i;
+  const uint32_t numSamples = DALI_LIKELY(mPixelRadius > 1u) ? mPixelRadius : 2u;
 
-  uvOffsets = new Vector2[mNumSamples + 1];
-  weights   = new float[mNumSamples + 1];
+  std::vector<float> uvOffsets(numSamples);
+  std::vector<float> weights(numSamples);
 
-  totalWeights = weights[0] = CalcGaussianWeight(0);
-  uvOffsets[0].x            = 0.0f;
-  uvOffsets[0].y            = 0.0f;
+  // generate bell curve kernel
+  unsigned int       halfSize = numSamples * 2;
+  std::vector<float> halfSideKernel(halfSize);
 
-  for(i = 0; i<mNumSamples >> 1; i++)
+  halfSideKernel[0]  = CalculateGaussianWeight(0.0f, mBellCurveWidth);
+  float totalWeights = halfSideKernel[0];
+  for(unsigned int i = 1; i < halfSize; i++)
   {
-    w                     = CalcGaussianWeight((float)(i + 1));
-    weights[(i << 1) + 1] = w;
-    weights[(i << 1) + 2] = w;
+    float w           = CalculateGaussianWeight(i, mBellCurveWidth);
+    halfSideKernel[i] = w;
     totalWeights += w * 2.0f;
-
-    // offset texture lookup to between texels, that way the bilinear filter in the texture hardware will average two samples with one lookup
-    ofs = ((float)(i << 1)) + 1.5f;
-
-    // get offsets from units of pixels into uv coordinates in [0..1]
-    float ofsX                = ofs / mDownsampledWidth;
-    float ofsY                = ofs / mDownsampledHeight;
-    uvOffsets[(i << 1) + 1].x = ofsX;
-    uvOffsets[(i << 1) + 1].y = ofsY;
-
-    uvOffsets[(i << 1) + 2].x = -ofsX;
-    uvOffsets[(i << 1) + 2].y = -ofsY;
   }
+  for(unsigned int i = 0; i < halfSize; i++)
+  {
+    halfSideKernel[i] /= totalWeights;
+  }
+  halfSideKernel[0] *= 0.5f;
 
-  for(i = 0; i < mNumSamples; i++)
+  // compress kernel
+  for(unsigned int i = 0; i < numSamples; i++)
   {
-    weights[i] /= totalWeights;
+    weights[i]   = halfSideKernel[2 * i] + halfSideKernel[2 * i + 1];
+    uvOffsets[i] = 2.0f * i + halfSideKernel[2 * i + 1] / weights[i];
   }
 
   // set shader constants
-  Vector2 xAxis(1.0f, 0.0f);
-  Vector2 yAxis(0.0f, 1.0f);
-  for(i = 0; i < mNumSamples; ++i)
+  for(unsigned int i = 0; i < numSamples; ++i)
   {
-    mHorizBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), uvOffsets[i] * xAxis);
-    mHorizBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
+    mHorizontalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(uvOffsets[i] / mDownsampledWidth, 0.0f));
+    mHorizontalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
 
-    mVertBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), uvOffsets[i] * yAxis);
-    mVertBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
+    mVerticalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(0.0f, uvOffsets[i] / mDownsampledHeight));
+    mVerticalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
   }
-
-  delete[] uvOffsets;
-  delete[] weights;
 }
 
 std::string GaussianBlurView::GetSampleOffsetsPropertyName(unsigned int index) const
 {
-  DALI_ASSERT_ALWAYS(index < mNumSamples);
-
   std::ostringstream oss;
   oss << "uSampleOffsets[" << index << "]";
   return oss.str();
@@ -610,8 +612,6 @@ std::string GaussianBlurView::GetSampleOffsetsPropertyName(unsigned int index) c
 
 std::string GaussianBlurView::GetSampleWeightsPropertyName(unsigned int index) const
 {
-  DALI_ASSERT_ALWAYS(index < mNumSamples);
-
   std::ostringstream oss;
   oss << "uSampleWeights[" << index << "]";
   return oss.str();
index 31fac62..02b88d3 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_GAUSSIAN_BLUR_EFFECT_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -100,8 +100,6 @@ private:
    */
   void OnChildRemove(Actor& child) override;
 
-  void        SetBlurBellCurveWidth(float blurBellCurveWidth);
-  float       CalcGaussianWeight(float x);
   void        SetShaderConstants();
   std::string GetSampleOffsetsPropertyName(unsigned int index) const;
   std::string GetSampleWeightsPropertyName(unsigned int index) const;
@@ -109,9 +107,9 @@ private:
   void OnRenderTaskFinished(Dali::RenderTask& renderTask);
 
   /////////////////////////////////////////////////////////////
-  unsigned int  mNumSamples;         // number of blur samples in each of horiz/vert directions
-  float         mBlurBellCurveWidth; // constant used when calculating the gaussian weights
-  Pixel::Format mPixelFormat;        // pixel format used by render targets
+  unsigned int  mPixelRadius;    // number of blur samples in each of horiz/vert directions
+  float         mBellCurveWidth; // constant used when calculating the gaussian weights
+  Pixel::Format mPixelFormat;    // pixel format used by render targets
 
   /////////////////////////////////////////////////////////////
   // downsampling is used for the separated blur passes to get increased blur with the same number of samples and also to make rendering quicker
@@ -152,17 +150,18 @@ private:
   // for rendering all user added children to offscreen target
   FrameBuffer mRenderTargetForRenderingChildren;
   RenderTask  mRenderChildrenTask;
+  FrameBuffer mBlurResultFrameBuffer;
 
   /////////////////////////////////////////////////////////////
   // for rendering separated blur passes to offscreen targets
   FrameBuffer mRenderTarget1;
   FrameBuffer mRenderTarget2;
 
-  Actor mHorizBlurActor;
-  Actor mVertBlurActor;
+  Actor mHorizontalBlurActor;
+  Actor mVerticalBlurActor;
 
-  RenderTask mHorizBlurTask;
-  RenderTask mVertBlurTask;
+  RenderTask mHorizontalBlurTask;
+  RenderTask mVerticalBlurTask;
 
   /////////////////////////////////////////////////////////////
   // for compositing blur and children renders to offscreen target
index 11fe19d..75de201 100644 (file)
@@ -223,44 +223,14 @@ void GlView::OnControlInheritedVisibilityChanged(Dali::Actor actor, bool visible
   }
 }
 
-void GlView::OnWindowVisibilityChanged(Window window, bool visible)
-{
-  if(mRenderThread)
-  {
-    if(visible && Self().GetProperty<bool>(Actor::Property::VISIBLE))
-    {
-      mRenderThread->Resume();
-    }
-    else
-    {
-      mRenderThread->Pause();
-    }
-  }
-}
-
 void GlView::OnSceneConnection(int depth)
 {
   Control::OnSceneConnection(depth);
-
-  Actor  self   = Self();
-  Window window = DevelWindow::Get(self);
-
-  if(window)
-  {
-    mPlacementWindow = window;
-    DevelWindow::VisibilityChangedSignal(window).Connect(this, &GlView::OnWindowVisibilityChanged);
-  }
 }
 
 void GlView::OnSceneDisconnection()
 {
   Control::OnSceneDisconnection();
-  Window window = mPlacementWindow.GetHandle();
-  if(window)
-  {
-    DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &GlView::OnWindowVisibilityChanged);
-    mPlacementWindow.Reset();
-  }
 }
 
 Dali::Geometry GlView::CreateTexturedQuad()
@@ -321,13 +291,15 @@ Dali::NativeImageSourceQueue::ColorFormat GlView::GetColorFormat(Dali::Toolkit::
   {
     case Toolkit::GlView::ColorFormat::RGBA8888:
     {
-      return Dali::NativeImageSourceQueue::ColorFormat::RGBA8888;
+      // TODO : Shouldn't it be RGBA8888?
+      return Dali::NativeImageSourceQueue::ColorFormat::BGRA8888;
     }
 
     case Toolkit::GlView::ColorFormat::RGB888:
     default:
     {
-      return Dali::NativeImageSourceQueue::ColorFormat::RGBX8888;
+      // TODO : Shouldn't it be RGBX8888?
+      return Dali::NativeImageSourceQueue::ColorFormat::BGRX8888;
     }
   }
 }
index 0d80925..faeafc5 100644 (file)
@@ -148,7 +148,6 @@ private:
   Dali::NativeImageSourceQueue::ColorFormat GetColorFormat(Dali::Toolkit::GlView::ColorFormat format);
 
 private:
-  WeakHandle<Window>                   mPlacementWindow;
   std::unique_ptr<GlViewRenderThread>  mRenderThread;
   Dali::NativeImageSourceQueuePtr      mNativeImageQueue;
   Dali::Toolkit::GlView::RenderingMode mRenderingMode;
index 200aebf..e124d8b 100644 (file)
@@ -19,7 +19,9 @@
 #include "image-view-impl.h"
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/scripting/scripting.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/public-api/math/math-utils.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/public-api/object/type-registry.h>
@@ -44,8 +46,6 @@ namespace Internal
 {
 namespace
 {
-const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
-
 constexpr float FULL_OPACITY            = 1.0f;
 constexpr float LOW_OPACITY             = 0.2f;
 constexpr float TRANSITION_EFFECT_SPEED = 0.3f;
@@ -69,6 +69,20 @@ DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "transitionEffectOption", MAP, TR
 DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, ImageView, "pixelArea", Vector4(0.f, 0.f, 1.f, 1.f), PIXEL_AREA)
 DALI_TYPE_REGISTRATION_END()
 
+/**
+ * @brief Discard the given visual into VisualFactory. The visual will be destroyed at next idle time.
+ *
+ * @param[in,out] visual Visual to be discarded. It will be reset to an empty handle.
+ */
+void DiscardImageViewVisual(Dali::Toolkit::Visual::Base& visual)
+{
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && visual))
+  {
+    Dali::Toolkit::VisualFactory::Get().DiscardVisual(visual);
+  }
+  visual.Reset();
+}
+
 } // anonymous namespace
 
 using namespace Dali;
@@ -77,16 +91,16 @@ ImageView::ImageView(ControlBehaviour additionalBehaviour)
 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT | additionalBehaviour)),
   mImageSize(),
   mTransitionTargetAlpha(FULL_OPACITY),
-  mImageVisualPaddingSetByTransform(false),
-  mImageViewPixelAreaSetByFittingMode(false),
   mTransitionEffect(false),
-  mNeedLazyFittingMode(false),
   mImageReplaced(false)
 {
 }
 
 ImageView::~ImageView()
 {
+  DiscardImageViewVisual(mVisual);
+  DiscardImageViewVisual(mPreviousVisual);
+  DiscardImageViewVisual(mPlaceholderVisual);
 }
 
 Toolkit::ImageView ImageView::New(ControlBehaviour additionalBehaviour)
@@ -135,6 +149,8 @@ void ImageView::SetImage(const Property::Map& map)
     // This previous visual will be deleted when transition effect is done.
     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
     controlDataImpl.EnableReadyTransitionOverriden(mVisual, true);
+
+    DiscardImageViewVisual(mPreviousVisual);
     mPreviousVisual = mVisual;
   }
 
@@ -163,6 +179,12 @@ void ImageView::SetImage(const Property::Map& map)
   Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(mPropertyMap);
   if(visual)
   {
+    Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
+    if(visualImpl.GetFittingMode() == Visual::FittingMode::DONT_CARE)
+    {
+      visualImpl.SetFittingMode(Visual::FittingMode::FILL);
+    }
+
     // Don't set mVisual until it is ready and shown. Getters will still use current visual.
     if(!mVisual)
     {
@@ -171,7 +193,6 @@ void ImageView::SetImage(const Property::Map& map)
 
     if(!mShaderMap.Empty())
     {
-      Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
       visualImpl.SetCustomShader(mShaderMap);
     }
 
@@ -209,6 +230,8 @@ void ImageView::SetImage(const std::string& url, ImageDimensions size)
     // This previous visual will be deleted when transition effect is done.
     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
     controlDataImpl.EnableReadyTransitionOverriden(mVisual, true);
+
+    DiscardImageViewVisual(mPreviousVisual);
     mPreviousVisual = mVisual;
   }
 
@@ -228,6 +251,13 @@ void ImageView::SetImage(const std::string& url, ImageDimensions size)
   Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(url, size);
   if(visual)
   {
+    Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
+    if(visualImpl.GetFittingMode() == Visual::FittingMode::DONT_CARE)
+    {
+      visualImpl.SetFittingMode(Visual::FittingMode::FILL);
+    }
+
+    // Don't set mVisual until it is ready and shown. Getters will still use current visual.
     if(!mVisual)
     {
       mVisual = visual;
@@ -235,7 +265,6 @@ void ImageView::SetImage(const std::string& url, ImageDimensions size)
 
     if(!mShaderMap.Empty())
     {
-      Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
       visualImpl.SetCustomShader(mShaderMap);
     }
 
@@ -257,7 +286,8 @@ void ImageView::ClearImageVisual()
   // Clear cached properties
   mPropertyMap.Clear();
   mUrl.clear();
-  mVisual.Reset();
+
+  DiscardImageViewVisual(mVisual);
 
   // Unregister the exsiting visual
   DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::IMAGE);
@@ -296,7 +326,7 @@ void ImageView::SetPlaceholderUrl(const std::string& url)
   mPlaceholderUrl = url;
   if(!url.empty())
   {
-    mPlaceholderVisual.Reset();
+    DiscardImageViewVisual(mPlaceholderVisual);
     CreatePlaceholderImage();
   }
   else
@@ -308,7 +338,7 @@ void ImageView::SetPlaceholderUrl(const std::string& url)
       DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
     }
 
-    mPlaceholderVisual.Reset();
+    DiscardImageViewVisual(mPlaceholderVisual);
     mPlaceholderUrl = url;
   }
 }
@@ -387,16 +417,6 @@ void ImageView::OnRelayout(const Vector2& size, RelayoutContainer& container)
   Control::OnRelayout(size, container);
   if(mVisual)
   {
-    // If Resource is not ready, fittingMode is not working well.
-    // in this case, imageview set the flag for working applyFittingMode again when the resource is ready
-    if(!IsResourceReady())
-    {
-      mNeedLazyFittingMode = true;
-    }
-
-    // Apply FittingMode using actor's size
-    ApplyFittingMode(size);
-
     // mVisual is not updated util the resource is ready in the case of visual replacement.
     // in this case, the Property Map must be initialized so that the previous value is not reused.
     // after mVisual is updated, the correct value will be reset.
@@ -405,12 +425,6 @@ void ImageView::OnRelayout(const Vector2& size, RelayoutContainer& container)
     {
       visual.SetTransformAndSize(Property::Map(), size);
     }
-
-    if(!mTransitionEffect)
-    {
-      // we don't need placeholder anymore because visual is replaced. so hide placeholder.
-      HidePlaceholderImage();
-    }
   }
 }
 
@@ -469,170 +483,21 @@ void ImageView::OnResourceReady(Toolkit::Control control)
   }
 
   // Visual ready so update visual attached to this ImageView, following call to RelayoutRequest will use this visual.
-  mVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE);
-
-  // Applying FittingMode again if it is not working well on OnRelayout().
-  if(mNeedLazyFittingMode)
+  auto currentVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE);
+  if(mVisual != currentVisual)
   {
-    const Vector2& size = Self().GetProperty(Dali::Actor::Property::SIZE).Get<Vector2>();
-    ApplyFittingMode(size);
-    mNeedLazyFittingMode = false;
+    // If the current visual is not the same as the previous holded visual, then we need to discard old one.
+    DiscardImageViewVisual(mVisual);
   }
 
-  // Signal that a Relayout may be needed
-}
-
-void ImageView::SetTransformMapForFittingMode(Vector2 finalSize, Vector2 naturalSize, Vector2 finalOffset, Visual::FittingMode fittingMode, Property::Map& transformMap)
-{
-  switch(fittingMode)
+  if(!mTransitionEffect)
   {
-    case Visual::FittingMode::FIT_KEEP_ASPECT_RATIO:
-    {
-      auto availableVisualSize = finalSize;
-
-      // scale to fit the padded area
-      finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0),
-                                         (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0));
-
-      // calculate final offset within the padded area
-      finalOffset += (availableVisualSize - finalSize) * .5f;
-
-      // populate the transform map
-      transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
-        .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
-      break;
-    }
-    case Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO:
-    {
-      mImageViewPixelAreaSetByFittingMode = true;
-      auto availableVisualSize            = finalSize;
-      finalSize                           = naturalSize * std::max((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
-                                         (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
-
-      auto originalOffset = finalOffset;
-      finalOffset += (availableVisualSize - finalSize) * .5f;
-
-      float   x           = abs((availableVisualSize.width - finalSize.width) / finalSize.width) * .5f;
-      float   y           = abs((availableVisualSize.height - finalSize.height) / finalSize.height) * .5f;
-      float   widthRatio  = 1.f - abs((availableVisualSize.width - finalSize.width) / finalSize.width);
-      float   heightRatio = 1.f - abs((availableVisualSize.height - finalSize.height) / finalSize.height);
-      Vector4 pixelArea   = Vector4(x, y, widthRatio, heightRatio);
-      Self().SetProperty(Toolkit::ImageView::Property::PIXEL_AREA, pixelArea);
-
-      // populate the transform map
-      transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, originalOffset)
-        .Add(Toolkit::Visual::Transform::Property::SIZE, availableVisualSize);
-      break;
-    }
-    case Visual::FittingMode::CENTER:
-    {
-      auto availableVisualSize = finalSize;
-      if(availableVisualSize.width > naturalSize.width && availableVisualSize.height > naturalSize.height)
-      {
-        finalSize = naturalSize;
-      }
-      else
-      {
-        finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
-                                           (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
-      }
-
-      finalOffset += (availableVisualSize - finalSize) * .5f;
-
-      // populate the transform map
-      transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
-        .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
-      break;
-    }
-    case Visual::FittingMode::FILL:
-    {
-      transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
-        .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
-      break;
-    }
-    case Visual::FittingMode::FIT_WIDTH:
-    case Visual::FittingMode::FIT_HEIGHT:
-    {
-      // This FittingMode already converted
-      break;
-    }
+    // we don't need placeholder anymore because visual is replaced. so hide placeholder.
+    HidePlaceholderImage();
   }
-}
-
-void ImageView::ApplyFittingMode(const Vector2& size)
-{
-  Property::Map transformMap = Property::Map();
-
-  Extents padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
-
-  bool zeroPadding = (padding == Extents());
-
-  Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>(
-    Self().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
-  if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
-  {
-    std::swap(padding.start, padding.end);
-  }
-
-  // remove padding from the size to know how much is left for the visual
-  Vector2 finalSize   = size - Vector2(padding.start + padding.end, padding.top + padding.bottom);
-  Vector2 finalOffset = Vector2(padding.start, padding.top);
-
-  Visual::FittingMode fittingMode = Toolkit::GetImplementation(mVisual).GetFittingMode();
+  mVisual = currentVisual;
 
-  // Reset PIXEL_AREA after using OVER_FIT_KEEP_ASPECT_RATIO
-  if(mImageViewPixelAreaSetByFittingMode)
-  {
-    Self().SetProperty(Toolkit::ImageView::Property::PIXEL_AREA, FULL_TEXTURE_RECT);
-    mImageViewPixelAreaSetByFittingMode = false;
-  }
-
-  if((!zeroPadding) || // If padding is not zero
-     (fittingMode != Visual::FittingMode::FILL))
-  {
-    mImageVisualPaddingSetByTransform = true;
-
-    Vector2 naturalSize;
-    // NaturalSize will not be used for FILL fitting mode, which is default.
-    // Skip GetNaturalSize
-    if(fittingMode != Visual::FittingMode::FILL)
-    {
-      mVisual.GetNaturalSize(naturalSize);
-    }
-
-    // If FittingMode use FIT_WIDTH or FIT_HEIGTH, it need to change proper fittingMode
-    if(fittingMode == Visual::FittingMode::FIT_WIDTH)
-    {
-      fittingMode = (finalSize.height / naturalSize.height) < (finalSize.width / naturalSize.width) ? Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::FIT_KEEP_ASPECT_RATIO;
-    }
-    else if(fittingMode == Visual::FittingMode::FIT_HEIGHT)
-    {
-      fittingMode = (finalSize.height / naturalSize.height) < (finalSize.width / naturalSize.width) ? Visual::FittingMode::FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO;
-    }
-
-    SetTransformMapForFittingMode(finalSize, naturalSize, finalOffset, fittingMode, transformMap);
-
-    // Set extra value for applying transformMap
-    transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
-                     Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
-      .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
-      .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN)
-      .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
-           Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE));
-  }
-  else if(mImageVisualPaddingSetByTransform && zeroPadding) // Reset offset to zero only if padding applied previously
-  {
-    mImageVisualPaddingSetByTransform = false;
-    // Reset the transform map
-    transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, Vector2::ZERO)
-      .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
-           Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE))
-      .Add(Toolkit::Visual::Transform::Property::SIZE, Vector2::ONE)
-      .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
-           Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE));
-  }
-
-  mVisual.SetTransformAndSize(transformMap, size);
+  // Signal that a Relayout may be needed
 }
 
 void ImageView::CreatePlaceholderImage()
@@ -652,7 +517,7 @@ void ImageView::CreatePlaceholderImage()
   else
   {
     DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
-    mPlaceholderVisual.Reset();
+    DiscardImageViewVisual(mPlaceholderVisual);
   }
 }
 
@@ -709,9 +574,9 @@ void ImageView::TransitionImageWithEffect()
       if(!mTransitionEffectOptionMap.Empty())
       {
         // Set user's transition effect options
-        Dali::Toolkit::TransitionData transition = Toolkit::TransitionData::New(mTransitionEffectOptionMap);
-        Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
-        mTransitionAnimation  = controlDataImpl.CreateTransition(transition);
+        Dali::Toolkit::TransitionData transition      = Toolkit::TransitionData::New(mTransitionEffectOptionMap);
+        Internal::Control::Impl&      controlDataImpl = Internal::Control::Impl::Get(*this);
+        mTransitionAnimation                          = controlDataImpl.CreateTransition(transition);
         if(mTransitionAnimation)
         {
           mTransitionAnimation.SetEndAction(Animation::EndAction::DISCARD);
@@ -736,7 +601,7 @@ void ImageView::TransitionImageWithEffect()
           Dali::KeyFrames fadeinKeyFrames = Dali::KeyFrames::New();
           fadeinKeyFrames.Add(0.0f, LOW_OPACITY);
           fadeinKeyFrames.Add(1.0f, destinationAlpha);
-          mTransitionAnimation.AnimateBetween(DevelControl::GetVisualProperty(handle, Toolkit::ImageView::Property::IMAGE, Toolkit::Visual::Property::OPACITY), fadeinKeyFrames,  AlphaFunction::EASE_IN_OUT);
+          mTransitionAnimation.AnimateBetween(DevelControl::GetVisualProperty(handle, Toolkit::ImageView::Property::IMAGE, Toolkit::Visual::Property::OPACITY), fadeinKeyFrames, AlphaFunction::EASE_IN_OUT);
           imageVisual.SetDepthIndex(imageVisual.GetDepthIndex() + CURRENT_VISUAL_DEPTH_INDEX);
         }
 
@@ -757,7 +622,7 @@ void ImageView::ClearTransitionAnimation()
     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
     controlDataImpl.EnableReadyTransitionOverriden(mVisual, false);
     Toolkit::GetImplementation(mPreviousVisual).SetOffScene(self);
-    mPreviousVisual.Reset();
+    DiscardImageViewVisual(mPreviousVisual);
   }
 
   if(mTransitionAnimation)
index a4cf2e7..8deb323 100644 (file)
@@ -194,21 +194,6 @@ private:
    */
   void OnResourceReady(Toolkit::Control control);
 
-  /**
-   * @brief Set TransformMap for fittingMode
-   * param[in] finalSize The size for fittingMode
-   * param[in] textureSize The size of texture
-   * param[in] offset The offset for fittingMode
-   * param[in] fittingMode The mode for fitting image
-   * param[in] transformMap  The map for fitting image
-   */
-  void SetTransformMapForFittingMode(Vector2 finalSize, Vector2 textureSize, Vector2 offset, Visual::FittingMode fittingMode, Property::Map& transformMap);
-
-  /**
-   * @brief Apply fittingMode
-   */
-  void ApplyFittingMode(const Vector2& size);
-
    /**
    * @brief Create placeholder image if it set. placeholder image is shown when image view is waiting for the image to load.
    */
@@ -253,10 +238,7 @@ private:
 
   Animation       mTransitionAnimation;                    ///< the animation for transition effect
   float           mTransitionTargetAlpha;                  ///< Keep image's alpha value
-  bool            mImageVisualPaddingSetByTransform : 1;   ///< Flag to indicate Padding was set using a transform.
-  bool            mImageViewPixelAreaSetByFittingMode : 1; ///< Flag to indicate pixel area was set by fitting Mode
   bool            mTransitionEffect :1;                    ///< Flag to indicate TransitionEffect is enabled
-  bool            mNeedLazyFittingMode:1;                  ///< Flag to indicate FittingMode will be applying lazy
   bool            mImageReplaced:1;                        ///< Flag to indicate image is replaced
 };
 
index 9c9a1f2..16c9ea2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -343,7 +343,7 @@ void Popup::OnInitialize()
 
   SetupTouch();
 
-  self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::DIALOG);
+  self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, DevelControl::AccessibilityRole::DIALOG);
 }
 
 DevelControl::ControlAccessible* Popup::CreateAccessibleObject()
@@ -728,15 +728,13 @@ void Popup::SetDisplayState(Toolkit::Popup::DisplayState displayState)
   }
 
   // Convert the bool state to the actual display state to use.
-  mDisplayState    = display ? Toolkit::Popup::SHOWING : Toolkit::Popup::HIDING;
-  auto* accessible = Dali::Accessibility::Accessible::Get(Self());
+  mDisplayState = display ? Toolkit::Popup::SHOWING : Toolkit::Popup::HIDING;
 
   if(display)
   {
     // Update the state to indicate the current intent.
     mDisplayState = Toolkit::Popup::SHOWING;
-    Dali::Accessibility::Bridge::GetCurrentBridge()->RegisterDefaultLabel(accessible);
-    accessible->EmitShowing(true);
+    DevelControl::EmitAccessibilityStateChanged(Self(), Accessibility::State::SHOWING, 1);
 
     // We want the popup to have key input focus when it is displayed
     SetKeyInputFocus();
@@ -790,9 +788,8 @@ void Popup::SetDisplayState(Toolkit::Popup::DisplayState displayState)
   else // Not visible.
   {
     mDisplayState = Toolkit::Popup::HIDING;
-    Dali::Accessibility::Bridge::GetCurrentBridge()->UnregisterDefaultLabel(accessible);
     ClearKeyInputFocus();
-    accessible->EmitShowing(false);
+    DevelControl::EmitAccessibilityStateChanged(Self(), Accessibility::State::SHOWING, 0);
     // Restore the keyboard focus when popup is hidden.
     if(mPreviousFocusedActor && mPreviousFocusedActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))
     {
@@ -2024,7 +2021,6 @@ Dali::Accessibility::States Popup::PopupAccessible::CalculateStates()
   auto displayState = popup.GetProperty<std::string>(Toolkit::Popup::Property::DISPLAY_STATE);
 
   states[Dali::Accessibility::State::SHOWING] = (displayState == "SHOWN" || displayState == "SHOWING");
-  states[Dali::Accessibility::State::MODAL]   = true;
 
   return states;
 }
diff --git a/dali-toolkit/internal/controls/render-effects/blur-effect-impl.cpp b/dali-toolkit/internal/controls/render-effects/blur-effect-impl.cpp
new file mode 100644 (file)
index 0000000..987c582
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/render-effects/blur-effect-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/render-tasks/render-task-list.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <dali/public-api/rendering/shader.h>
+
+//INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/internal/controls/control/control-renderers.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+
+namespace
+{
+// Default values
+static constexpr float    BLUR_EFFECT_DOWNSCALE_FACTOR = 0.4f;
+static constexpr uint32_t BLUR_EFFECT_PIXEL_RADIUS     = 10u;
+static constexpr int32_t  BLUR_EFFECT_ORDER_INDEX      = 101;
+
+static constexpr float MINIMUM_DOWNSCALE_FACTOR = 0.1f;
+static constexpr float MAXIMUM_DOWNSCALE_FACTOR = 1.0f;
+
+static constexpr uint32_t MINIMUM_GPU_ARRAY_SIZE = 2u;   // GPU cannot handle array size smaller than 2.
+static constexpr uint32_t MAXIMUM_BLUR_RADIUS    = 500u; ///< Maximum pixel radius for blur effect. (GL_MAX_FRAGMENT_UNIFORM_COMPONENTS(Usually 1024) - 19 (vertex shader used)) / 3 float
+
+static constexpr float   MAXIMUM_BELL_CURVE_WIDTH            = 171.352f; ///< bell curve width for MAXIMUM_BLUR_RADIUS case
+static constexpr int32_t MAXIMUM_BELL_CURVE_LOOP_TRIAL_COUNT = 50;
+
+/**
+  * @brief Calculates gaussian weight
+  * @param[in] localOffset Input variable of gaussian distribution
+  * @param[in] sigma Standard deviation of gaussian distribution, the width of the "bell"
+  * @note Expected value of this gaussian distribution is 0.
+  */
+inline float CalculateGaussianWeight(float localOffset, float sigma)
+{
+  return (1.0f / (sigma * sqrt(2.0f * Dali::Math::PI))) * exp(-0.5f * (localOffset / sigma * localOffset / sigma));
+}
+} // namespace
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+#ifdef DEBUG_ENABLED
+extern Debug::Filter* gRenderEffectLogFilter; ///< Define at render-effect-impl.cpp
+#endif
+
+BlurEffectImpl::BlurEffectImpl(bool isBackground)
+: RenderEffectImpl(),
+  mInternalRoot(Actor::New()),
+  mDownscaleFactor(BLUR_EFFECT_DOWNSCALE_FACTOR),
+  mPixelRadius(BLUR_EFFECT_PIXEL_RADIUS),
+  mBellCurveWidth(Math::MACHINE_EPSILON_1),
+  mSkipBlur(false),
+  mIsBackground(isBackground)
+{
+}
+
+BlurEffectImpl::BlurEffectImpl(float downscaleFactor, uint32_t blurRadius, bool isBackground)
+: RenderEffectImpl(),
+  mInternalRoot(Actor::New()),
+  mDownscaleFactor(downscaleFactor),
+  mPixelRadius(blurRadius),
+  mBellCurveWidth(Math::MACHINE_EPSILON_1),
+  mSkipBlur(false),
+  mIsBackground(isBackground)
+{
+  if(DALI_UNLIKELY(mDownscaleFactor < MINIMUM_DOWNSCALE_FACTOR || mDownscaleFactor > MAXIMUM_DOWNSCALE_FACTOR))
+  {
+    mDownscaleFactor = Dali::Clamp(mDownscaleFactor, MINIMUM_DOWNSCALE_FACTOR, MAXIMUM_DOWNSCALE_FACTOR);
+  }
+
+  if(DALI_UNLIKELY(blurRadius > MAXIMUM_BLUR_RADIUS))
+  {
+    const uint32_t fixedBlurRadius      = MAXIMUM_BLUR_RADIUS;
+    const float    fixedDownScaleFactor = Dali::Clamp(
+      mDownscaleFactor * static_cast<float>(fixedBlurRadius) / static_cast<float>(blurRadius),
+      MINIMUM_DOWNSCALE_FACTOR,
+      MAXIMUM_DOWNSCALE_FACTOR);
+
+    DALI_LOG_ERROR("Blur radius is out of bound: %u. Use %u and make downscale factor %f to %f.\n",
+                   blurRadius,
+                   fixedBlurRadius,
+                   mDownscaleFactor,
+                   fixedDownScaleFactor);
+
+    mDownscaleFactor = fixedDownScaleFactor;
+    mPixelRadius     = fixedBlurRadius;
+  }
+
+  mPixelRadius = static_cast<uint32_t>(mPixelRadius * mDownscaleFactor);
+
+  if(DALI_UNLIKELY((mPixelRadius >> 1) < MINIMUM_GPU_ARRAY_SIZE))
+  {
+    mSkipBlur = true;
+    DALI_LOG_ERROR("Blur radius is too small. This blur will be ignored.\n");
+  }
+}
+
+BlurEffectImpl::~BlurEffectImpl()
+{
+}
+
+BlurEffectImplPtr BlurEffectImpl::New(bool isBackground)
+{
+  BlurEffectImplPtr handle = new BlurEffectImpl(isBackground);
+  handle->Initialize();
+  return handle;
+}
+
+BlurEffectImplPtr BlurEffectImpl::New(float downscaleFactor, uint32_t blurRadius, bool isBackground)
+{
+  BlurEffectImplPtr handle = new BlurEffectImpl(downscaleFactor, blurRadius, isBackground);
+  handle->Initialize();
+  return handle;
+}
+
+void BlurEffectImpl::OnInitialize()
+{
+  if(DALI_UNLIKELY(mSkipBlur))
+  {
+    return;
+  }
+
+  // Create CameraActors
+  {
+    mRenderFullSizeCamera = CameraActor::New();
+    mRenderFullSizeCamera.SetInvertYAxis(true);
+    mRenderFullSizeCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    mRenderFullSizeCamera.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+    mRenderFullSizeCamera.SetType(Dali::Camera::FREE_LOOK);
+    mInternalRoot.Add(mRenderFullSizeCamera);
+
+    mRenderDownsampledCamera = CameraActor::New();
+    mRenderDownsampledCamera.SetInvertYAxis(true);
+    mRenderDownsampledCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    mRenderDownsampledCamera.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+    mRenderDownsampledCamera.SetType(Dali::Camera::FREE_LOOK);
+    mInternalRoot.Add(mRenderDownsampledCamera);
+  }
+
+  // Calculate bell curve width
+  {
+    const float epsilon     = 1e-2f / (mPixelRadius * 2);
+    const float localOffset = (mPixelRadius * 2) - 1;
+
+    float lowerBoundBellCurveWidth = Math::MACHINE_EPSILON_10000;
+    float upperBoundBellCurveWidth = MAXIMUM_BELL_CURVE_WIDTH;
+
+    int trialCount = 0;
+    while(trialCount++ < MAXIMUM_BELL_CURVE_LOOP_TRIAL_COUNT && upperBoundBellCurveWidth - lowerBoundBellCurveWidth > Math::MACHINE_EPSILON_10000)
+    {
+      mBellCurveWidth = (lowerBoundBellCurveWidth + upperBoundBellCurveWidth) * 0.5f;
+      if(CalculateGaussianWeight(localOffset, mBellCurveWidth) < epsilon)
+      {
+        lowerBoundBellCurveWidth = mBellCurveWidth;
+      }
+      else
+      {
+        upperBoundBellCurveWidth = mBellCurveWidth;
+      }
+    }
+  }
+
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Verbose, "[BlurEffect:%p] mBellCurveWidth calculated! [mPixelRadius:%u][mBellCurveWidth:%f]\n", this, mPixelRadius, mBellCurveWidth);
+
+  // Create blur actors
+  {
+    mInternalRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+    // shader
+    std::ostringstream fragmentStringStream;
+    fragmentStringStream << "#define NUM_SAMPLES " << (mPixelRadius >> 1) << "\n";
+    fragmentStringStream << SHADER_BLUR_EFFECT_FRAG;
+    std::string fragmentSource(fragmentStringStream.str());
+
+    // Create an actor for performing a horizontal blur on the texture
+    mHorizontalBlurActor = Actor::New();
+    mHorizontalBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    Renderer horizontalBlurRenderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
+    horizontalBlurRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use pre-multiply alpha
+    mHorizontalBlurActor.AddRenderer(horizontalBlurRenderer);
+    mInternalRoot.Add(mHorizontalBlurActor);
+
+    // Create an actor for performing a vertical blur on the texture
+    mVerticalBlurActor = Actor::New();
+    mVerticalBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    Renderer verticalBlurRenderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
+    verticalBlurRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use pre-multiply alpha
+    mVerticalBlurActor.AddRenderer(verticalBlurRenderer);
+    mInternalRoot.Add(mVerticalBlurActor);
+  }
+}
+
+void BlurEffectImpl::OnActivate()
+{
+  if(DALI_UNLIKELY(mSkipBlur))
+  {
+    return;
+  }
+
+  Toolkit::Control ownerControl = GetOwnerControl();
+  DALI_ASSERT_ALWAYS(ownerControl && "Set the owner of RenderEffect before you activate.");
+
+  // Get size
+  Vector2 size = GetTargetSizeForValidTexture();
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[BlurEffect:%p] OnActivated! [ID:%d][size:%fx%f]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1, size.x, size.y);
+
+  if(size == Vector2::ZERO)
+  {
+    return;
+  }
+  uint32_t downsampledWidth  = std::max(static_cast<uint32_t>(size.width * mDownscaleFactor), 1u);
+  uint32_t downsampledHeight = std::max(static_cast<uint32_t>(size.height * mDownscaleFactor), 1u);
+
+  // Set size
+  mRenderFullSizeCamera.SetPerspectiveProjection(size);
+  mRenderDownsampledCamera.SetPerspectiveProjection(Size(downsampledWidth, downsampledHeight));
+
+  mHorizontalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
+  mVerticalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
+
+  // Keep sceneHolder as week handle.
+  Integration::SceneHolder sceneHolder = Integration::SceneHolder::Get(ownerControl);
+  if(DALI_UNLIKELY(!sceneHolder))
+  {
+    DALI_LOG_ERROR("BlurEffect Could not be activated due to ownerControl's SceneHolder is not exist\n");
+    return;
+  }
+  mPlacementSceneHolder = sceneHolder;
+
+  // Set blur
+  CreateFrameBuffers(size, ImageDimensions(downsampledWidth, downsampledHeight));
+  CreateRenderTasks(sceneHolder, ownerControl);
+  SetShaderConstants(downsampledWidth, downsampledHeight);
+
+  // Inject blurred output to control
+  Renderer renderer = GetTargetRenderer();
+  if(mIsBackground)
+  {
+    renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Dali::Toolkit::DepthIndex::BACKGROUND - 3);
+  }
+  else
+  {
+    renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Dali::Toolkit::DepthIndex::CONTENT);
+  }
+  ownerControl.AddRenderer(renderer);
+  SetRendererTexture(renderer, mSourceFrameBuffer);
+
+  ownerControl.Add(mInternalRoot);
+}
+
+void BlurEffectImpl::OnDeactivate()
+{
+  if(DALI_UNLIKELY(mSkipBlur))
+  {
+    return;
+  }
+
+  auto ownerControl = GetOwnerControl();
+  if(DALI_LIKELY(ownerControl))
+  {
+    Renderer renderer = GetTargetRenderer();
+    ownerControl.RemoveRenderer(renderer);
+  }
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[BlurEffect:%p] OnDeactivated! [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
+
+  mInternalRoot.Unparent();
+
+  mInputBackgroundFrameBuffer.Reset();
+  mTemporaryFrameBuffer.Reset();
+  mSourceFrameBuffer.Reset();
+
+  auto sceneHolder = mPlacementSceneHolder.GetHandle();
+  if(DALI_LIKELY(sceneHolder))
+  {
+    RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+    taskList.RemoveTask(mHorizontalBlurTask);
+    taskList.RemoveTask(mVerticalBlurTask);
+    taskList.RemoveTask(mSourceRenderTask);
+    mPlacementSceneHolder.Reset();
+  }
+
+  mHorizontalBlurTask.Reset();
+  mVerticalBlurTask.Reset();
+  mSourceRenderTask.Reset();
+}
+
+void BlurEffectImpl::CreateFrameBuffers(const Vector2 size, const ImageDimensions downsampledSize)
+{
+  uint32_t downsampledWidth  = downsampledSize.GetWidth();
+  uint32_t downsampledHeight = downsampledSize.GetHeight();
+
+  // buffer to draw input texture
+  mInputBackgroundFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
+  Texture inputBackgroundTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
+  mInputBackgroundFrameBuffer.AttachColorTexture(inputBackgroundTexture);
+
+  // buffer to draw half-blurred output
+  mTemporaryFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
+  Texture temporaryTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
+  mTemporaryFrameBuffer.AttachColorTexture(temporaryTexture);
+
+  // buffer to draw blurred output
+  mSourceFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
+  Texture sourceTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
+  mSourceFrameBuffer.AttachColorTexture(sourceTexture);
+}
+
+void BlurEffectImpl::CreateRenderTasks(Integration::SceneHolder sceneHolder, const Toolkit::Control sourceControl)
+{
+  RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+
+  // draw input texture
+  mSourceRenderTask = taskList.CreateTask();
+  if(mIsBackground)
+  {
+    mSourceRenderTask.SetSourceActor(sceneHolder.GetRootLayer());
+    mSourceRenderTask.RenderUntil(sourceControl);
+  }
+  else
+  {
+    mSourceRenderTask.SetSourceActor(sourceControl);
+  }
+  mSourceRenderTask.SetOrderIndex(BLUR_EFFECT_ORDER_INDEX);
+  mSourceRenderTask.SetCameraActor(mRenderFullSizeCamera);
+  mSourceRenderTask.SetFrameBuffer(mInputBackgroundFrameBuffer);
+  mSourceRenderTask.SetInputEnabled(false);
+  mSourceRenderTask.SetExclusive(false);
+
+  // Clear inputBackgroundTexture as scene holder background.
+  mSourceRenderTask.SetClearEnabled(true);
+  mSourceRenderTask.SetClearColor(sceneHolder.GetBackgroundColor());
+
+  // draw half-blurred output
+  SetRendererTexture(mHorizontalBlurActor.GetRendererAt(0), mInputBackgroundFrameBuffer);
+  mHorizontalBlurTask = taskList.CreateTask();
+  mHorizontalBlurTask.SetSourceActor(mHorizontalBlurActor);
+  mHorizontalBlurTask.SetOrderIndex(BLUR_EFFECT_ORDER_INDEX + 1);
+  mHorizontalBlurTask.SetExclusive(true);
+  mHorizontalBlurTask.SetInputEnabled(false);
+  mHorizontalBlurTask.SetCameraActor(mRenderDownsampledCamera);
+  mHorizontalBlurTask.SetFrameBuffer(mTemporaryFrameBuffer);
+
+  // Clear temporaryTexture as Transparent.
+  mHorizontalBlurTask.SetClearEnabled(true);
+  mHorizontalBlurTask.SetClearColor(Color::TRANSPARENT);
+
+  // draw blurred output
+  SetRendererTexture(mVerticalBlurActor.GetRendererAt(0), mTemporaryFrameBuffer);
+  mVerticalBlurTask = taskList.CreateTask();
+  mVerticalBlurTask.SetSourceActor(mVerticalBlurActor);
+  mVerticalBlurTask.SetOrderIndex(BLUR_EFFECT_ORDER_INDEX + 2);
+  mVerticalBlurTask.SetExclusive(true);
+  mVerticalBlurTask.SetInputEnabled(false);
+  mVerticalBlurTask.SetCameraActor(mRenderDownsampledCamera);
+  mVerticalBlurTask.SetFrameBuffer(mSourceFrameBuffer);
+
+  // Clear sourceTexture as Transparent.
+  mVerticalBlurTask.SetClearEnabled(true);
+  mVerticalBlurTask.SetClearColor(Color::TRANSPARENT);
+}
+
+Vector2 BlurEffectImpl::GetTargetSizeForValidTexture() const
+{
+  Vector2 size = GetTargetSize();
+  if(size == Vector2::ZERO)
+  {
+    size = GetOwnerControl().GetNaturalSize();
+  }
+
+  if(size == Vector2::ZERO || size.x < 0.0f || size.y < 0.0f)
+  {
+    return Vector2::ZERO;
+  }
+
+  const uint32_t maxTextureSize = Dali::GetMaxTextureSize();
+  if(uint32_t(size.x) > maxTextureSize || uint32_t(size.y) > maxTextureSize)
+  {
+    uint32_t denominator = std::max(size.x, size.y);
+    size.x               = (size.x * maxTextureSize / denominator);
+    size.y               = (size.y * maxTextureSize / denominator);
+  }
+  return size;
+}
+
+void BlurEffectImpl::SetShaderConstants(uint32_t downsampledWidth, uint32_t downsampledHeight)
+{
+  const uint32_t sampleCount    = mPixelRadius >> 1; // compression
+  const uint32_t kernelSize     = sampleCount * 4 - 1;
+  const uint32_t halfKernelSize = kernelSize / 2 + 1; // Gaussian curve is symmetric
+
+  // Output: Gaussian kernel compressed to half size
+  std::vector<float> uvOffsets(sampleCount);
+  std::vector<float> weights(sampleCount);
+
+  // Generate half size kernel
+  std::vector<float> halfSideKernel(halfKernelSize);
+
+  halfSideKernel[0]  = CalculateGaussianWeight(0.0f, mBellCurveWidth);
+  float totalWeights = halfSideKernel[0];
+  for(unsigned int i = 1; i < halfKernelSize; i++)
+  {
+    float w           = CalculateGaussianWeight(i, mBellCurveWidth);
+    halfSideKernel[i] = w;
+    totalWeights += w * 2.0f;
+  }
+  for(unsigned int i = 0; i < halfKernelSize; i++)
+  {
+    halfSideKernel[i] /= totalWeights;
+  }
+  halfSideKernel[0] *= 0.5f;
+
+  // Compress kernel to half size
+  for(unsigned int i = 0; i < sampleCount; i++)
+  {
+    weights[i]   = halfSideKernel[2 * i] + halfSideKernel[2 * i + 1];
+    uvOffsets[i] = 2.0f * i + halfSideKernel[2 * i + 1] / weights[i];
+  }
+
+  // Set shader constants
+  for(unsigned int i = 0; i < sampleCount; ++i)
+  {
+    mHorizontalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(uvOffsets[i] / downsampledWidth, 0.0f));
+    mHorizontalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
+
+    mVerticalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(0.0f, uvOffsets[i] / downsampledHeight));
+    mVerticalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
+  }
+
+  // Apply background properties
+  if(mIsBackground)
+  {
+    SynchronizeBackgroundCornerRadius();
+  }
+}
+
+std::string BlurEffectImpl::GetSampleOffsetsPropertyName(unsigned int index) const
+{
+  std::ostringstream oss;
+  oss << "uSampleOffsets[" << index << "]";
+  return oss.str();
+}
+
+std::string BlurEffectImpl::GetSampleWeightsPropertyName(unsigned int index) const
+{
+  std::ostringstream oss;
+  oss << "uSampleWeights[" << index << "]";
+  return oss.str();
+}
+
+void BlurEffectImpl::SynchronizeBackgroundCornerRadius()
+{
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Verbose, "[BlurEffect:%p] Synchronize background corner radius\n", this);
+
+  DALI_ASSERT_ALWAYS(GetOwnerControl() && "You should first SetRenderEffect(), then set its background property map");
+
+  Property::Map map    = GetOwnerControl().GetProperty<Property::Map>(Toolkit::Control::Property::BACKGROUND);
+  Vector4       radius = Vector4::ZERO;
+  map[Toolkit::DevelVisual::Property::CORNER_RADIUS].Get(radius);
+
+  Visual::Transform::Policy::Type policy{Visual::Transform::Policy::ABSOLUTE};
+  map[Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY].Get(policy);
+
+  Renderer renderer = GetTargetRenderer();
+  renderer.RegisterProperty("uCornerRadius", radius);
+  renderer.RegisterProperty("uCornerRadiusPolicy", static_cast<float>(policy));
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/render-effects/blur-effect-impl.h b/dali-toolkit/internal/controls/render-effects/blur-effect-impl.h
new file mode 100644 (file)
index 0000000..6147398
--- /dev/null
@@ -0,0 +1,208 @@
+#ifndef DALI_TOOLKIT_INTERNAL_BLUR_EFFECT_H
+#define DALI_TOOLKIT_INTERNAL_BLUR_EFFECT_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/actors/camera-actor.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/object/weak-handle.h>
+#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/rendering/frame-buffer.h>
+#include <string>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
+#include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class BlurEffectImpl;
+using BlurEffectImplPtr = IntrusivePtr<BlurEffectImpl>;
+
+class BlurEffectImpl : public RenderEffectImpl
+{
+public:
+  /**
+   * @brief Creates an initialized BlurEffect implementation, using default settings. The default settings are:
+   *
+   * downscaleFactor = 0.4f
+   * pixelRadius = 5u
+   *
+   * This blur algorithm is used for both foreground and background blurs.
+   *
+   * @param[in] isBackground True when blurring background, False otherwise
+   * @return A handle to a newly allocated Dali resource
+   */
+
+  static BlurEffectImplPtr New(bool isBackground);
+
+  /**
+   * @brief Creates an initialized BlurEffect implementation.
+   * This blur algorithm is used for both foreground and background blurs.
+   *
+   * @param[in] downscaleFactor This value should reside in the range [0.0, 1.0].
+   * @param[in] blurRadius The radius of Gaussian kernel.
+   * @param[in] isBackground True when blurring background, False otherwise
+   * @return A handle to a newly allocated Dali resource
+   */
+  static BlurEffectImplPtr New(float downscaleFactor, uint32_t blurRadius, bool isBackground);
+
+protected:
+  /**
+   * @brief Creates an uninitialized blur effect implementation
+   * @param[in] isBackground True when blurring background, False otherwise
+   */
+  BlurEffectImpl(bool isBackground);
+
+  /**
+   * @brief Creates an uninitialized blur effect implementation
+   * @param[in] downscaleFactor This value should reside in the range [0.0, 1.0].
+   * @param[in] blurRadius The radius of Gaussian kernel.
+   * @param[in] isBackground True when blurring background, False otherwise
+   */
+  BlurEffectImpl(float downscaleFactor, uint32_t blurRadius, bool isBackground);
+
+  /**
+   * @brief Destructor
+   */
+  virtual ~BlurEffectImpl();
+
+  /**
+   * @brief Initializes blur effect
+   */
+  void OnInitialize() override;
+
+  /**
+   * @brief Activates blur effect
+   */
+  void OnActivate() override;
+
+  /**
+   * @brief Dectivates blur effect
+   */
+  void OnDeactivate() override;
+
+private:
+  // Inner functions
+  /**
+   * @brief Sets frame buffers to draw blurred output.
+   * @param[in] size Full size of input.
+   * @param[in] downsampledSize Downsampled size for performance.
+   */
+  void CreateFrameBuffers(const Vector2 size, const ImageDimensions downsampledSize);
+
+  /**
+   * @brief Sets blur render tasks.
+   * Requires initialized buffers, source actors, and source cameras.
+   * @param[in] sceneHolder SceneHolder of source control
+   * @param[in] sourceControl Input source control
+   */
+  void CreateRenderTasks(Integration::SceneHolder sceneHolder, const Toolkit::Control sourceControl);
+
+  /**
+   * @brief Gets or Calculates a valid target size for texture.
+   * Invalid cases include: zero vector, minus numbers or large numbers(larger than the maximum).
+   * @return A valid version of mTargetSize, Vector2::ZERO otherwise.
+   * @note The return value is a copy, not mTargetSize itself.
+   */
+  Vector2 GetTargetSizeForValidTexture() const;
+
+  /**
+   * @brief Sets shader constants, gaussian kernel weights and pixel offsets.
+   * @param[in] downsampledWidth Downsized width of input texture.
+   * @param[in] downsampledHeight Downsized height of input texture.
+   */
+  void SetShaderConstants(uint32_t downsampledWidth, uint32_t downsampledHeight);
+
+  /**
+   * @brief Get an offset property in std::string format
+   * @param[in] index Property's index
+   * @return A string for shader
+   */
+  std::string GetSampleOffsetsPropertyName(unsigned int index) const;
+
+  /**
+   * @brief Get a weight property in std::string format
+   * @param[in] index Property's index
+   * @return A string for shader
+   */
+  std::string GetSampleWeightsPropertyName(unsigned int index) const;
+
+  /**
+   * @brief Synchronize mOwnerControl's background corner radius to the blurred output.
+   */
+  void SynchronizeBackgroundCornerRadius();
+
+  BlurEffectImpl(const BlurEffectImpl&) = delete;
+  BlurEffectImpl(BlurEffectImpl&&)      = delete;
+  BlurEffectImpl& operator=(BlurEffectImpl&&) = delete;      // no move()
+  BlurEffectImpl& operator=(const BlurEffectImpl&) = delete; // no copy()
+
+private:
+  // Camera actors
+  CameraActor mRenderFullSizeCamera;
+  CameraActor mRenderDownsampledCamera;
+
+  // Resource
+  FrameBuffer mInputBackgroundFrameBuffer; // Input. Background. What to blur.
+
+  WeakHandle<Integration::SceneHolder> mPlacementSceneHolder;
+
+  Actor       mInternalRoot;
+  Actor       mHorizontalBlurActor;
+  RenderTask  mHorizontalBlurTask;
+  FrameBuffer mTemporaryFrameBuffer;
+  Actor       mVerticalBlurActor;
+  RenderTask  mVerticalBlurTask;
+
+  FrameBuffer mSourceFrameBuffer; // Output. Blurred background texture for mOwnerControl and mRenderer.
+  RenderTask  mSourceRenderTask;
+
+  // Variables
+  float    mDownscaleFactor;
+  uint32_t mPixelRadius;
+  float    mBellCurveWidth;
+
+  bool mSkipBlur : 1;
+  bool mIsBackground : 1;
+};
+} // namespace Internal
+
+inline Toolkit::Internal::BlurEffectImpl& GetImplementation(Toolkit::BackgroundBlurEffect& obj)
+{
+  BaseObject& handle = obj.GetBaseObject();
+  return static_cast<Toolkit::Internal::BlurEffectImpl&>(handle);
+}
+
+inline const Toolkit::Internal::BlurEffectImpl& GetImplementation(const Toolkit::BackgroundBlurEffect& obj)
+{
+  const BaseObject& handle = obj.GetBaseObject();
+  return static_cast<const Toolkit::Internal::BlurEffectImpl&>(handle);
+}
+
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_BACKGROUND_BLUR_EFFECT_H
diff --git a/dali-toolkit/internal/controls/render-effects/render-effect-impl.cpp b/dali-toolkit/internal/controls/render-effects/render-effect-impl.cpp
new file mode 100644 (file)
index 0000000..c38048e
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/control/control-renderers.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+
+namespace
+{
+static constexpr float SIZE_STEP_CONDITION = 3.0f;
+} // namespace
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+#if defined(DEBUG_ENABLED)
+// Keep this log filter inside of Dali::Toolkit::Internal, so subclass of RenderEffect can also use this.
+Debug::Filter* gRenderEffectLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RENDER_EFFECT");
+#endif
+
+RenderEffectImpl::RenderEffectImpl()
+: mRenderer(),
+  mOwnerControl(),
+  mSizeNotification(),
+  mTargetSize(Vector2::ZERO),
+  mIsActivated(false)
+{
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Verbose, "[RenderEffect:%p] Constructor\n", this);
+}
+
+RenderEffectImpl::~RenderEffectImpl()
+{
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Verbose, "[RenderEffect:%p] Destructor.\n", this);
+
+  // Reset weak handle first. (Since it might not valid during destruction.)
+  mOwnerControl.Reset();
+
+  // Don't call Deactivate here, since we cannot call virtual function during destruction.
+  // Deactivate already be called at Control's destructor, and InheritVisibilityChanged signal.
+}
+
+void RenderEffectImpl::SetOwnerControl(Dali::Toolkit::Control control)
+{
+  Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+  if(ownerControl != control)
+  {
+    // Clear previous owner control
+    ClearOwnerControl();
+
+    mOwnerControl = (ownerControl = control);
+
+    DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[RenderEffect:%p] SetOwnerControl [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
+
+    if(ownerControl)
+    {
+      mTargetSize = ownerControl.GetProperty<Vector2>(Actor::Property::SIZE);
+      if(!mRenderer)
+      {
+        mRenderer = CreateRenderer(SHADER_RENDER_EFFECT_VERT, SHADER_RENDER_EFFECT_FRAG);
+        mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use pre-multiply alpha
+      }
+
+      ownerControl.InheritedVisibilityChangedSignal().Connect(this, &RenderEffectImpl::OnControlInheritedVisibilityChanged);
+
+      mSizeNotification = ownerControl.AddPropertyNotification(Actor::Property::SIZE, StepCondition(SIZE_STEP_CONDITION));
+      mSizeNotification.NotifySignal().Connect(this, &RenderEffectImpl::OnSizeSet);
+
+      Activate(); // Dev note : Activate after set the owner control.
+    }
+  }
+}
+
+void RenderEffectImpl::ClearOwnerControl()
+{
+  Deactivate(); // Dev note : Deactivate before clearing the owner control.
+
+  Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[RenderEffect:%p] ClearOwnerControl [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
+  if(ownerControl)
+  {
+    ownerControl.InheritedVisibilityChangedSignal().Disconnect(this, &RenderEffectImpl::OnControlInheritedVisibilityChanged);
+
+    ownerControl.RemovePropertyNotification(mSizeNotification);
+    mSizeNotification.Reset();
+
+    auto previousOwnerControl = ownerControl;
+    mOwnerControl.Reset();
+
+    // Make previous owner don't have render effect, after make we don't have owner control now.
+    previousOwnerControl.ClearRenderEffect();
+  }
+}
+
+bool RenderEffectImpl::IsActivated() const
+{
+  return mIsActivated;
+}
+
+void RenderEffectImpl::Initialize()
+{
+  OnInitialize();
+}
+
+Toolkit::Control RenderEffectImpl::GetOwnerControl() const
+{
+  return mOwnerControl.GetHandle();
+}
+
+Renderer RenderEffectImpl::GetTargetRenderer() const
+{
+  return mRenderer;
+}
+
+Vector2 RenderEffectImpl::GetTargetSize() const
+{
+  return mTargetSize;
+}
+
+void RenderEffectImpl::Activate()
+{
+  if(!IsActivated() && IsActivateValid())
+  {
+    Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+    DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[RenderEffect:%p] Activated! [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
+    mIsActivated = true;
+
+    // Activate logic for subclass.
+    OnActivate();
+  }
+}
+
+void RenderEffectImpl::Deactivate()
+{
+  if(IsActivated() || !IsActivateValid())
+  {
+    Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+    DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[RenderEffect:%p] Deactivated! [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
+    mIsActivated = false;
+
+    // Deactivate logic for subclass.
+    OnDeactivate();
+  }
+}
+
+bool RenderEffectImpl::IsActivateValid() const
+{
+  // Activate is valid if
+  // - Owner control is on scene
+  // - All actors of owner control are visible
+  // - The SceneHolder is exist, and it is visible
+  // TODO : Currently we don't check SceneHolder's visibility.
+  bool ret = false;
+
+  Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+  if(ownerControl && ownerControl.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+  {
+    Integration::SceneHolder sceneHolder = Integration::SceneHolder::Get(ownerControl);
+    if(sceneHolder)
+    {
+      ret = true;
+
+      // Check visibility of owner control's parents.
+      // TODO : We'd better check the control visibility at core side.
+      // TODO : Window visibility will be consider at dali-core actor side in future.
+      Dali::Actor self = ownerControl;
+      while(self)
+      {
+        if(!self.GetProperty<bool>(Dali::Actor::Property::VISIBLE))
+        {
+          ret = false;
+          break;
+        }
+        self = self.GetParent();
+      }
+    }
+  }
+
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Concise, "[RenderEffect:%p] IsActivateValid? [ID:%d][ret:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1, ret);
+
+  return ret;
+}
+
+void RenderEffectImpl::OnSizeSet(PropertyNotification& source)
+{
+  Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+  if(ownerControl)
+  {
+    const auto targetSize = ownerControl.GetCurrentProperty<Vector2>(Actor::Property::SIZE);
+    if(mTargetSize != targetSize && IsActivated())
+    {
+      mTargetSize = targetSize;
+      Deactivate();
+      Activate();
+    }
+  }
+}
+
+void RenderEffectImpl::OnControlInheritedVisibilityChanged(Actor actor, bool visible)
+{
+  Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
+  DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Concise, "[RenderEffect:%p] visibility changed [ID:%d][visible:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1, visible);
+  if(visible)
+  {
+    Activate();
+  }
+  else
+  {
+    Deactivate();
+  }
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/render-effects/render-effect-impl.h b/dali-toolkit/internal/controls/render-effects/render-effect-impl.h
new file mode 100644 (file)
index 0000000..3913bcb
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef DALI_TOOLKIT_INTERNAL_BACKGROUND_EFFECT_H
+#define DALI_TOOLKIT_INTERNAL_BACKGROUND_EFFECT_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDE
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/object/property-notification.h>
+#include <dali/public-api/object/weak-handle.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <dali/public-api/signals/connection-tracker.h>
+
+//INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+class RenderEffectImpl;
+
+namespace Internal
+{
+using RenderEffectImplPtr = IntrusivePtr<RenderEffectImpl>;
+
+class RenderEffectImpl : public BaseObject, public ConnectionTracker
+{
+public:
+  /**
+   * @brief Sets owner Control. Applies effect on the owner.
+   * @param[in] control The owner control to apply RenderEffect.
+   */
+  void SetOwnerControl(Toolkit::Control control);
+
+  /**
+   * @brief Clears owner Control.
+   */
+  void ClearOwnerControl();
+
+  /**
+   * @brief Get whether this effect activated or not.
+   * @return True if effect is activated. False otherwise.
+   */
+  bool IsActivated() const;
+
+protected:
+  /**
+   * @copydoc Dali::Toolkit::RenderEffect::RenderEffect
+   */
+  RenderEffectImpl();
+
+  /**
+   * @copydoc Dali::Toolkit::RenderEffect::~RenderEffect
+   */
+  virtual ~RenderEffectImpl() override;
+
+  RenderEffectImpl(const RenderEffectImpl&) = delete;
+  RenderEffectImpl(RenderEffectImpl&&)      = delete;
+  RenderEffectImpl& operator=(RenderEffectImpl&&) = delete;      // no move()
+  RenderEffectImpl& operator=(const RenderEffectImpl&) = delete; // no copy()
+
+  /**
+   * @brief Second-phase Initialization
+   */
+  void Initialize();
+
+  /**
+   * @brief Get target renderer
+   * On internal Activate(), the renderer draws our visual effect and is added to our Owner control.
+   * @return mRenderer
+   */
+  Renderer GetTargetRenderer() const;
+
+  /**
+   * @brief The final size of the owner after resizing or relayouts.
+   * @return mTargetSize
+   */
+  Vector2 GetTargetSize() const;
+
+  /**
+   * @brief Get Owner control. It could be return empty handle if owner control is not set, or destroyed.
+   * @return mOwnerControl
+   */
+  Toolkit::Control GetOwnerControl() const;
+
+  /// For sub classes
+protected:
+  /**
+   * @brief Initialize sub classes effect
+   */
+  virtual void OnInitialize() = 0;
+
+  /**
+   * @brief Activates sub classes effect on ownerControl
+   */
+  virtual void OnActivate() = 0;
+
+  /**
+   * @brief Deactivates sub classes effect
+   */
+  virtual void OnDeactivate() = 0;
+
+private:
+  /**
+   * @brief Activates effect on ownerControl
+   */
+  void Activate();
+
+  /**
+   * @brief Deactivates effect
+   */
+  void Deactivate();
+
+  /**
+   * @brief Check whether it is possible to activate effect or not.
+   *        It will check various status, e.g. the control's visibility.
+   * @note This API don't consider mIsActivated
+   */
+  bool IsActivateValid() const;
+
+private:
+  /**
+   * @brief Callback when the size changes.
+   */
+  void OnSizeSet(PropertyNotification& source);
+
+  /**
+   * @brief Callback when the visibility of the actor is changed.
+   * @param[in] actor The actor
+   * @param[in] visible Whether this actor is visible or not.
+   */
+  void OnControlInheritedVisibilityChanged(Actor actor, bool visible);
+
+private:
+  Dali::Renderer mRenderer; // An additional renderer for mOwnerControl
+
+  Dali::WeakHandle<Dali::Toolkit::Control> mOwnerControl; ///< Weakhandle of owner control.
+
+  PropertyNotification mSizeNotification; // Resize/Relayout signal.
+  Vector2              mTargetSize;       // The final size of mOwnerControl
+
+  bool mIsActivated : 1;
+};
+} // namespace Internal
+
+inline Toolkit::Internal::RenderEffectImpl& GetImplementation(Toolkit::RenderEffect& obj)
+{
+  BaseObject& handle = obj.GetBaseObject();
+  return static_cast<Toolkit::Internal::RenderEffectImpl&>(handle);
+}
+
+inline const Toolkit::Internal::RenderEffectImpl& GetImplementation(const Toolkit::RenderEffect& obj)
+{
+  const BaseObject& handle = obj.GetBaseObject();
+  return static_cast<const Toolkit::Internal::RenderEffectImpl&>(handle);
+}
+
+} // namespace Toolkit
+} // namespace Dali
+#endif // DALI_TOOLKIT_INTERNAL_BACKGROUND_EFFECT_H
index 43a5884..33deefc 100644 (file)
@@ -912,11 +912,15 @@ void TextEditor::OnTap(const TapGesture& gesture)
 
 void TextEditor::OnPan(const PanGesture& gesture)
 {
-  mController->PanEvent(gesture.GetState(), gesture.GetDisplacement());
-  if(gesture.GetState() == GestureState::STARTED && !mController->IsScrollable(gesture.GetDisplacement()))
+  if(!mController->IsScrollable(gesture.GetDisplacement()))
   {
     Dali::DevelActor::SetNeedGesturePropagation(Self(), true);
   }
+  else
+  {
+    Dali::DevelActor::SetNeedGesturePropagation(Self(), false);
+  }
+  mController->PanEvent(gesture.GetState(), gesture.GetDisplacement());
 }
 
 void TextEditor::OnLongPress(const LongPressGesture& gesture)
@@ -1284,10 +1288,21 @@ void TextEditor::KeyboardStatusChanged(bool keyboardShown)
 {
   DALI_LOG_INFO(gTextEditorLogFilter, Debug::Verbose, "TextEditor::KeyboardStatusChanged %p keyboardShown %d\n", mController.Get(), keyboardShown);
 
+  bool isFocused = false;
+
+  Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+  if(keyboardFocusManager)
+  {
+    isFocused = keyboardFocusManager.GetCurrentFocusActor() == Self();
+  }
+
   // Just hide the grab handle when keyboard is hidden.
   if(!keyboardShown)
   {
-    mController->KeyboardFocusLostEvent();
+    if(!isFocused)
+    {
+      mController->KeyboardFocusLostEvent();
+    }
   }
   else
   {
index 8336b14..aa15a8d 100644 (file)
@@ -819,11 +819,15 @@ void TextField::OnTap(const TapGesture& gesture)
 
 void TextField::OnPan(const PanGesture& gesture)
 {
-  mController->PanEvent(gesture.GetState(), gesture.GetDisplacement());
-  if(gesture.GetState() == GestureState::STARTED && !mController->IsScrollable(gesture.GetDisplacement()))
+  if(!mController->IsScrollable(gesture.GetDisplacement()))
   {
     Dali::DevelActor::SetNeedGesturePropagation(Self(), true);
   }
+  else
+  {
+    Dali::DevelActor::SetNeedGesturePropagation(Self(), false);
+  }
+  mController->PanEvent(gesture.GetState(), gesture.GetDisplacement());
 }
 
 void TextField::OnLongPress(const LongPressGesture& gesture)
@@ -859,7 +863,8 @@ bool TextField::OnKeyEvent(const KeyEvent& event)
 
     return true;
   }
-  else if(Dali::DevelKey::DALI_KEY_RETURN == event.GetKeyCode() && KEY_RETURN_NAME == event.GetKeyName())
+  else if((Dali::DevelKey::DALI_KEY_RETURN == event.GetKeyCode() && KEY_RETURN_NAME == event.GetKeyName()) ||
+           Dali::DevelKey::DALI_KEY_KP_ENTER == event.GetKeyCode())
   {
     // Do nothing when enter is comming.
     return false;
@@ -1117,10 +1122,21 @@ void TextField::KeyboardStatusChanged(bool keyboardShown)
 {
   DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField::KeyboardStatusChanged %p keyboardShown %d\n", mController.Get(), keyboardShown);
 
+  bool isFocused = false;
+
+  Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+  if(keyboardFocusManager)
+  {
+    isFocused = keyboardFocusManager.GetCurrentFocusActor() == Self();
+  }
+
   // Just hide the grab handle when keyboard is hidden.
   if(!keyboardShown)
   {
-    mController->KeyboardFocusLostEvent();
+    if(!isFocused)
+    {
+      mController->KeyboardFocusLostEvent();
+    }
   }
   else
   {
index 5af0fe1..d66cfc0 100644 (file)
@@ -63,6 +63,8 @@ namespace Internal
 {
 namespace
 {
+static constexpr uint32_t NUMBER_OF_RENDER_MODE = 3;
+
 const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::DevelText::DEFAULT_RENDERING_BACKEND;
 
 /**
@@ -148,6 +150,9 @@ DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "anchorClickedCol
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "removeFrontInset",             BOOLEAN, REMOVE_FRONT_INSET             )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "removeBackInset",              BOOLEAN, REMOVE_BACK_INSET              )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "cutout",                       BOOLEAN, CUTOUT                         )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextLabel, "renderMode",                   INTEGER, RENDER_MODE                    )
+DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY(Toolkit, TextLabel, "manualRendered",               BOOLEAN, MANUAL_RENDERED                )
+DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY(Toolkit, TextLabel, "asyncLineCount",               INTEGER, ASYNC_LINE_COUNT               )
 
 DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, TextLabel, "textColor",      Color::BLACK,     TEXT_COLOR   )
 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit,    TextLabel, "textColorRed",   TEXT_COLOR_RED,   TEXT_COLOR, 0)
@@ -155,8 +160,11 @@ DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit,    TextLabel, "textColo
 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit,    TextLabel, "textColorBlue",  TEXT_COLOR_BLUE,  TEXT_COLOR, 2)
 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit,    TextLabel, "textColorAlpha", TEXT_COLOR_ALPHA, TEXT_COLOR, 3)
 
-DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "anchorClicked", SIGNAL_ANCHOR_CLICKED)
-DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "textFitChanged", SIGNAL_TEXT_FIT_CHANGED)
+DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "anchorClicked",               SIGNAL_ANCHOR_CLICKED                 )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "textFitChanged",              SIGNAL_TEXT_FIT_CHANGED               )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "asyncTextRendered",           SIGNAL_ASYNC_TEXT_RENDERED            )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "asyncNaturalSizeComputed",    SIGNAL_ASYNC_NATURAL_SIZE_COMPUTED    )
+DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "asyncHeightForWidthComputed", SIGNAL_ASYNC_HEIGHT_FOR_WIDTH_COMPUTED)
 
 DALI_TYPE_REGISTRATION_END()
 // clang-format on
@@ -232,6 +240,20 @@ void ParseTextFitProperty(Text::ControllerPtr& controller, const Property::Map*
   }
 }
 
+/**
+ * @brief Discard the given visual into VisualFactory. The visual will be destroyed at next idle time.
+ *
+ * @param[in,out] visual Visual to be discarded. It will be reset to an empty handle.
+ */
+void DiscardTextLabelVisual(Dali::Toolkit::Visual::Base& visual)
+{
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && visual))
+  {
+    Dali::Toolkit::VisualFactory::Get().DiscardVisual(visual);
+  }
+  visual.Reset();
+}
+
 } // namespace
 
 Toolkit::TextLabel TextLabel::New(ControlBehaviour additionalBehaviour)
@@ -284,6 +306,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
       case Toolkit::TextLabel::Property::TEXT:
       {
         impl.mController->SetText(value.Get<std::string>());
+        impl.mTextUpdateNeeded = true;
 
         if(impl.mController->HasAnchors())
         {
@@ -303,11 +326,13 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
 
         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextLabel::SetProperty Property::FONT_FAMILY newFont(%s)\n", fontFamily.c_str());
         impl.mController->SetDefaultFontFamily(fontFamily);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::TextLabel::Property::FONT_STYLE:
       {
         SetFontStyleProperty(impl.mController, value, Text::FontStyle::DEFAULT);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::TextLabel::Property::POINT_SIZE:
@@ -317,12 +342,14 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         if(!Equals(impl.mController->GetDefaultFontSize(Text::Controller::POINT_SIZE), pointSize))
         {
           impl.mController->SetDefaultFontSize(pointSize, Text::Controller::POINT_SIZE);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
       case Toolkit::TextLabel::Property::MULTI_LINE:
       {
         impl.mController->SetMultiLineEnabled(value.Get<bool>());
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT:
@@ -331,6 +358,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         if(Text::GetHorizontalAlignmentEnumeration(value, alignment))
         {
           impl.mController->SetHorizontalAlignment(alignment);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -340,6 +368,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         if(Text::GetVerticalAlignmentEnumeration(value, alignment))
         {
           impl.mController->SetVerticalAlignment(alignment);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -379,6 +408,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
           {
             impl.mController->SetAutoScrollEnabled(enableAutoScroll);
           }
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -449,6 +479,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         if(!Equals(impl.mController->GetDefaultFontSize(Text::Controller::PIXEL_SIZE), pixelSize))
         {
           impl.mController->SetDefaultFontSize(pixelSize, Text::Controller::PIXEL_SIZE);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -458,6 +489,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         DALI_LOG_INFO(gLogFilter, Debug::General, "TextLabel %p ELLIPSIS %d\n", impl.mController.Get(), ellipsis);
 
         impl.mController->SetTextElideEnabled(ellipsis);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::TextLabel::Property::LINE_WRAP_MODE:
@@ -467,6 +499,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         {
           DALI_LOG_INFO(gLogFilter, Debug::General, "TextLabel %p LineWrap::MODE %d\n", impl.mController.Get(), lineWrapMode);
           impl.mController->SetLineWrapMode(lineWrapMode);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -483,6 +516,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
 
           // No need to trigger full re-layout. Instead call UpdateRenderer() directly
           TextVisual::UpdateRenderer(impl.mVisual);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -494,11 +528,13 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
       case Toolkit::DevelTextLabel::Property::IGNORE_SPACES_AFTER_TEXT:
       {
         impl.mController->SetIgnoreSpacesAfterText(value.Get<bool>());
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::MATCH_SYSTEM_LANGUAGE_DIRECTION:
       {
         impl.mController->SetMatchLayoutDirection(value.Get<bool>() ? DevelText::MatchLayoutDirection::LOCALE : DevelText::MatchLayoutDirection::CONTENTS);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::TEXT_FIT:
@@ -512,6 +548,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
 
         ParseTextFitProperty(impl.mController, value.GetMap());
         impl.mController->SetTextFitChanged(true);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::MIN_LINE_SIZE:
@@ -523,6 +560,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
           impl.mTextUpdateNeeded = impl.mController->SetDefaultLineSize(lineSize) || impl.mTextUpdateNeeded;
         }
         impl.mController->SetCurrentLineSize(lineSize);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::FONT_SIZE_SCALE:
@@ -533,6 +571,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         if(!Equals(impl.mController->GetFontSizeScale(), scale))
         {
           impl.mController->SetFontSizeScale(scale);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -542,6 +581,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         if(!Equals(impl.mController->IsFontSizeScaleEnabled(), enableFontSizeScale))
         {
           impl.mController->SetFontSizeScaleEnabled(enableFontSizeScale);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -552,6 +592,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         {
           DALI_LOG_INFO(gLogFilter, Debug::General, "TextLabel %p EllipsisPosition::Type %d\n", impl.mController.Get(), ellipsisPositionType);
           impl.mController->SetEllipsisPosition(ellipsisPositionType);
+          impl.mIsAsyncRenderNeeded = true;
         }
         break;
       }
@@ -564,6 +605,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
       {
         const float characterSpacing = value.Get<float>();
         impl.mController->SetCharacterSpacing(characterSpacing);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::RELATIVE_LINE_SIZE:
@@ -572,6 +614,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextLabel %p RELATIVE_LINE_SIZE %f\n", impl.mController.Get(), relativeLineSize);
 
         impl.mController->SetRelativeLineSize(relativeLineSize);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::ANCHOR_COLOR:
@@ -598,12 +641,14 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
       {
         const bool remove = value.Get<bool>();
         impl.mController->SetRemoveFrontInset(remove);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::REMOVE_BACK_INSET:
       {
         const bool remove = value.Get<bool>();
         impl.mController->SetRemoveBackInset(remove);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
       case Toolkit::DevelTextLabel::Property::CUTOUT:
@@ -611,14 +656,26 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
         const bool cutout = value.Get<bool>();
 
         impl.mController->SetTextCutout(cutout);
-
-        // Property doesn't affect the layout, only Visual must be updated
-        TextVisual::EnableRendererUpdate(impl.mVisual);
-
-        // No need to trigger full re-layout. Instead call UpdateRenderer() directly
-        TextVisual::UpdateRenderer(impl.mVisual);
+        impl.mIsAsyncRenderNeeded = true;
         break;
       }
+      case Toolkit::DevelTextLabel::Property::RENDER_MODE:
+      {
+        DevelTextLabel::Render::Mode renderMode = static_cast<DevelTextLabel::Render::Mode>(value.Get<int>());
+        if(renderMode < 0 || renderMode >= NUMBER_OF_RENDER_MODE)
+        {
+          renderMode = DevelTextLabel::Render::SYNC;
+        }
+
+        if(impl.mController->GetRenderMode() != renderMode)
+        {
+          impl.mController->SetRenderMode(renderMode);
+          if(renderMode == DevelTextLabel::Render::ASYNC_AUTO)
+          {
+            impl.RequestTextRelayout();
+          }
+        }
+      }
     }
 
     // Request relayout when text update is needed. It's necessary to call it
@@ -628,6 +685,7 @@ void TextLabel::SetProperty(BaseObject* object, Property::Index index, const Pro
     {
       // need to request relayout as size of text may have changed
       impl.RequestTextRelayout();
+      impl.mIsAsyncRenderNeeded = true;
     }
   }
 }
@@ -908,6 +966,21 @@ Property::Value TextLabel::GetProperty(BaseObject* object, Property::Index index
         value = impl.mController->IsTextCutout();
         break;
       }
+      case Toolkit::DevelTextLabel::Property::RENDER_MODE:
+      {
+        value = impl.mController->GetRenderMode();
+        break;
+      }
+      case Toolkit::DevelTextLabel::Property::MANUAL_RENDERED:
+      {
+        value = impl.mManualRendered;
+        break;
+      }
+      case Toolkit::DevelTextLabel::Property::ASYNC_LINE_COUNT:
+      {
+        value = impl.mAsyncLineCount;
+        break;
+      }
     }
   }
 
@@ -937,6 +1010,30 @@ bool TextLabel::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface*
       labelImpl.TextFitChangedSignal().Connect(tracker, functor);
     }
   }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_ASYNC_TEXT_RENDERED))
+  {
+    if(label)
+    {
+      Internal::TextLabel& labelImpl(GetImpl(label));
+      labelImpl.AsyncTextRenderedSignal().Connect(tracker, functor);
+    }
+  }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_ASYNC_NATURAL_SIZE_COMPUTED))
+  {
+    if(label)
+    {
+      Internal::TextLabel& labelImpl(GetImpl(label));
+      labelImpl.AsyncNaturalSizeComputedSignal().Connect(tracker, functor);
+    }
+  }
+  else if(0 == strcmp(signalName.c_str(), SIGNAL_ASYNC_HEIGHT_FOR_WIDTH_COMPUTED))
+  {
+    if(label)
+    {
+      Internal::TextLabel& labelImpl(GetImpl(label));
+      labelImpl.AsyncHeightForWidthComputedSignal().Connect(tracker, functor);
+    }
+  }
   else
   {
     // signalName does not match any signal
@@ -956,6 +1053,21 @@ DevelTextLabel::TextFitChangedSignalType& TextLabel::TextFitChangedSignal()
   return mTextFitChangedSignal;
 }
 
+DevelTextLabel::AsyncTextRenderedSignalType& TextLabel::AsyncTextRenderedSignal()
+{
+  return mAsyncTextRenderedSignal;
+}
+
+DevelTextLabel::AsyncNaturalSizeComputedSignalType& TextLabel::AsyncNaturalSizeComputedSignal()
+{
+  return mAsyncNaturalSizeComputedSignal;
+}
+
+DevelTextLabel::AsyncHeightForWidthComputedSignalType& TextLabel::AsyncHeightForWidthComputedSignal()
+{
+  return mAsyncHeightForWidthComputedSignal;
+}
+
 void TextLabel::OnInitialize()
 {
   Actor self = Self();
@@ -966,6 +1078,7 @@ void TextLabel::OnInitialize()
   mVisual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap);
   DevelControl::RegisterVisual(*this, Toolkit::TextLabel::Property::TEXT, mVisual, DepthIndex::CONTENT);
 
+  TextVisual::SetAsyncTextInterface(mVisual, this);
   TextVisual::SetAnimatableTextColorProperty(mVisual, Toolkit::TextLabel::Property::TEXT_COLOR);
 
   mController = TextVisual::GetController(mVisual);
@@ -1084,13 +1197,44 @@ void TextLabel::OnPropertySet(Property::Index index, const Property::Value& prop
 
   switch(index)
   {
+    case Dali::Actor::Property::SIZE:
+    {
+      const Vector2& size = propertyValue.Get<Vector2>();
+      if(mSize != size)
+      {
+        mSize          = size;
+        mIsSizeChanged = true;
+      }
+      break;
+    }
+    case Dali::Actor::Property::SIZE_WIDTH:
+    {
+      const float width = propertyValue.Get<float>();
+      if(mSize.width != width)
+      {
+        mSize.width    = width;
+        mIsSizeChanged = true;
+      }
+      break;
+    }
+    case Dali::Actor::Property::SIZE_HEIGHT:
+    {
+      const float height = propertyValue.Get<float>();
+      if(mSize.height != height)
+      {
+        mSize.height   = height;
+        mIsSizeChanged = true;
+      }
+      break;
+    }
     case Toolkit::TextLabel::Property::TEXT_COLOR:
     {
       const Vector4& textColor = propertyValue.Get<Vector4>();
       if(mController->GetDefaultColor() != textColor)
       {
         mController->SetDefaultColor(textColor);
-        mTextUpdateNeeded = true;
+        mTextUpdateNeeded    = true;
+        mIsAsyncRenderNeeded = true;
       }
       break;
     }
@@ -1102,45 +1246,41 @@ void TextLabel::OnPropertySet(Property::Index index, const Property::Value& prop
     }
     case Toolkit::Control::Property::BACKGROUND:
     {
-      const Vector4 backgroundColor = propertyValue.Get<Vector4>();
-
       if(mController->IsTextCutout())
       {
-        DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, false);
-        mController->SetBackgroundWithCutoutEnabled(true);
+        const Vector4 backgroundColor = propertyValue.Get<Vector4>();
         mController->SetBackgroundColorWithCutout(backgroundColor);
-      }
+        mController->SetBackgroundWithCutoutEnabled(true);
 
+        if(mController->GetRenderMode() == DevelTextLabel::Render::SYNC)
+        {
+          EnableControlBackground(false);
+        }
+        mIsAsyncRenderNeeded = true;
+      }
       break;
     }
     case Toolkit::DevelTextLabel::Property::CUTOUT:
     {
       const bool cutoutEnabled = propertyValue.Get<bool>();
-
+      mController->SetBackgroundWithCutoutEnabled(cutoutEnabled);
       if(cutoutEnabled)
       {
-        Vector4 backgroundColor = Vector4::ZERO;
-
         const Property::Map backgroundMap   = Self().GetProperty(Toolkit::Control::Property::BACKGROUND).Get<Property::Map>();
         Property::Value*    backgroundValue = backgroundMap.Find(ColorVisual::Property::MIX_COLOR);
         if(backgroundValue)
         {
-          backgroundColor = backgroundValue->Get<Vector4>();
+          Vector4 backgroundColor = Vector4::ZERO;
+          backgroundColor         = backgroundValue->Get<Vector4>();
+          mController->SetBackgroundColorWithCutout(backgroundColor);
         }
-
-        DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, false);
-        mController->SetBackgroundWithCutoutEnabled(true);
-        mController->SetBackgroundColorWithCutout(backgroundColor);
       }
-      else
+      if(mController->GetRenderMode() == DevelTextLabel::Render::SYNC)
       {
-        DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, true);
-
-        Property::Map backgroundMapSet;
-        mController->SetBackgroundWithCutoutEnabled(false);
+        EnableControlBackground(!cutoutEnabled);
+        TextVisual::SetRequireRender(mVisual, cutoutEnabled);
       }
-
-      TextVisual::SetRequireRender(mVisual, cutoutEnabled);
+      mIsAsyncRenderNeeded = true;
       break;
     }
     default:
@@ -1153,6 +1293,8 @@ void TextLabel::OnPropertySet(Property::Index index, const Property::Value& prop
 
 void TextLabel::OnSceneConnection(int depth)
 {
+  mIsAsyncRenderNeeded = true;
+
   if(mController->IsAutoScrollEnabled() || mLastAutoScrollEnabled)
   {
     mController->SetAutoScrollEnabled(true);
@@ -1162,6 +1304,10 @@ void TextLabel::OnSceneConnection(int depth)
 
 void TextLabel::OnSceneDisconnection()
 {
+  mIsSizeChanged    = false;
+  mIsManualRender   = false;
+  mIsManualRendered = false;
+
   if(mTextScroller)
   {
     if(mLastAutoScrollEnabled && !mController->IsAutoScrollEnabled())
@@ -1183,6 +1329,15 @@ void TextLabel::OnSceneDisconnection()
 void TextLabel::OnRelayout(const Vector2& size, RelayoutContainer& container)
 {
   DALI_LOG_INFO(gLogFilter, Debug::General, "TextLabel::OnRelayout\n");
+  bool sizeChanged    = mIsSizeChanged;
+  bool manualRendered = mIsManualRendered;
+  mIsSizeChanged      = false;
+  mIsManualRendered   = false;
+
+  if(mController->GetRenderMode() == DevelTextLabel::Render::ASYNC_MANUAL)
+  {
+    return;
+  }
 
   if(mTextScroller && mTextScroller->IsStop())
   {
@@ -1195,7 +1350,50 @@ void TextLabel::OnRelayout(const Vector2& size, RelayoutContainer& container)
   Extents padding;
   padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
 
-  Vector2 contentSize(size.x - (padding.start + padding.end), size.y - (padding.top + padding.bottom));
+  float   width  = std::max(size.x - (padding.start + padding.end), 0.0f);
+  float   height = std::max(size.y - (padding.top + padding.bottom), 0.0f);
+  Vector2 contentSize(width, height);
+
+  // Support Right-To-Left
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
+
+  // Support Right-To-Left of padding
+  if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
+  {
+    std::swap(padding.start, padding.end);
+  }
+
+  if(mController->GetRenderMode() == DevelTextLabel::Render::ASYNC_AUTO ||
+     mController->GetRenderMode() == DevelTextLabel::Render::ASYNC_MANUAL)
+  {
+    if(mController->GetRenderMode() == DevelTextLabel::Render::ASYNC_AUTO && mTextScroller && mTextScroller->IsScrolling() && !(mTextUpdateNeeded || sizeChanged))
+    {
+      // When auto scroll is playing, a text load request is made only if a text update is absolutely necessary.
+      return;
+    }
+
+    if(mIsManualRender || !(sizeChanged || mIsAsyncRenderNeeded))
+    {
+      // Do not request async render if the manual render is still ongoing or if there are no size or property updates.
+      return;
+    }
+
+    if(manualRendered && sizeChanged && !mIsAsyncRenderNeeded)
+    {
+      // Do not request async render if only the size has changed when manual render is completed.
+      // Users may attempt to change the size inside the completed callback post manual render.
+      // In case of ASYNC_AUTO, this could potentially trigger relayout and engender further computation.
+      // This is needed to avoid recomputations, but it may have some limitations.
+      return;
+    }
+
+    DALI_LOG_RELEASE_INFO("Request render, size : %f, %f\n", contentSize.width, contentSize.height);
+    AsyncTextParameters parameters = GetAsyncTextParameters(Async::RENDER_FIXED_SIZE, contentSize, padding, layoutDirection);
+    TextVisual::UpdateAsyncRenderer(mVisual, parameters);
+    mTextUpdateNeeded    = false;
+    mIsAsyncRenderNeeded = false;
+    return;
+  }
 
   if(mController->IsTextFitArrayEnabled())
   {
@@ -1208,9 +1406,6 @@ void TextLabel::OnRelayout(const Vector2& size, RelayoutContainer& container)
     mController->SetTextFitContentSize(contentSize);
   }
 
-  // Support Right-To-Left
-  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
-
   const Text::Controller::UpdateTextType updateTextType = mController->Relayout(contentSize, layoutDirection);
 
   if((Text::Controller::NONE_UPDATED != (Text::Controller::MODEL_UPDATED & updateTextType)) || mTextUpdateNeeded)
@@ -1220,12 +1415,6 @@ void TextLabel::OnRelayout(const Vector2& size, RelayoutContainer& container)
     // Update the visual
     TextVisual::EnableRendererUpdate(mVisual);
 
-    // Support Right-To-Left of padding
-    if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
-    {
-      std::swap(padding.start, padding.end);
-    }
-
     // Calculate the size of the visual that can fit the text
     Size layoutSize = mController->GetTextModel()->GetLayoutSize();
     layoutSize.x    = contentSize.x;
@@ -1290,6 +1479,82 @@ void TextLabel::RequestTextRelayout()
   // Signal that a Relayout may be needed
 }
 
+AsyncTextParameters TextLabel::GetAsyncTextParameters(const Async::RequestType requestType, const Vector2& contentSize, const Extents& padding, const Dali::LayoutDirection::Type layoutDirection)
+{
+  // Logically, all properties of the text label should be passed.
+
+  std::string text;
+  mController->GetRawText(text);
+
+  AsyncTextParameters parameters;
+  parameters.requestType     = requestType;
+  parameters.textWidth       = contentSize.width;
+  parameters.textHeight      = contentSize.height;
+  parameters.padding         = padding;
+  parameters.layoutDirection = layoutDirection;
+  parameters.text            = text;
+
+  parameters.maxTextureSize         = Dali::GetMaxTextureSize();
+  parameters.fontSize               = mController->GetDefaultFontSize(Text::Controller::POINT_SIZE);
+  parameters.textColor              = mController->GetDefaultColor();
+  parameters.fontFamily             = mController->GetDefaultFontFamily();
+  parameters.fontWeight             = mController->GetDefaultFontWeight();
+  parameters.fontWidth              = mController->GetDefaultFontWidth();
+  parameters.fontSlant              = mController->GetDefaultFontSlant();
+  parameters.isMultiLine            = mController->IsMultiLineEnabled();
+  parameters.ellipsis               = mController->IsTextElideEnabled();
+  parameters.enableMarkup           = mController->IsMarkupProcessorEnabled();
+  parameters.removeFrontInset       = mController->IsRemoveFrontInset();
+  parameters.removeBackInset        = mController->IsRemoveBackInset();
+  parameters.minLineSize            = mController->GetDefaultLineSize();
+  parameters.lineSpacing            = mController->GetDefaultLineSpacing();
+  parameters.relativeLineSize       = mController->GetRelativeLineSize();
+  parameters.characterSpacing       = mController->GetCharacterSpacing();
+  parameters.fontSizeScale          = mController->IsFontSizeScaleEnabled() ? mController->GetFontSizeScale() : 1.f;
+  parameters.horizontalAlignment    = mController->GetHorizontalAlignment();
+  parameters.verticalAlignment      = mController->GetVerticalAlignment();
+  parameters.verticalLineAlignment  = mController->GetVerticalLineAlignment();
+  parameters.lineWrapMode           = mController->GetLineWrapMode();
+  parameters.layoutDirectionPolicy  = mController->GetMatchLayoutDirection();
+  parameters.ellipsisPosition       = mController->GetEllipsisPosition();
+  parameters.isUnderlineEnabled     = mController->IsUnderlineEnabled();
+  parameters.underlineType          = mController->GetUnderlineType();
+  parameters.underlineColor         = mController->GetUnderlineColor();
+  parameters.underlineHeight        = mController->GetUnderlineHeight();
+  parameters.dashedUnderlineWidth   = mController->GetDashedUnderlineWidth();
+  parameters.dashedUnderlineGap     = mController->GetDashedUnderlineGap();
+  parameters.isStrikethroughEnabled = mController->IsStrikethroughEnabled();
+  parameters.strikethroughColor     = mController->GetStrikethroughColor();
+  parameters.strikethroughHeight    = mController->GetStrikethroughHeight();
+  parameters.shadowBlurRadius       = mController->GetShadowBlurRadius();
+  parameters.shadowColor            = mController->GetShadowColor();
+  parameters.shadowOffset           = mController->GetShadowOffset();
+  parameters.outlineWidth           = mController->GetOutlineWidth();
+  parameters.outlineColor           = mController->GetOutlineColor();
+  parameters.outlineBlurRadius      = mController->GetOutlineBlurRadius();
+  parameters.outlineOffset          = mController->GetOutlineOffset();
+  parameters.isTextFitEnabled       = mController->IsTextFitEnabled();
+  parameters.textFitMinSize         = mController->GetTextFitMinSize();
+  parameters.textFitMaxSize         = mController->GetTextFitMaxSize();
+  parameters.textFitStepSize        = mController->GetTextFitStepSize();
+  parameters.isTextFitArrayEnabled  = mController->IsTextFitArrayEnabled();
+  parameters.textFitArray           = mController->GetTextFitArray();
+  parameters.isAutoScrollEnabled    = mController->IsAutoScrollEnabled();
+  if(parameters.isAutoScrollEnabled)
+  {
+    parameters.autoScrollStopMode  = GetTextScroller()->GetStopMode();
+    parameters.autoScrollSpeed     = GetTextScroller()->GetSpeed();
+    parameters.autoScrollLoopCount = GetTextScroller()->GetLoopCount();
+    parameters.autoScrollLoopDelay = GetTextScroller()->GetLoopDelay();
+    parameters.autoScrollGap       = GetTextScroller()->GetGap();
+  }
+  parameters.cutout                      = mController->IsTextCutout();
+  parameters.backgroundWithCutoutEnabled = mController->IsBackgroundWithCutoutEnabled();
+  parameters.backgroundColorWithCutout   = mController->GetBackgroundColorWithCutout();
+
+  return parameters;
+}
+
 void TextLabel::SetUpAutoScrolling()
 {
   const Size&                    controlSize     = mController->GetView().GetControlSize();
@@ -1301,7 +1566,6 @@ void TextLabel::SetUpAutoScrolling()
   if(!mTextScroller)
   {
     DALI_LOG_INFO(gLogFilter, Debug::General, "TextLabel::SetUpAutoScrolling Creating default TextScoller\n");
-
     // If speed, loopCount or gap not set via property system then will need to create a TextScroller with defaults
     mTextScroller = Text::TextScroller::New(*this);
   }
@@ -1354,6 +1618,35 @@ void TextLabel::SetUpAutoScrolling()
   mController->SetAutoScrollMaxTextureExceeded(false);
 }
 
+void TextLabel::AsyncSetupAutoScroll(Text::AsyncTextRenderInfo renderInfo)
+{
+  // Pure Virtual from AsyncTextInterface
+  Size verifiedSize(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
+
+  Size  controlSize = renderInfo.controlSize;
+  float wrapGap     = renderInfo.autoScrollWrapGap;
+
+  PixelData data    = renderInfo.autoScrollPixelData;
+  Texture   texture = Texture::New(Dali::TextureType::TEXTURE_2D,
+                                 data.GetPixelFormat(),
+                                 data.GetWidth(),
+                                 data.GetHeight());
+  texture.Upload(data);
+
+  TextureSet textureSet = TextureSet::New();
+  textureSet.SetTexture(0u, texture);
+
+  // Filter mode needs to be set to linear to produce better quality while scaling.
+  Sampler sampler = Sampler::New();
+  sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
+  sampler.SetWrapMode(Dali::WrapMode::DEFAULT, Dali::WrapMode::REPEAT, Dali::WrapMode::DEFAULT); // Wrap the texture in the x direction
+  textureSet.SetSampler(0u, sampler);
+
+  // Set parameters for scrolling
+  Renderer renderer = static_cast<Internal::Visual::Base&>(GetImplementation(mVisual)).GetRenderer();
+  mTextScroller->SetParameters(Self(), renderer, textureSet, controlSize, verifiedSize, wrapGap, renderInfo.isTextDirectionRTL, mController->GetHorizontalAlignment(), mController->GetVerticalAlignment());
+}
+
 void TextLabel::ScrollingFinished()
 {
   // Pure Virtual from TextScroller Interface
@@ -1362,6 +1655,75 @@ void TextLabel::ScrollingFinished()
   RequestTextRelayout();
 }
 
+void TextLabel::AsyncTextFitChanged(float pointSize)
+{
+  // Pure Virtual from AsyncTextInterface
+  DALI_LOG_INFO(gLogFilter, Debug::General, "TextLabel::AsyncTextFitChanged pointSize : %f\n", pointSize);
+  if(mController->IsTextFitEnabled())
+  {
+    mController->SetTextFitPointSize(pointSize);
+    EmitTextFitChangedSignal();
+  }
+}
+
+void TextLabel::AsyncSizeComputed(Text::AsyncTextRenderInfo renderInfo)
+{
+  switch(renderInfo.requestType)
+  {
+    case Async::COMPUTE_NATURAL_SIZE:
+    {
+      DALI_LOG_RELEASE_INFO("Natural size : %f, %f, line count : %d\n", renderInfo.renderedSize.width, renderInfo.renderedSize.height, renderInfo.lineCount);
+      mAsyncLineCount = renderInfo.lineCount;
+      EmitAsyncNaturalSizeComputedSignal(renderInfo.renderedSize.width, renderInfo.renderedSize.height);
+      break;
+    }
+    case Async::COMPUTE_HEIGHT_FOR_WIDTH:
+    {
+      DALI_LOG_RELEASE_INFO("Height for width : %f, %f, line count : %d\n", renderInfo.renderedSize.width, renderInfo.renderedSize.height, renderInfo.lineCount);
+      mAsyncLineCount = renderInfo.lineCount;
+      EmitAsyncHeightForWidthComputedSignal(renderInfo.renderedSize.width, renderInfo.renderedSize.height);
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unexpected request type recieved : %d\n", renderInfo.requestType);
+      break;
+    }
+  }
+}
+
+void TextLabel::AsyncLoadComplete(Text::AsyncTextRenderInfo renderInfo)
+{
+  // Pure Virtual from AsyncTextInterface
+  DALI_LOG_RELEASE_INFO("Rendered size : %f, %f, line count : %d\n", renderInfo.renderedSize.width, renderInfo.renderedSize.height, renderInfo.lineCount);
+
+  // To avoid flickering issues, enable/disable the background visual when async load is completed.
+  EnableControlBackground(!mController->IsTextCutout());
+
+  Actor self = Self();
+
+  Extents padding;
+  padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+
+  if(mIsManualRender)
+  {
+    mIsManualRender   = false;
+    mIsManualRendered = true;
+  }
+
+  mManualRendered = renderInfo.manualRendered;
+  mAsyncLineCount = renderInfo.lineCount;
+
+  if(renderInfo.isCutout)
+  {
+    EmitAsyncTextRenderedSignal(renderInfo.renderedSize.width, renderInfo.renderedSize.height);
+  }
+  else
+  {
+    EmitAsyncTextRenderedSignal(renderInfo.renderedSize.width + (padding.start + padding.end), renderInfo.renderedSize.height + (padding.top + padding.bottom));
+  }
+}
+
 void TextLabel::OnLayoutDirectionChanged(Actor actor, LayoutDirection::Type type)
 {
   mController->ChangedLayoutDirection();
@@ -1387,6 +1749,28 @@ void TextLabel::EmitTextFitChangedSignal()
   mTextFitChangedSignal.Emit(handle);
 }
 
+void TextLabel::EmitAsyncTextRenderedSignal(float width, float height)
+{
+  Dali::Toolkit::TextLabel handle(GetOwner());
+  mAsyncTextRenderedSignal.Emit(handle, width, height);
+}
+
+void TextLabel::EmitAsyncNaturalSizeComputedSignal(float width, float height)
+{
+  Dali::Toolkit::TextLabel handle(GetOwner());
+  Extents                  padding;
+  padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+  mAsyncNaturalSizeComputedSignal.Emit(handle, width + (padding.start + padding.end), height + (padding.top + padding.bottom));
+}
+
+void TextLabel::EmitAsyncHeightForWidthComputedSignal(float width, float height)
+{
+  Dali::Toolkit::TextLabel handle(GetOwner());
+  Extents                  padding;
+  padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+  mAsyncHeightForWidthComputedSignal.Emit(handle, width, height + (padding.top + padding.bottom));
+}
+
 void TextLabel::OnAccessibilityStatusChanged()
 {
   CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
@@ -1395,14 +1779,25 @@ void TextLabel::OnAccessibilityStatusChanged()
 TextLabel::TextLabel(ControlBehaviour additionalBehaviour)
 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT | additionalBehaviour)),
   mLocale(std::string()),
+  mSize(),
   mRenderingBackend(DEFAULT_RENDERING_BACKEND),
+  mAsyncLineCount(0),
   mTextUpdateNeeded(false),
-  mLastAutoScrollEnabled(false)
+  mLastAutoScrollEnabled(false),
+  mControlBackgroundEnabeld(true),
+  mIsAsyncRenderNeeded(false),
+  mIsSizeChanged(false),
+  mIsManualRender(false),
+  mIsManualRendered(false),
+  mManualRendered(false)
 {
 }
 
 TextLabel::~TextLabel()
 {
+  // This prevents access to the async text interface until the visual is actually destroyed.
+  TextVisual::SetAsyncTextInterface(mVisual, nullptr);
+  DiscardTextLabelVisual(mVisual);
 }
 
 Vector<Vector2> TextLabel::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
@@ -1449,6 +1844,9 @@ void TextLabel::SetTextFitArray(const bool enable, std::vector<Toolkit::DevelTex
   }
   mController->SetTextFitArrayEnabled(enable);
   mController->SetTextFitArray(fitOptions);
+
+  RequestTextRelayout();
+  mIsAsyncRenderNeeded = true;
 }
 
 std::vector<Toolkit::DevelTextLabel::FitOption>& TextLabel::GetTextFitArray()
@@ -1481,6 +1879,134 @@ bool TextLabel::IsRemoveBackInset() const
   return mController->IsRemoveBackInset();
 }
 
+void TextLabel::EnableControlBackground(const bool enable)
+{
+  // Avoid function calls if there is no change.
+  if(mControlBackgroundEnabeld != enable)
+  {
+    mControlBackgroundEnabeld = enable;
+    DevelControl::EnableVisual(*this, Toolkit::Control::Property::BACKGROUND, enable);
+  }
+}
+
+void TextLabel::RequestAsyncNaturalSize()
+{
+  Actor                       self = Self();
+  Extents                     padding;
+  Size                        contentSize     = Size::ZERO;
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
+
+  AsyncTextParameters parameters = GetAsyncTextParameters(Async::COMPUTE_NATURAL_SIZE, contentSize, padding, layoutDirection);
+  TextVisual::RequestAsyncSizeComputation(mVisual, parameters);
+}
+
+void TextLabel::RequestAsyncHeightForWidth(float width)
+{
+  Actor                       self = Self();
+  Extents                     padding;
+  Size                        contentSize(width, 0.0f);
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
+
+  AsyncTextParameters parameters = GetAsyncTextParameters(Async::COMPUTE_HEIGHT_FOR_WIDTH, contentSize, padding, layoutDirection);
+  TextVisual::RequestAsyncSizeComputation(mVisual, parameters);
+}
+
+void TextLabel::RequestAsyncRenderWithFixedSize(float width, float height)
+{
+  DALI_LOG_RELEASE_INFO("Request size : %f, %f\n", width, height);
+
+  if(mController->GetRenderMode() == DevelTextLabel::Render::SYNC)
+  {
+    DALI_LOG_WARNING("Render mode is sync, return\n");
+    return;
+  }
+
+  Actor   self = Self();
+  Extents padding;
+  padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+
+  float contentWidth  = std::max(width - (padding.start + padding.end), 0.0f);
+  float contentHeight = std::max(height - (padding.top + padding.bottom), 0.0f);
+  Size  contentSize(contentWidth, contentHeight);
+
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
+  if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
+  {
+    std::swap(padding.start, padding.end);
+  }
+
+  AsyncTextParameters parameters = GetAsyncTextParameters(Async::RENDER_FIXED_SIZE, contentSize, padding, layoutDirection);
+  parameters.manualRender        = true;
+
+  mIsManualRender      = TextVisual::UpdateAsyncRenderer(mVisual, parameters);
+  mTextUpdateNeeded    = false;
+  mIsAsyncRenderNeeded = false;
+}
+
+void TextLabel::RequestAsyncRenderWithFixedWidth(float width, float heightConstraint)
+{
+  DALI_LOG_RELEASE_INFO("Request width : %f, height constraint : %f\n", width, heightConstraint);
+
+  if(mController->GetRenderMode() == DevelTextLabel::Render::SYNC)
+  {
+    DALI_LOG_WARNING("Render mode is sync, return\n");
+    return;
+  }
+
+  Actor   self = Self();
+  Extents padding;
+  padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+
+  float contentWidth            = std::max(width - (padding.start + padding.end), 0.0f);
+  float contentHeightConstraint = std::max(heightConstraint - (padding.top + padding.bottom), 0.0f);
+  Size  contentSize(contentWidth, contentHeightConstraint);
+
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
+  if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
+  {
+    std::swap(padding.start, padding.end);
+  }
+
+  AsyncTextParameters parameters = GetAsyncTextParameters(Async::RENDER_FIXED_WIDTH, contentSize, padding, layoutDirection);
+  parameters.manualRender        = true;
+
+  mIsManualRender      = TextVisual::UpdateAsyncRenderer(mVisual, parameters);
+  mTextUpdateNeeded    = false;
+  mIsAsyncRenderNeeded = false;
+}
+
+void TextLabel::RequestAsyncRenderWithConstraint(float widthConstraint, float heightConstraint)
+{
+  DALI_LOG_RELEASE_INFO("Request constraint : %f, %f\n", widthConstraint, heightConstraint);
+
+  if(mController->GetRenderMode() == DevelTextLabel::Render::SYNC)
+  {
+    DALI_LOG_WARNING("Render mode is sync, return\n");
+    return;
+  }
+
+  Actor   self = Self();
+  Extents padding;
+  padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+
+  float contentWidthConstraint  = std::max(widthConstraint - (padding.start + padding.end), 0.0f);
+  float contentHeightConstraint = std::max(heightConstraint - (padding.top + padding.bottom), 0.0f);
+  Size  contentSize(contentWidthConstraint, contentHeightConstraint);
+
+  Dali::LayoutDirection::Type layoutDirection = mController->GetLayoutDirection(self);
+  if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
+  {
+    std::swap(padding.start, padding.end);
+  }
+
+  AsyncTextParameters parameters = GetAsyncTextParameters(Async::RENDER_CONSTRAINT, contentSize, padding, layoutDirection);
+  parameters.manualRender        = true;
+
+  mIsManualRender      = TextVisual::UpdateAsyncRenderer(mVisual, parameters);
+  mTextUpdateNeeded    = false;
+  mIsAsyncRenderNeeded = false;
+}
+
 std::string TextLabel::TextLabelAccessible::GetNameRaw() const
 {
   return GetWholeText();
index 5730cc5..ddd6e48 100644 (file)
@@ -28,6 +28,7 @@
 #include <dali-toolkit/devel-api/text/spanned.h>
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
 #include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
 #include <dali-toolkit/internal/text/controller/text-controller.h>
 #include <dali-toolkit/internal/text/rendering/text-renderer.h>
 #include <dali-toolkit/internal/text/text-anchor-control-interface.h>
@@ -46,7 +47,7 @@ namespace Internal
 /**
  * @brief A control which renders a short text string.
  */
-class TextLabel : public Control, public Text::ControlInterface, public Text::ScrollerInterface, public Text::AnchorControlInterface
+class TextLabel : public Control, public Text::ControlInterface, public Text::ScrollerInterface, public Text::AnchorControlInterface, public Text::AsyncTextInterface
 {
 public:
   /**
@@ -86,6 +87,21 @@ public:
   DevelTextLabel::TextFitChangedSignalType& TextFitChangedSignal();
 
   /**
+   * @copydoc Dali::Toollkit::TextLabel::AsyncTextRenderedSignal()
+   */
+  DevelTextLabel::AsyncTextRenderedSignalType& AsyncTextRenderedSignal();
+
+  /**
+   * @copydoc Dali::Toollkit::TextLabel::AsyncNaturalSizeComputedSignal()
+   */
+  DevelTextLabel::AsyncNaturalSizeComputedSignalType& AsyncNaturalSizeComputedSignal();
+
+  /**
+   * @copydoc Dali::Toollkit::TextLabel::AsyncHeightForWidthComputedSignal()
+   */
+  DevelTextLabel::AsyncHeightForWidthComputedSignalType& AsyncHeightForWidthComputedSignal();
+
+  /**
    * Connects a callback function with the object's signals.
    * @param[in] object The object providing the signal.
    * @param[in] tracker Used to disconnect the signal.
@@ -226,6 +242,57 @@ public:
    */
   bool IsRemoveBackInset() const;
 
+  /**
+   * @brief Enable control's background
+   *
+   * @param[in] enable Whether to enable the background of control.
+   */
+  void EnableControlBackground(const bool enable);
+
+  /**
+   * @brief A method that requests asynchronous rendering of text with a fixed size.
+   *
+   * @param[in] width The width of text to render.
+   * @param[in] height The height of text to render.
+   */
+  void RequestAsyncRenderWithFixedSize(float width, float height);
+
+  /**
+   * @brief Requests asynchronous text rendering with a fixed width.
+   * The height is determined by the content of the text when rendered with the given width.
+   * The result will be the same as the height returned by GetHeightForWidth.
+   * If the heightConstraint is given, the maximum height will be the heightConstraint.
+   *
+   * @param[in] width The width of text to render.
+   * @param[in] heightConstraint The maximum available height of text to render.
+   */
+  void RequestAsyncRenderWithFixedWidth(float width, float heightConstraint);
+
+  /**
+   * @brief Requests asynchronous rendering with the maximum available width using the given widthConstraint.
+   *
+   * If the width of the text content is smaller than the widthConstraint, the width will be determined by the width of the text.
+   * If the width of the text content is larger than the widthConstraint, the width will be determined by the widthConstraint.
+   * The height is determined by the content of the text when rendered with the given width.
+   * In this case, the result will be the same as the height returned by GetHeightForWidth.
+   * If the heightConstraint is given, the maximum height will be the heightConstraint.
+   *
+   * @param[in] widthConstraint The maximum available width of text to render.
+   */
+  void RequestAsyncRenderWithConstraint(float widthConstraint, float heightConstraint);
+
+  /**
+   * @brief Requests asynchronous text natural size computation.
+   */
+  void RequestAsyncNaturalSize();
+
+  /**
+   * @brief Requests asynchronous computation of the height of the text based on the given width.
+   * @param[in] width The width of text to compute.
+   */
+  void RequestAsyncHeightForWidth(float width);
+
+
 private: // From Control
   /**
    * @copydoc Control::OnInitialize()
@@ -296,6 +363,29 @@ public: // From AnchorControlInterface
    */
   void AnchorClicked(const std::string& href) override;
 
+private: // from AsyncTextInterface
+
+  /**
+   * @copydoc Text::AsyncTextInterface::AsyncSetupAutoScroll()
+   */
+  void AsyncSetupAutoScroll(Text::AsyncTextRenderInfo renderInfo) override;
+
+  /**
+   * @copydoc Text::AsyncTextInterface::AsyncTextFitChanged()
+   */
+  void AsyncTextFitChanged(float pointSize) override;
+
+  /**
+   * @copydoc Text::AsyncTextInterface::AsyncLoadComplete()
+   */
+  void AsyncLoadComplete(Text::AsyncTextRenderInfo renderInfo);
+
+  /**
+   * @copydoc Text::AsyncTextInterface::AsyncSizeComputed()
+   */
+  void AsyncSizeComputed(Text::AsyncTextRenderInfo renderInfo);
+
+
 private: // Implementation
   /**
    * Construct a new TextLabel.
@@ -315,6 +405,18 @@ private:
   TextLabel& operator=(const TextLabel& rhs);
 
   /**
+   * @brief Get the AsyncTextParameters
+   * All properties of the text label needed to render the text are stored and returned in the parameter.
+   *
+   * @param[in] requestType Type to request asynchronous computation.
+   * @param[in] contentSize The size of the text content requested by relayout excluding padding.
+   * @param[in] padding The size of the label's padding.
+   * @param[in] layoutDirection The layout direction.
+   * @return The parameters for async text render.
+   */
+  Text::AsyncTextParameters GetAsyncTextParameters(const Text::Async::RequestType requestType, const Vector2& contentSize, const Extents& padding, const Dali::LayoutDirection::Type layoutDirection);
+
+  /**
    * @brief Set up Autoscrolling
    */
   void SetUpAutoScrolling();
@@ -349,6 +451,22 @@ private:
    * @brief Emits TextFitChanged signal.
    */
   void EmitTextFitChangedSignal();
+
+  /**
+   * @brief Emits AsyncTextRendered signal.
+   */
+  void EmitAsyncTextRenderedSignal(float width, float height);
+
+  /**
+   * @brief Emits AsyncNaturalSizeComputed signal.
+   */
+  void EmitAsyncNaturalSizeComputedSignal(float width, float height);
+
+  /**
+   * @brief Emits AsyncHeightForWidthComputed signal.
+   */
+  void EmitAsyncHeightForWidthComputedSignal(float width, float height);
+
   void OnAccessibilityStatusChanged();
 
 private: // Data
@@ -362,12 +480,25 @@ private: // Data
   // Signals
   Toolkit::DevelTextLabel::AnchorClickedSignalType  mAnchorClickedSignal;
   Toolkit::DevelTextLabel::TextFitChangedSignalType mTextFitChangedSignal;
+  Toolkit::DevelTextLabel::AsyncTextRenderedSignalType mAsyncTextRenderedSignal;
+  Toolkit::DevelTextLabel::AsyncNaturalSizeComputedSignalType mAsyncNaturalSizeComputedSignal;
+  Toolkit::DevelTextLabel::AsyncHeightForWidthComputedSignalType mAsyncHeightForWidthComputedSignal;
+
 
   std::string mLocale;
+  Vector2     mSize;
 
   int  mRenderingBackend;
-  bool mTextUpdateNeeded : 1;
-  bool mLastAutoScrollEnabled : 1;
+  int  mAsyncLineCount;
+  bool mTextUpdateNeeded         : 1;
+  bool mLastAutoScrollEnabled    : 1;
+  bool mControlBackgroundEnabeld : 1;
+
+  bool mIsAsyncRenderNeeded : 1; // true if a render request is required in ASYNC_AUTO mode, otherwise false.
+  bool mIsSizeChanged       : 1; // whether the size has been changed or not.
+  bool mIsManualRender      : 1; // whether an async manual render has been requested, returns false when completed.
+  bool mIsManualRendered    : 1; // whether an async manual render has been completed, returns false on the next relayout.
+  bool mManualRendered      : 1;
 
 protected:
   /**
index 50d7d3d..8bc9151 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ namespace
 #define GET_LOCALE_TEXT(string) dgettext("dali-toolkit", string)
 #endif
 
-const char*   TEXT_SELECTION_POPUP_BUTTON_STYLE_NAME("TextSelectionPopupButton");
+const char*         TEXT_SELECTION_POPUP_BUTTON_STYLE_NAME("TextSelectionPopupButton");
 const Dali::Vector4 DEFAULT_OPTION_PRESSED_COLOR(Dali::Vector4(0.24f, 0.72f, 0.8f, 1.0f));
 const float         DEFAULT_OPTION_PRESSED_CORNER_RADIUS = 0.0f;
 const Dali::Vector4 DEFAULT_LABEL_PADDING(Dali::Vector4(24.0f, 24.0f, 14.0f, 14.0f));
@@ -105,31 +105,31 @@ BaseHandle Create()
 
 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::TextSelectionPopup, Toolkit::Control, Create);
 
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupMaxSize",              VECTOR2, POPUP_MAX_SIZE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupMinSize",              VECTOR2, POPUP_MIN_SIZE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionMaxSize",             VECTOR2, OPTION_MAX_SIZE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionMinSize",             VECTOR2, OPTION_MIN_SIZE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionDividerSize",         VECTOR2, OPTION_DIVIDER_SIZE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionDividerPadding",      VECTOR4, OPTION_DIVIDER_PADDING)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupClipboardButtonImage", STRING,  POPUP_CLIPBOARD_BUTTON_ICON_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupCutButtonImage",       STRING,  POPUP_CUT_BUTTON_ICON_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupCopyButtonImage",      STRING,  POPUP_COPY_BUTTON_ICON_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPasteButtonImage",     STRING,  POPUP_PASTE_BUTTON_ICON_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupSelectButtonImage",    STRING,  POPUP_SELECT_BUTTON_ICON_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupSelectAllButtonImage", STRING,  POPUP_SELECT_ALL_BUTTON_ICON_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupDividerColor",         VECTOR4, POPUP_DIVIDER_COLOR)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupIconColor",            VECTOR4, POPUP_ICON_COLOR)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedColor",         VECTOR4, POPUP_PRESSED_COLOR)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedCornerRadius",  FLOAT,   POPUP_PRESSED_CORNER_RADIUS)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedImage",         STRING,  POPUP_PRESSED_IMAGE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupFadeInDuration",       FLOAT,   POPUP_FADE_IN_DURATION)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupFadeOutDuration",      FLOAT,   POPUP_FADE_OUT_DURATION)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "backgroundBorder",          MAP,     BACKGROUND_BORDER)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "background",                MAP,     BACKGROUND)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelMinimumSize",          VECTOR2, LABEL_MINIMUM_SIZE)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelPadding",              VECTOR4, LABEL_PADDING)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelTextVisual",           MAP,     LABEL_TEXT_VISUAL)
-DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "enableScrollBar",           BOOLEAN, ENABLE_SCROLL_BAR)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupMaxSize", VECTOR2, POPUP_MAX_SIZE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupMinSize", VECTOR2, POPUP_MIN_SIZE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionMaxSize", VECTOR2, OPTION_MAX_SIZE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionMinSize", VECTOR2, OPTION_MIN_SIZE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionDividerSize", VECTOR2, OPTION_DIVIDER_SIZE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionDividerPadding", VECTOR4, OPTION_DIVIDER_PADDING)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupClipboardButtonImage", STRING, POPUP_CLIPBOARD_BUTTON_ICON_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupCutButtonImage", STRING, POPUP_CUT_BUTTON_ICON_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupCopyButtonImage", STRING, POPUP_COPY_BUTTON_ICON_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPasteButtonImage", STRING, POPUP_PASTE_BUTTON_ICON_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupSelectButtonImage", STRING, POPUP_SELECT_BUTTON_ICON_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupSelectAllButtonImage", STRING, POPUP_SELECT_ALL_BUTTON_ICON_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupDividerColor", VECTOR4, POPUP_DIVIDER_COLOR)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupIconColor", VECTOR4, POPUP_ICON_COLOR)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedColor", VECTOR4, POPUP_PRESSED_COLOR)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedCornerRadius", FLOAT, POPUP_PRESSED_CORNER_RADIUS)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedImage", STRING, POPUP_PRESSED_IMAGE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupFadeInDuration", FLOAT, POPUP_FADE_IN_DURATION)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupFadeOutDuration", FLOAT, POPUP_FADE_OUT_DURATION)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "backgroundBorder", MAP, BACKGROUND_BORDER)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "background", MAP, BACKGROUND)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelMinimumSize", VECTOR2, LABEL_MINIMUM_SIZE)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelPadding", VECTOR4, LABEL_PADDING)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelTextVisual", MAP, LABEL_TEXT_VISUAL)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "enableScrollBar", BOOLEAN, ENABLE_SCROLL_BAR)
 
 DALI_TYPE_REGISTRATION_END()
 
@@ -164,7 +164,7 @@ void TextSelectionPopup::SetProperty(BaseObject* object, Property::Index index,
 
 Property::Value TextSelectionPopup::GetProperty(BaseObject* object, Property::Index index)
 {
-  Property::Value value;
+  Property::Value             value;
   Toolkit::TextSelectionPopup selectionPopup = Toolkit::TextSelectionPopup::DownCast(Dali::BaseHandle(object));
 
   if(selectionPopup)
@@ -208,7 +208,7 @@ void TextSelectionPopup::GetProperties(Property::Map& properties)
   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_COLOR, mPressedColor);
   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_CORNER_RADIUS, mPressedCornerRadius);
 
-  Property::Map backgroundMap;
+  Property::Map         backgroundMap;
   Toolkit::Visual::Base backgroundVisual = DevelControl::GetVisual(*this, Toolkit::Control::Property::BACKGROUND);
   if(backgroundVisual)
   {
@@ -216,7 +216,7 @@ void TextSelectionPopup::GetProperties(Property::Map& properties)
   }
   map.Insert(Toolkit::TextSelectionPopup::Property::BACKGROUND, backgroundMap);
 
-  Property::Map borderMap;
+  Property::Map         borderMap;
   Toolkit::Visual::Base borderVisual = DevelControl::GetVisual(*this, Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER);
   if(borderVisual)
   {
@@ -281,20 +281,6 @@ void TextSelectionPopup::OnInitialize()
   self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::DIALOG);
 }
 
-DevelControl::ControlAccessible* TextSelectionPopup::CreateAccessibleObject()
-{
-  return new TextSelectionPopupAccessible(Self());
-}
-
-Dali::Accessibility::States TextSelectionPopup::TextSelectionPopupAccessible::CalculateStates()
-{
-  auto states = ControlAccessible::CalculateStates();
-
-  states[Dali::Accessibility::State::MODAL] = true;
-
-  return states;
-}
-
 void TextSelectionPopup::HideAnimationFinished(Animation& animation)
 {
   Actor self = Self();
index b3739d6..d2e6a30 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_TEXT_SELECTION_POPUP_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -150,11 +150,6 @@ private: // From Control
    */
   void OnInitialize() override;
 
-  /**
-   * @copydoc Toolkit::Internal::Control::CreateAccessibleObject()
-   */
-  DevelControl::ControlAccessible* CreateAccessibleObject() override;
-
 private: // Implementation
   void HideAnimationFinished(Animation& animation);
 
@@ -291,7 +286,6 @@ private: // Implementation
    */
   void CreateBackground(Property::Map& propertyMap);
 
-
   /**
    * Construct a new TextField.
    */
@@ -305,17 +299,6 @@ private: // Implementation
 protected:
   struct PropertyHandler;
 
-  class TextSelectionPopupAccessible : public DevelControl::ControlAccessible
-  {
-  public:
-    using DevelControl::ControlAccessible::ControlAccessible;
-
-    /**
-     * @copydoc DevelControl::ControlAccessible::CalculateStates()
-     */
-    Dali::Accessibility::States CalculateStates() override;
-  };
-
 private:
   // Undefined copy constructor and assignment operators
   TextSelectionPopup(const TextSelectionPopup&);
index 5fa13de..8524950 100644 (file)
@@ -518,11 +518,15 @@ void VideoView::OnSceneConnection(int depth)
     SetWindowSurfaceTarget();
   }
 
+  DALI_LOG_RELEASE_INFO("Calls mVideoPlayer.SceneConnection()\n");
+  mVideoPlayer.SceneConnection();
   Control::OnSceneConnection(depth);
 }
 
 void VideoView::OnSceneDisconnection()
 {
+  DALI_LOG_RELEASE_INFO("Calls mVideoPlayer.SceneDisconnection()\n");
+  mVideoPlayer.SceneDisconnection();
   Control::OnSceneDisconnection();
 }
 
@@ -530,7 +534,11 @@ void VideoView::OnSizeSet(const Vector3& targetSize)
 {
   if(mIsUnderlay && mSyncMode == Dali::VideoSyncMode::ENABLED)
   {
-    SetFrameRenderCallback();
+    // TODO: SR Video shell's designed is completed,
+    // it will be re-designed and implemented.
+    // Until it is completed, the below code will be commented.
+
+    //SetFrameRenderCallback();
     mVideoPlayer.StartSynchronization();
   }
   Control::OnSizeSet(targetSize);
@@ -732,7 +740,11 @@ void VideoView::UpdateDisplayArea(Dali::PropertyNotification& source)
 {
   // If mSyncMode is enabled, Video player's size and poistion is updated in Video player's constraint.
   // Because video view and player should be work syncronization.
-  if(!mIsUnderlay || mSyncMode == Dali::VideoSyncMode::ENABLED)
+
+  // TODO: SR Video shell's designed is completed,
+  // it will be re-designed and implemented.
+  // Until it is completed, the below code will be commented.
+  if(!mIsUnderlay /* || mSyncMode == Dali::VideoSyncMode::ENABLED */)
   {
     return;
   }
@@ -819,7 +831,11 @@ Any VideoView::GetMediaPlayer()
 void VideoView::OnAnimationFinished(Animation& animation)
 {
   // send desync
-  SetFrameRenderCallback();
+  // TODO: SR Video shell's designed is completed,
+  // it will be re-designed and implemented.
+  // Until it is completed, the below code will be commented.
+
+  //SetFrameRenderCallback();
 }
 
 void VideoView::OnWindowResized(Dali::Window winHandle, Dali::Window::WindowSize size)
index 9edac2d..ff4665e 100644 (file)
@@ -36,6 +36,7 @@
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/scripting/enum-helper.h>
 #include <dali/devel-api/scripting/scripting.h>
+#include <dali/integration-api/debug.h>
 #include <dali/public-api/adaptor-framework/native-image-source.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/public-api/object/type-registry.h>
@@ -51,6 +52,7 @@
 #include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali-toolkit/public-api/image-loader/image.h>
 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
+#include <dali/integration-api/debug.h>
 
 #include <functional>
 #include <memory>
@@ -164,7 +166,9 @@ WebView::WebView(const std::string& locale, const std::string& timezoneId)
   mKeyEventsEnabled(true),
   mVisualChangeRequired(false),
   mScreenshotCapturedCallback{nullptr},
-  mFrameRenderedCallback{nullptr}
+  mFrameRenderedCallback{nullptr},
+  mCornerRadius(Vector4::ZERO),
+  mCornerRadiusPolicy(1.0f)
 {
   mWebEngine = Dali::WebEngine::New();
 
@@ -188,7 +192,9 @@ WebView::WebView(uint32_t argc, char** argv)
   mKeyEventsEnabled(true),
   mVisualChangeRequired(false),
   mScreenshotCapturedCallback{nullptr},
-  mFrameRenderedCallback{nullptr}
+  mFrameRenderedCallback{nullptr},
+  mCornerRadius(Vector4::ZERO),
+  mCornerRadiusPolicy(1.0f)
 {
   mWebEngine = Dali::WebEngine::New();
 
@@ -278,13 +284,10 @@ void WebView::OnInitialize()
   Actor self = Self();
 
   self.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
-  self.SetProperty(DevelActor::Property::TOUCH_FOCUSABLE, true);
   self.TouchedSignal().Connect(this, &WebView::OnTouchEvent);
   self.HoveredSignal().Connect(this, &WebView::OnHoverEvent);
   self.WheelEventSignal().Connect(this, &WebView::OnWheelEvent);
-  Dali::DevelActor::VisibilityChangedSignal(self).Connect(this, &WebView::OnVisibilityChanged);
-
-  mWebViewVisibleState |= WebViewVisibleStateFlag::SELF_SHOW;
+  self.InheritedVisibilityChangedSignal().Connect(this, &WebView::OnInheritedVisibilityChanged);
 
   mPositionUpdateNotification = self.AddPropertyNotification(Actor::Property::WORLD_POSITION, StepCondition(1.0f, 1.0f));
   mSizeUpdateNotification     = self.AddPropertyNotification(Actor::Property::SIZE, StepCondition(1.0f, 1.0f));
@@ -293,6 +296,21 @@ void WebView::OnInitialize()
   mSizeUpdateNotification.NotifySignal().Connect(this, &WebView::OnDisplayAreaUpdated);
   mScaleUpdateNotification.NotifySignal().Connect(this, &WebView::OnDisplayAreaUpdated);
 
+  // Create WebVisual for WebView
+  Property::Map propertyMap;
+  propertyMap.Insert(Dali::Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::COLOR);
+  propertyMap.Insert(Dali::Toolkit::Visual::Property::MIX_COLOR, Color::TRANSPARENT);
+  Toolkit::Visual::Base webVisual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap);
+  if(webVisual)
+  {
+    Dali::Toolkit::DevelControl::RegisterVisual(*this, Toolkit::WebView::Property::URL, webVisual);
+  }
+  else
+  {
+    DALI_LOG_ERROR("fail to create webVisual for CornerRadius");
+    Dali::Toolkit::DevelControl::UnregisterVisual(*this, Toolkit::WebView::Property::URL);
+  }
+
   if(mWebEngine)
   {
     mWebEngine.RegisterFrameRenderedCallback(std::bind(&WebView::OnFrameRendered, this));
@@ -320,6 +338,14 @@ void WebView::OnRelayout(const Vector2& size, RelayoutContainer& container)
   SetDisplayArea(displayArea);
 }
 
+void WebView::ChangeOrientation(int orientation)
+{
+  if(mWebEngine)
+  {
+    mWebEngine.ChangeOrientation(orientation);
+  }
+}
+
 Dali::Toolkit::WebSettings* WebView::GetSettings() const
 {
   return mWebSettings.get();
@@ -506,6 +532,14 @@ void WebView::AddJavaScriptMessageHandler(const std::string& exposedObjectName,
   }
 }
 
+void WebView::AddJavaScriptEntireMessageHandler(const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptEntireMessageHandlerCallback handler)
+{
+  if(mWebEngine)
+  {
+    mWebEngine.AddJavaScriptEntireMessageHandler(exposedObjectName, std::move(handler));
+  }
+}
+
 void WebView::RegisterJavaScriptAlertCallback(Dali::WebEnginePlugin::JavaScriptAlertCallback callback)
 {
   if(mWebEngine)
@@ -647,6 +681,14 @@ bool WebView::CheckVideoPlayingAsynchronously(Dali::WebEnginePlugin::VideoPlayin
   return mWebEngine ? mWebEngine.CheckVideoPlayingAsynchronously(std::move(callback)) : false;
 }
 
+void WebView::ExitFullscreen()
+{
+  if(mWebEngine)
+  {
+    mWebEngine.ExitFullscreen();
+  }
+}
+
 void WebView::RegisterGeolocationPermissionCallback(Dali::WebEnginePlugin::GeolocationPermissionCallback callback)
 {
   if(mWebEngine)
@@ -783,6 +825,14 @@ void WebView::RegisterNavigationPolicyDecidedCallback(Dali::WebEnginePlugin::Web
   }
 }
 
+void WebView::RegisterNewWindowPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback callback)
+{
+  if(mWebEngine)
+  {
+    mWebEngine.RegisterNewWindowPolicyDecidedCallback(callback);
+  }
+}
+
 void WebView::RegisterNewWindowCreatedCallback(Dali::WebEnginePlugin::WebEngineNewWindowCreatedCallback callback)
 {
   if(mWebEngine)
@@ -831,6 +881,30 @@ void WebView::RegisterContextMenuHiddenCallback(Dali::WebEnginePlugin::WebEngine
   }
 }
 
+void WebView::RegisterFullscreenEnteredCallback(Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback callback)
+{
+  if(mWebEngine)
+  {
+    mWebEngine.RegisterFullscreenEnteredCallback(callback);
+  }
+}
+
+void WebView::RegisterFullscreenExitedCallback(Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback callback)
+{
+  if(mWebEngine)
+  {
+    mWebEngine.RegisterFullscreenExitedCallback(callback);
+  }
+}
+
+void WebView::RegisterTextFoundCallback(Dali::WebEnginePlugin::WebEngineTextFoundCallback callback)
+{
+  if(mWebEngine)
+  {
+    mWebEngine.RegisterTextFoundCallback(callback);
+  }
+}
+
 void WebView::GetPlainTextAsynchronously(Dali::WebEnginePlugin::PlainTextReceivedCallback callback)
 {
   if(mWebEngine)
@@ -846,8 +920,33 @@ void WebView::OnFrameRendered()
     mFrameRenderedCallback();
   }
 
-  // Make sure that mVisual is created only if required.
-  if(mVisualChangeRequired || !mVisual)
+  // Make sure that mVisual is created only once.
+  if (mVisual)
+    return;
+
+  // Get webVisual for checking corner radius
+  Toolkit::Visual::Base webVisual = Dali::Toolkit::DevelControl::GetVisual(*this, Toolkit::WebView::Property::URL);
+  Property::Map webMap;
+  webVisual.CreatePropertyMap(webMap);
+  Property::Value* cornerRadiusValue =  webMap.Find(Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS);
+  if(cornerRadiusValue)
+  {
+    mCornerRadius = cornerRadiusValue->Get<Vector4>();
+  }
+  Property::Value* cornerRadiusValuePolicy =  webMap.Find(Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY);
+  if(cornerRadiusValuePolicy)
+  {
+    mCornerRadiusPolicy = cornerRadiusValuePolicy->Get<int>();
+  }
+
+  Dali::Toolkit::ImageUrl nativeImageUrl = Dali::Toolkit::Image::GenerateUrl(mWebEngine.GetNativeImageSource());
+  Property::Map propertyMap;
+  propertyMap.Insert(Dali::Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::IMAGE);
+  propertyMap.Insert(Dali::Toolkit::ImageVisual::Property::URL, nativeImageUrl.GetUrl());
+  propertyMap.Insert(Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS, mCornerRadius);
+  propertyMap.Insert(Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, mCornerRadiusPolicy);
+  mVisual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap);
+  if(mVisual)
   {
     // Reset flag
     mVisualChangeRequired = false;
@@ -886,45 +985,9 @@ void WebView::OnDisplayAreaUpdated(Dali::PropertyNotification& /*source*/)
   SetDisplayArea(displayArea);
 }
 
-void WebView::OnVisibilityChanged(Actor actor, bool isVisible, Dali::DevelActor::VisibilityChange::Type type)
+void WebView::OnInheritedVisibilityChanged(Actor actor, bool isVisible)
 {
-  if(type == Dali::DevelActor::VisibilityChange::Type::SELF)
-  {
-    if(isVisible)
-    {
-      mWebViewVisibleState |= WebViewVisibleStateFlag::SELF_SHOW;
-    }
-    else
-    {
-      mWebViewVisibleState &= ~WebViewVisibleStateFlag::SELF_SHOW;
-    }
-  }
-  else if(type == Dali::DevelActor::VisibilityChange::Type::PARENT)
-  {
-    if(isVisible)
-    {
-      mWebViewVisibleState |= WebViewVisibleStateFlag::PARENT_SHOW;
-      // TODO : We should consider double-hide called from parent
-    }
-    else
-    {
-      mWebViewVisibleState &= ~WebViewVisibleStateFlag::PARENT_SHOW;
-    }
-  }
-  ApplyVisibilityCheck();
-}
-
-void WebView::OnWindowVisibilityChanged(Window window, bool visible)
-{
-  if(visible)
-  {
-    mWebViewVisibleState |= WebViewVisibleStateFlag::WINDOW_SHOW;
-  }
-  else
-  {
-    mWebViewVisibleState &= ~WebViewVisibleStateFlag::WINDOW_SHOW;
-  }
-  ApplyVisibilityCheck();
+  SetVisibility(isVisible);
 }
 
 void WebView::OnScreenshotCaptured(Dali::PixelData pixel)
@@ -963,41 +1026,12 @@ void WebView::SetDisplayArea(const Dali::Rect<int32_t>& displayArea)
 
 void WebView::OnSceneConnection(int depth)
 {
-  mWebViewVisibleState |= WebViewVisibleStateFlag::SCENE_ON;
-  mWebViewVisibleState |= WebViewVisibleStateFlag::PARENT_SHOW;
-  // TODO : We should consider already hided parent
-  Window window = DevelWindow::Get(Self());
-  if(window)
-  {
-    // Hold the weak handle of the placement window.
-    mPlacementWindow = window;
-    if(window.IsVisible())
-    {
-      mWebViewVisibleState |= WebViewVisibleStateFlag::WINDOW_SHOW;
-    }
-    else
-    {
-      mWebViewVisibleState &= ~WebViewVisibleStateFlag::WINDOW_SHOW;
-    }
-    DevelWindow::VisibilityChangedSignal(window).Connect(this, &WebView::OnWindowVisibilityChanged);
-  }
-  ApplyVisibilityCheck();
   Control::OnSceneConnection(depth);
   EnableBlendMode(!mVideoHoleEnabled);
 }
 
 void WebView::OnSceneDisconnection()
 {
-  mWebViewVisibleState &= ~WebViewVisibleStateFlag::SCENE_ON;
-  mWebViewVisibleState &= ~WebViewVisibleStateFlag::WINDOW_SHOW;
-  mWebViewVisibleState &= ~WebViewVisibleStateFlag::PARENT_SHOW;
-  Window window = mPlacementWindow.GetHandle();
-  if(window)
-  {
-    DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &WebView::OnWindowVisibilityChanged);
-    mPlacementWindow.Reset();
-  }
-  ApplyVisibilityCheck();
   Control::OnSceneDisconnection();
 }
 
@@ -1419,11 +1453,6 @@ bool WebView::SetVisibility(bool visible)
   return mWebEngine ? mWebEngine.SetVisibility(visible) : false;
 }
 
-void WebView::ApplyVisibilityCheck()
-{
-  SetVisibility(mWebViewVisibleState == WebViewVisibleStateFlag::VISIBLE);
-}
-
 WebView::WebViewAccessible::WebViewAccessible(Dali::Actor self, Dali::WebEngine& webEngine)
 : ControlAccessible(self),
   mRemoteChild{},
@@ -1458,11 +1487,17 @@ Dali::Accessibility::Attributes WebView::WebViewAccessible::GetAttributes() cons
 
 void WebView::WebViewAccessible::DoGetChildren(std::vector<Dali::Accessibility::Accessible*>& children)
 {
+  if(Dali::Accessibility::IsUp() && !mRemoteChild.GetAddress())
+  {
+    DALI_LOG_DEBUG_INFO("Try setting address as it has not not been set on initialize.\n");
+    SetRemoteChildAddress(mWebEngine.GetAccessibilityAddress());
+  }
+
   if(mRemoteChild.GetAddress())
   {
     // DoGetChildren is called at most once per every OnChildrenChanged.
-    // We have only one OnChildrenChanged in this case, so EmbedAtkSocket will be called only once.
-    Accessibility::Bridge::GetCurrentBridge()->EmbedAtkSocket(GetAddress(), mRemoteChild.GetAddress());
+    // We have only one OnChildrenChanged in this case, so EmbedSocket will be called only once.
+    Accessibility::Bridge::GetCurrentBridge()->EmbedSocket(GetAddress(), mRemoteChild.GetAddress());
     children.push_back(&mRemoteChild);
   }
 }
index 6415a8b..3a7ee88 100644 (file)
@@ -88,6 +88,11 @@ public:
   static Dali::WebEngineCookieManager* GetCookieManager();
 
   /**
+   * @copydoc Dali::Toolkit::WebView::ChangeOrientation()
+   */
+  void ChangeOrientation(int orientation);
+
+  /**
    * @copydoc Dali::Toolkit::WebView::GetSettings()
    */
   Dali::Toolkit::WebSettings* GetSettings() const;
@@ -223,6 +228,11 @@ public:
   void AddJavaScriptMessageHandler(const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptMessageHandlerCallback handler);
 
   /**
+   * @copydoc Dali::Toolkit::WebView::AddJavaScriptEntireMessageHandler()
+   */
+  void AddJavaScriptEntireMessageHandler(const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptEntireMessageHandlerCallback handler);
+
+  /**
    * @copydoc Dali::Toolkit::WebView::RegisterJavaScriptAlertCallback()
    */
   void RegisterJavaScriptAlertCallback(Dali::WebEnginePlugin::JavaScriptAlertCallback callback);
@@ -313,6 +323,11 @@ public:
   bool CheckVideoPlayingAsynchronously(Dali::WebEnginePlugin::VideoPlayingCallback callback);
 
   /**
+   * @copydoc Dali::Toolkit::WebView::ExitFullscreen()
+   */
+  void ExitFullscreen();
+
+  /**
    * @copydoc Dali::Toolkit::WebView::RegisterGeolocationPermissionCallback()
    */
   void RegisterGeolocationPermissionCallback(Dali::WebEnginePlugin::GeolocationPermissionCallback callback);
@@ -378,6 +393,11 @@ public:
   void RegisterNavigationPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNavigationPolicyDecidedCallback callback);
 
   /**
+   * @copydoc Dali::Toolkit::WebView::RegisterNewWindowPolicyDecidedCallback()
+   */
+  void RegisterNewWindowPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNewWindowPolicyDecidedCallback callback);
+
+  /**
    * @copydoc Dali::Toolkit::WebView::RegisterNewWindowCreatedCallback()
    */
   void RegisterNewWindowCreatedCallback(Dali::WebEnginePlugin::WebEngineNewWindowCreatedCallback callback);
@@ -408,6 +428,21 @@ public:
   void RegisterContextMenuHiddenCallback(Dali::WebEnginePlugin::WebEngineContextMenuHiddenCallback callback);
 
   /**
+   * @copydoc Dali::Toolkit::WebView::RegisterFullscreenEnteredCallback()
+   */
+  void RegisterFullscreenEnteredCallback(Dali::WebEnginePlugin::WebEngineFullscreenEnteredCallback callback);
+
+  /**
+   * @copydoc Dali::Toolkit::WebView::RegisterFullscreenExitedCallback()
+   */
+  void RegisterFullscreenExitedCallback(Dali::WebEnginePlugin::WebEngineFullscreenExitedCallback callback);
+
+  /**
+   * @copydoc Dali::Toolkit::WebView::RegisterTextFoundCallback()
+   */
+  void RegisterTextFoundCallback(Dali::WebEnginePlugin::WebEngineTextFoundCallback callback);
+
+  /**
    * @copydoc Dali::Toolkit::WebView::GetPlainTextAsynchronously()
    */
   void GetPlainTextAsynchronously(Dali::WebEnginePlugin::PlainTextReceivedCallback callback);
@@ -676,18 +711,10 @@ private:
 
   /**
    * @brief Callback function to be called when visibility is changed.
-   * @param[in] actor The actor, or child of actor, whose visibility has changed
+   * @param[in] actor The actor, whose inherit visibility has changed
    * @param[in] isVisible Whether the actor is now visible or not
-   * @param[in] type, Whether the actor's visible property has changed or a parent's
-   */
-  void OnVisibilityChanged(Actor actor, bool isVisible, Dali::DevelActor::VisibilityChange::Type type);
-
-  /**
-   * @brief Callback when the visibility of the window is changed.
-   * @param[in] window The window whose visibility has changed
-   * @param[in] visible Whether the window is now visible or not
    */
-  void OnWindowVisibilityChanged(Window window, bool visible);
+  void OnInheritedVisibilityChanged(Actor actor, bool isVisible);
 
   /**
    * @brief callback for screen shot captured.
@@ -703,11 +730,6 @@ private:
    */
   void SetDisplayArea(const Dali::Rect<int32_t>& displayArea);
 
-  /**
-   * @brief Apply self visibility state and send visibility chagend to web engine.
-   */
-  void ApplyVisibilityCheck();
-
 protected:
   class WebViewAccessible : public DevelControl::ControlAccessible
   {
@@ -744,19 +766,6 @@ private:
   uint32_t mLastRenderedNativeImageWidth;
   uint32_t mLastRenderedNativeImageHeight;
 
-  enum WebViewVisibleStateFlag
-  {
-    NONE        = 0,
-    SCENE_ON    = 1 << 0,
-    WINDOW_SHOW = 1 << 1,
-    SELF_SHOW   = 1 << 2,
-    PARENT_SHOW = 1 << 3,
-
-    VISIBLE = SCENE_ON | WINDOW_SHOW | SELF_SHOW | PARENT_SHOW,
-  };
-  uint32_t           mWebViewVisibleState{WebViewVisibleStateFlag::NONE}; /// Flag of web view visible.
-  WeakHandle<Window> mPlacementWindow;
-
   std::unique_ptr<Dali::Toolkit::WebSettings>        mWebSettings;
   std::unique_ptr<Dali::Toolkit::WebBackForwardList> mWebBackForwardList;
 
@@ -772,6 +781,10 @@ private:
 
   Dali::Toolkit::WebView::WebViewScreenshotCapturedCallback mScreenshotCapturedCallback;
   Dali::WebEnginePlugin::WebEngineFrameRenderedCallback     mFrameRenderedCallback;
+
+  Vector4 mCornerRadius;                     /// < Corner radius
+  float   mCornerRadiusPolicy;               /// < Corner radius policy
+  static std::unordered_map<Dali::WebEnginePlugin*, Dali::WeakHandle<Toolkit::WebView>> mPluginWebViewMap;
 };
 
 } // namespace Internal
index e9ae5b7..59d4955 100644 (file)
@@ -35,27 +35,32 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/visuals/animated-vector-image/vector-animation-thread.cpp
    ${toolkit_src_dir}/visuals/arc/arc-visual.cpp
    ${toolkit_src_dir}/visuals/border/border-visual.cpp
+   ${toolkit_src_dir}/visuals/color/color-visual-shader-factory.cpp
    ${toolkit_src_dir}/visuals/color/color-visual.cpp
+   ${toolkit_src_dir}/visuals/custom-shader-factory.cpp
+   ${toolkit_src_dir}/visuals/npatch-shader-factory.cpp
    ${toolkit_src_dir}/visuals/gradient/gradient-visual.cpp
    ${toolkit_src_dir}/visuals/gradient/gradient.cpp
    ${toolkit_src_dir}/visuals/gradient/linear-gradient.cpp
    ${toolkit_src_dir}/visuals/gradient/radial-gradient.cpp
    ${toolkit_src_dir}/visuals/animated-gradient/animated-gradient-visual.cpp
-   ${toolkit_src_dir}/visuals/image-atlas-manager.cpp
+   ${toolkit_src_dir}/visuals/image/image-atlas-manager.cpp
    ${toolkit_src_dir}/visuals/image/image-visual.cpp
+   ${toolkit_src_dir}/visuals/image/image-visual-shader-debug.cpp
+   ${toolkit_src_dir}/visuals/image/image-visual-shader-factory.cpp
+   ${toolkit_src_dir}/visuals/image/image-visual-shader-feature-builder.cpp
    ${toolkit_src_dir}/visuals/mesh/mesh-visual.cpp
-   ${toolkit_src_dir}/visuals/npatch-data.cpp
-   ${toolkit_src_dir}/visuals/npatch-loader.cpp
+   ${toolkit_src_dir}/visuals/npatch/npatch-data.cpp
+   ${toolkit_src_dir}/visuals/npatch/npatch-loader.cpp
    ${toolkit_src_dir}/visuals/npatch/npatch-visual.cpp
    ${toolkit_src_dir}/visuals/primitive/primitive-visual.cpp
+   ${toolkit_src_dir}/visuals/svg/svg-loader-observer.cpp
+   ${toolkit_src_dir}/visuals/svg/svg-loader.cpp
    ${toolkit_src_dir}/visuals/svg/svg-task.cpp
    ${toolkit_src_dir}/visuals/svg/svg-visual.cpp
-   ${toolkit_src_dir}/visuals/text-visual-shader-factory.cpp
+   ${toolkit_src_dir}/visuals/text/text-visual-shader-factory.cpp
    ${toolkit_src_dir}/visuals/text/text-visual.cpp
    ${toolkit_src_dir}/visuals/transition-data-impl.cpp
-   ${toolkit_src_dir}/visuals/image-visual-shader-debug.cpp
-   ${toolkit_src_dir}/visuals/image-visual-shader-factory.cpp
-   ${toolkit_src_dir}/visuals/image-visual-shader-feature-builder.cpp
    ${toolkit_src_dir}/visuals/visual-base-data-impl.cpp
    ${toolkit_src_dir}/visuals/visual-base-impl.cpp
    ${toolkit_src_dir}/visuals/visual-factory-cache.cpp
@@ -64,6 +69,8 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/visuals/visual-url.cpp
    ${toolkit_src_dir}/visuals/wireframe/wireframe-visual.cpp
    ${toolkit_src_dir}/controls/alignment/alignment-impl.cpp
+   ${toolkit_src_dir}/controls/render-effects/render-effect-impl.cpp
+   ${toolkit_src_dir}/controls/render-effects/blur-effect-impl.cpp
    ${toolkit_src_dir}/controls/bloom-view/bloom-view-impl.cpp
    ${toolkit_src_dir}/controls/bubble-effect/bubble-emitter-impl.cpp
    ${toolkit_src_dir}/controls/bubble-effect/bubble-renderer.cpp
@@ -127,10 +134,6 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/controls/video-view/video-view-impl.cpp
    ${toolkit_src_dir}/controls/web-view/web-view-impl.cpp
    ${toolkit_src_dir}/controls/camera-view/camera-view-impl.cpp
-   ${toolkit_src_dir}/controls/gl-view/drawable-view-impl.cpp
-   ${toolkit_src_dir}/controls/gl-view/drawable-view-native-renderer.cpp
-   ${toolkit_src_dir}/controls/gl-view/gl-view-impl.cpp
-   ${toolkit_src_dir}/controls/gl-view/gl-view-render-thread.cpp
    ${toolkit_src_dir}/accessibility-manager/accessibility-manager-impl.cpp
    ${toolkit_src_dir}/feedback/feedback-style.cpp
    ${toolkit_src_dir}/focus-manager/keyboard-focus-manager-impl.cpp
@@ -191,6 +194,14 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/text-view.cpp
    ${toolkit_src_dir}/text/text-view-interface.cpp
    ${toolkit_src_dir}/text/visual-model-impl.cpp
+   ${toolkit_src_dir}/text/async-text/async-text-loader.cpp
+   ${toolkit_src_dir}/text/async-text/async-text-loader-impl.cpp
+   ${toolkit_src_dir}/text/async-text/async-text-manager.cpp
+   ${toolkit_src_dir}/text/async-text/async-text-manager-impl.cpp
+   ${toolkit_src_dir}/text/async-text/async-text-module.cpp
+   ${toolkit_src_dir}/text/async-text/async-text-module-impl.cpp
+   ${toolkit_src_dir}/text/async-text/text-load-observer.cpp
+   ${toolkit_src_dir}/text/async-text/text-loading-task.cpp
    ${toolkit_src_dir}/text/decorator/text-decorator.cpp
    ${toolkit_src_dir}/text/controller/text-controller.cpp
    ${toolkit_src_dir}/text/controller/text-controller-background-actor.cpp
@@ -232,6 +243,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/rendering/atlas/atlas-mesh-factory.cpp
    ${toolkit_src_dir}/text/rendering/text-backend-impl.cpp
    ${toolkit_src_dir}/text/rendering/text-typesetter.cpp
+   ${toolkit_src_dir}/text/rendering/text-typesetter-impl.cpp
    ${toolkit_src_dir}/text/rendering/view-model.cpp
    ${toolkit_src_dir}/text/rendering/styles/underline-helper-functions.cpp
    ${toolkit_src_dir}/text/rendering/styles/strikethrough-helper-functions.cpp
@@ -253,6 +265,13 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/text-geometry.cpp
 )
 
+SET(toolkit_internal_egl_src_files
+   ${toolkit_src_dir}/controls/gl-view/drawable-view-impl.cpp
+   ${toolkit_src_dir}/controls/gl-view/drawable-view-native-renderer.cpp
+   ${toolkit_src_dir}/controls/gl-view/gl-view-impl.cpp
+   ${toolkit_src_dir}/controls/gl-view/gl-view-render-thread.cpp
+)
+
 SET( SOURCES ${SOURCES}
   ${toolkit_src_files}
 )
index e8a990f..b9abe8a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -206,13 +206,16 @@ bool KeyboardFocusManager::SetCurrentFocusActor(Actor actor)
 
 bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
 {
-  bool success = false;
+  bool                     success = false;
+  Integration::SceneHolder currentWindow;
 
   // Check whether the actor is in the stage and is keyboard focusable.
   if(actor &&
      actor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) &&
      actor.GetProperty<bool>(DevelActor::Property::USER_INTERACTION_ENABLED) &&
-     actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+     actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE) &&
+     (currentWindow = Integration::SceneHolder::Get(actor))) ///< Note : SceneHolder might not be valid even if actor is connected to scene.
+                                                             ///         (e.g. Adaptor Stopped, SceneHolder removed but Scene is still alive)
   {
     // If the parent's KEYBOARD_FOCUSABLE_CHILDREN is false, it cannot have focus.
     Actor parent = actor.GetParent();
@@ -234,12 +237,11 @@ bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
       return true;
     }
 
-    Integration::SceneHolder currentWindow = Integration::SceneHolder::Get(actor);
     if(currentWindow.GetRootLayer() != mCurrentFocusedWindow.GetHandle())
     {
       Layer rootLayer       = currentWindow.GetRootLayer();
       mCurrentFocusedWindow = rootLayer;
-      mCurrentWindowId = static_cast<uint32_t>(currentWindow.GetNativeId());
+      mCurrentWindowId      = static_cast<uint32_t>(currentWindow.GetNativeId());
     }
 
     if((mIsFocusIndicatorShown == SHOW) && (mEnableFocusIndicator == ENABLE))
@@ -247,6 +249,8 @@ bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
       actor.Add(GetFocusIndicatorActor());
     }
 
+    actor.OffSceneSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnSceneDisconnection);
+
     // Save the current focused actor
     mCurrentFocusActor = actor;
 
@@ -676,13 +680,12 @@ void KeyboardFocusManager::DoKeyboardEnter(Actor actor)
   }
 }
 
-void KeyboardFocusManager::ClearFocus()
+void KeyboardFocusManager::ClearFocus(Actor actor)
 {
-  ClearFocusIndicator();
-  Actor actor = GetCurrentFocusActor();
   if(actor)
   {
-    DALI_LOG_RELEASE_INFO("ClearFocus id:(%d)\n",  actor.GetProperty<int32_t>(Dali::Actor::Property::ID));
+    DALI_LOG_RELEASE_INFO("ClearFocus id:(%d)\n", actor.GetProperty<int32_t>(Dali::Actor::Property::ID));
+    actor.OffSceneSignal().Disconnect(mSlotDelegate, &KeyboardFocusManager::OnSceneDisconnection);
     // Send notification for the change of focus actor
     if(!mFocusChangedSignal.Empty())
     {
@@ -699,9 +702,8 @@ void KeyboardFocusManager::ClearFocus()
   mCurrentFocusActor.Reset();
 }
 
-void KeyboardFocusManager::ClearFocusIndicator()
+void KeyboardFocusManager::ClearFocusIndicator(Actor actor)
 {
-  Actor actor = GetCurrentFocusActor();
   if(actor)
   {
     if(mFocusIndicatorActor)
@@ -712,6 +714,13 @@ void KeyboardFocusManager::ClearFocusIndicator()
   mIsFocusIndicatorShown = (mAlwaysShowIndicator == ALWAYS_SHOW) ? SHOW : HIDE;
 }
 
+void KeyboardFocusManager::ClearFocus()
+{
+  Actor actor = GetCurrentFocusActor();
+  ClearFocusIndicator(actor);
+  ClearFocus(actor);
+}
+
 void KeyboardFocusManager::SetFocusGroupLoop(bool enabled)
 {
   mFocusGroupLoopEnabled = enabled;
@@ -1035,7 +1044,7 @@ void KeyboardFocusManager::OnTouch(const TouchEvent& touch)
     // If mClearFocusOnTouch is false, do not clear the focus indicator even if user touch the screen.
     if(mClearFocusOnTouch)
     {
-      ClearFocusIndicator();
+      ClearFocusIndicator(GetCurrentFocusActor());
     }
 
     // If KEYBOARD_FOCUSABLE and TOUCH_FOCUSABLE is true, set focus actor
@@ -1106,7 +1115,7 @@ void KeyboardFocusManager::OnWindowFocusChanged(Window window, bool focusIn)
     // Change Current Focused Window
     Layer rootLayer       = window.GetRootLayer();
     mCurrentFocusedWindow = rootLayer;
-    mCurrentWindowId = static_cast<uint32_t>(Integration::SceneHolder::Get(rootLayer).GetNativeId());
+    mCurrentWindowId      = static_cast<uint32_t>(Integration::SceneHolder::Get(rootLayer).GetNativeId());
 
     // Get Current Focused Actor from window
     Actor currentFocusedActor = GetFocusActorFromCurrentWindow();
@@ -1216,6 +1225,16 @@ void KeyboardFocusManager::ResetFocusFinderRootActor()
   mFocusFinderRootActor.Reset();
 }
 
+void KeyboardFocusManager::OnSceneDisconnection(Dali::Actor actor)
+{
+  if(actor && actor == mCurrentFocusActor.GetHandle())
+  {
+    DALI_LOG_RELEASE_INFO("ClearFocus due to actor id:(%d) removed from scene\n", actor.GetProperty<int32_t>(Dali::Actor::Property::ID));
+    ClearFocusIndicator(actor);
+    ClearFocus(actor);
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 87e73f8..1d708d2 100644 (file)
@@ -333,16 +333,29 @@ private:
    */
   bool EmitCustomWheelSignals(Actor actor, const WheelEvent& event);
 
+   /**
+   * Clear the focus actor
+   * @param[in] actor Actor to be cleared of focus
+   */
+  void ClearFocus(Actor actor);
+
   /**
    * Clear the focus indicator actor.
+   * @param[in] actor Actor to be cleared of focus indicator.
    */
-  void ClearFocusIndicator();
+  void ClearFocusIndicator(Actor actor);
 
   /**
    * Gets the current native window id
    */
   uint32_t GetCurrentWindowId() const;
 
+  /**
+   * Signal handler called when a focused actor is removed from Scene.
+   * @param[in] actor The actor removed from the scene.
+   */
+  void OnSceneDisconnection(Dali::Actor actor);
+
 private:
   // Undefined
   KeyboardFocusManager(const KeyboardFocusManager&);
index c191b88..4720ae8 100644 (file)
@@ -1,7 +1,6 @@
 INPUT mediump vec2 vPosition;
 
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 uniform mediump float thickness;
 uniform mediump float radius;
 uniform mediump float startAngle;
@@ -30,6 +29,6 @@ mediump float GetOpacity()
 
 void main()
 {
-  OUT_COLOR = vec4( mixColor, 1.0 ) * uColor;
+  OUT_COLOR = uColor;
   OUT_COLOR.a *= GetOpacity();
 }
index 8439290..a73cc94 100644 (file)
@@ -1,7 +1,6 @@
 INPUT mediump vec2 vPosition;
 
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 uniform mediump float thickness;
 uniform mediump float radius;
 uniform mediump float startAngle;
@@ -27,6 +26,6 @@ mediump float GetOpacity()
 
 void main()
 {
-  OUT_COLOR = vec4( mixColor, 1.0 ) * uColor;
+  OUT_COLOR = uColor;
   OUT_COLOR.a *= GetOpacity();
 }
\ No newline at end of file
diff --git a/dali-toolkit/internal/graphics/shaders/blur-effect.frag b/dali-toolkit/internal/graphics/shaders/blur-effect.frag
new file mode 100644 (file)
index 0000000..0f0ca14
--- /dev/null
@@ -0,0 +1,14 @@
+varying highp vec2 vTexCoord;
+uniform sampler2D sTexture;
+uniform highp vec2 uSampleOffsets[NUM_SAMPLES];
+uniform highp float uSampleWeights[NUM_SAMPLES];
+
+void main()
+{
+  highp vec4 col = vec4(0.0);
+  for (int i=0; i<NUM_SAMPLES; ++i)
+  {
+    col += (texture2D(sTexture, vTexCoord + uSampleOffsets[i]) + texture2D(sTexture, vTexCoord - uSampleOffsets[i])) * uSampleWeights[i];
+  }
+  gl_FragColor = col;
+}
index 8853321..1195092 100644 (file)
@@ -2,11 +2,10 @@ INPUT mediump float vAlpha;
 
 uniform lowp vec4 uColor;
 uniform lowp vec4 borderColor;
-uniform lowp vec3 mixColor;
 uniform mediump float borderSize;
 
 void main()
 {
-  OUT_COLOR = vec4(mixColor, 1.0) * borderColor * uColor;
+  OUT_COLOR = borderColor * uColor;
   OUT_COLOR.a *= smoothstep(0.0, 1.5, vAlpha) * smoothstep( borderSize + 1.5, borderSize, vAlpha );
 }
\ No newline at end of file
index 9718445..de9da98 100644 (file)
@@ -1,8 +1,7 @@
 uniform lowp vec4 uColor;
 uniform lowp vec4 borderColor;
-uniform lowp vec3 mixColor;
 
 void main()
 {
-  OUT_COLOR = vec4(mixColor, 1.0) * borderColor * uColor;
+  OUT_COLOR = borderColor * uColor;
 }
\ No newline at end of file
index 735f96b..482ff7f 100644 (file)
@@ -1,15 +1,20 @@
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE) || defined(IS_REQUIRED_BLUR)
 INPUT highp vec2 vPosition;
-INPUT highp vec2 vRectSize;
-INPUT highp vec2 vOptRectSize;
-INPUT highp float vAliasMargin;
+FLAT INPUT highp vec2 vRectSize;
+FLAT INPUT highp vec2 vOptRectSize;
+FLAT INPUT highp float vAliasMargin;
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-INPUT highp vec4 vCornerRadius;
+FLAT INPUT highp vec4 vCornerRadius;
+#endif
+#endif
+#if defined(IS_REQUIRED_CUTOUT)
+INPUT highp vec2 vPositionFromCenter;
+#if defined(IS_REQUIRED_ROUNDED_CORNER)
+FLAT INPUT highp vec4 vCutoutCornerRadius;
 #endif
 #endif
 
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 #ifdef IS_REQUIRED_BLUR
 uniform highp float blurRadius;
 #elif defined(IS_REQUIRED_BORDERLINE)
@@ -19,6 +24,11 @@ uniform lowp vec4 borderlineColor;
 uniform lowp vec4 uActorColor;
 #endif
 
+#if defined(IS_REQUIRED_CUTOUT)
+uniform highp vec3 uSize;
+uniform lowp int uCutoutWithCornerRadius;
+#endif
+
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE) || defined(IS_REQUIRED_BLUR)
 // Global values both rounded corner and borderline use
 
@@ -38,28 +48,34 @@ highp float gPotential = 0.0;
 highp float gPotentialRange = 0.0;
 highp float gMaxOutlinePotential = 0.0;
 highp float gMinOutlinePotential = 0.0;
+#ifdef IS_REQUIRED_BLUR
+#elif defined(IS_REQUIRED_BORDERLINE)
 highp float gMaxInlinePotential = 0.0;
 highp float gMinInlinePotential = 0.0;
+#endif
 
-void calculateCornerRadius()
+void calculateCornerRadius(highp vec4 cornerRadius, highp vec2 position)
 {
 #ifdef IS_REQUIRED_ROUNDED_CORNER
   gRadius =
   mix(
-    mix(vCornerRadius.x, vCornerRadius.y, sign(vPosition.x) * 0.5 + 0.5),
-    mix(vCornerRadius.w, vCornerRadius.z, sign(vPosition.x) * 0.5 + 0.5),
-    sign(vPosition.y) * 0.5 + 0.5
+    mix(cornerRadius.x, cornerRadius.y, sign(position.x) * 0.5 + 0.5),
+    mix(cornerRadius.w, cornerRadius.z, sign(position.x) * 0.5 + 0.5),
+    sign(position.y) * 0.5 + 0.5
   );
 #endif
 }
+void calculateFragmentPosition(highp vec2 position, highp vec2 halfSizeOfRect)
+{
+  gFragmentPosition = abs(position) - halfSizeOfRect;
+}
 
-void calculatePosition()
+void calculatePosition(highp float currentBorderlineWidth)
 {
-  gFragmentPosition = abs(vPosition) - vRectSize;
   gCenterPosition = -gRadius;
 #ifdef IS_REQUIRED_BLUR
 #elif defined(IS_REQUIRED_BORDERLINE)
-  gCenterPosition += borderlineWidth * (clamp(borderlineOffset, -1.0, 1.0) + 1.0) * 0.5;
+  gCenterPosition += currentBorderlineWidth * (clamp(borderlineOffset, -1.0, 1.0) + 1.0) * 0.5;
 #endif
   gDiff = gFragmentPosition - gCenterPosition;
 }
@@ -69,7 +85,7 @@ void calculatePotential()
   gPotential = length(max(gDiff, 0.0)) + min(0.0, max(gDiff.x, gDiff.y));
 }
 
-void setupMinMaxPotential()
+void setupMinMaxPotential(highp float currentBorderlineWidth)
 {
   gPotentialRange = vAliasMargin;
 
@@ -77,28 +93,25 @@ void setupMinMaxPotential()
   gMinOutlinePotential = gRadius - gPotentialRange;
 
 #ifdef IS_REQUIRED_BLUR
-  gMaxInlinePotential = gMaxOutlinePotential;
-  gMinInlinePotential = gMinOutlinePotential;
 #elif defined(IS_REQUIRED_BORDERLINE)
-  gMaxInlinePotential = gMaxOutlinePotential - borderlineWidth;
-  gMinInlinePotential = gMinOutlinePotential - borderlineWidth;
+  gMaxInlinePotential = gMaxOutlinePotential - currentBorderlineWidth;
+  gMinInlinePotential = gMinOutlinePotential - currentBorderlineWidth;
 #else
-  gMaxInlinePotential = gMaxOutlinePotential;
-  gMinInlinePotential = gMinOutlinePotential;
 #endif
 
   // reduce defect near edge of rounded corner.
-  gMaxOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
-  gMinOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
+  highp float heuristicEdgeCasePotential = clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, gPotentialRange);
+  gMaxOutlinePotential += heuristicEdgeCasePotential;
+  gMinOutlinePotential += heuristicEdgeCasePotential;
 }
 
-void PreprocessPotential()
+void PreprocessPotential(highp vec4 cornerRadius, highp vec2 position, highp vec2 halfSizeOfRect, highp float currentBorderlineWidth)
 {
-  calculateCornerRadius();
-  calculatePosition();
+  calculateCornerRadius(cornerRadius, position);
+  calculateFragmentPosition(position, halfSizeOfRect);
+  calculatePosition(currentBorderlineWidth);
   calculatePotential();
-
-  setupMinMaxPotential();
+  setupMinMaxPotential(currentBorderlineWidth);
 }
 #endif
 
@@ -155,7 +168,7 @@ lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
     // Manual blend operation with premultiplied colors.
     // Final alpha = borderlineColorAlpha + (1.0 - borderlineColorAlpha) * textureColor.a.
     // (Final rgb * alpha) =  borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb
-    // If preMultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
+    // If premultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
     // Else, return vec4((rgb*alpha) / alpha, alpha)
 
     lowp float finalAlpha = mix(textureColor.a, 1.0, borderlineColorAlpha);
@@ -275,7 +288,39 @@ mediump float calculateBlurOpacity()
 
 void main()
 {
-  lowp vec4 targetColor = vec4(mixColor, 1.0) * uColor;
+  lowp vec4 targetColor = uColor;
+
+#ifdef IS_REQUIRED_CUTOUT
+  mediump float discardOpacity = 1.0;
+
+  if(abs(vPositionFromCenter.x) <= uSize.x * 0.5 && abs(vPositionFromCenter.y) <= uSize.y * 0.5)
+  {
+#if defined(IS_REQUIRED_ROUNDED_CORNER)
+    if(uCutoutWithCornerRadius > 0)
+    {
+      // Ignore borderline width
+      PreprocessPotential(vCutoutCornerRadius, vPositionFromCenter, uSize.xy * 0.5, 0.0);
+
+      // Decrease potential range, to avoid alias make some hole.
+      gMinOutlinePotential -= gPotentialRange * 0.5;
+      gMaxOutlinePotential -= gPotentialRange * 0.5;
+
+      discardOpacity = smoothstep(gMinOutlinePotential, gMaxOutlinePotential, gPotential);
+    }
+    else
+    {
+      discardOpacity = 0.0;
+    }
+
+    if(discardOpacity < 0.001)
+    {
+      discard;
+    }
+#else
+    discard;
+#endif
+  }
+#endif
 
 #if defined(IS_REQUIRED_BLUR) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
   // skip most potential calculate for performance
@@ -285,24 +330,62 @@ void main()
   }
   else
   {
-    PreprocessPotential();
+    highp vec4 tempCornerRadius = vec4(0.0);
+    highp float tempBorderlineWidth = 0.0;
+#ifdef IS_REQUIRED_ROUNDED_CORNER
+    tempCornerRadius = vCornerRadius;
+#endif
+    calculateCornerRadius(tempCornerRadius, vPosition);
+    calculateFragmentPosition(vPosition, vRectSize);
 #endif
 
 #ifdef IS_REQUIRED_BLUR
-#elif defined(IS_REQUIRED_BORDERLINE)
-    targetColor = convertBorderlineColor(targetColor);
-#endif
+    calculatePosition(tempBorderlineWidth);
+    calculatePotential();
+    setupMinMaxPotential(tempBorderlineWidth);
+
     OUT_COLOR = targetColor;
 
-#ifdef IS_REQUIRED_BLUR
     mediump float opacity = calculateBlurOpacity();
     OUT_COLOR.a *= opacity;
-#elif defined(IS_REQUIRED_ROUNDED_CORNER)
-    mediump float opacity = calculateCornerOpacity();
-    OUT_COLOR.a *= opacity;
+#else
+#if defined(IS_REQUIRED_ROUNDED_CORNER) && !defined(IS_REQUIRED_BORDERLINE)
+    // skip rounded corner calculate for performance
+    if(gFragmentPosition.x + gFragmentPosition.y < -(gRadius + vAliasMargin) * 2.0)
+    {
+      // Do nothing.
+      OUT_COLOR = targetColor;
+    }
+    else
+#endif
+    {
+#if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
+#ifdef IS_REQUIRED_BORDERLINE
+      tempBorderlineWidth = borderlineWidth;
+#endif
+      calculatePosition(tempBorderlineWidth);
+      calculatePotential();
+      setupMinMaxPotential(tempBorderlineWidth);
+
+#ifdef IS_REQUIRED_BORDERLINE
+      targetColor = convertBorderlineColor(targetColor);
+#endif
+#endif
+
+      OUT_COLOR = targetColor;
+
+#ifdef IS_REQUIRED_ROUNDED_CORNER
+      mediump float opacity = calculateCornerOpacity();
+      OUT_COLOR.a *= opacity;
+#endif
+    }
 #endif
 
 #if defined(IS_REQUIRED_BLUR) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
   }
 #endif
+
+#ifdef IS_REQUIRED_CUTOUT
+  OUT_COLOR.a *= discardOpacity;
+#endif
 }
index 29b7442..0e02279 100644 (file)
@@ -1,11 +1,17 @@
 INPUT mediump vec2 aPosition;
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE) || defined(IS_REQUIRED_BLUR)
 OUTPUT highp vec2 vPosition;
-OUTPUT highp vec2 vRectSize;
-OUTPUT highp vec2 vOptRectSize;
-OUTPUT highp float vAliasMargin;
+FLAT OUTPUT highp vec2 vRectSize;
+FLAT OUTPUT highp vec2 vOptRectSize;
+FLAT OUTPUT highp float vAliasMargin;
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-OUTPUT highp vec4 vCornerRadius;
+FLAT OUTPUT highp vec4 vCornerRadius;
+#endif
+#endif
+#if defined(IS_REQUIRED_CUTOUT)
+OUTPUT highp vec2 vPositionFromCenter;
+#if defined(IS_REQUIRED_ROUNDED_CORNER)
+FLAT OUTPUT highp vec4 vCutoutCornerRadius;
 #endif
 #endif
 
@@ -92,7 +98,17 @@ vec4 ComputeVertexPosition()
 #else
   highp vec2 vPosition = aPosition * visualSize;
 #endif
+
+#if defined(IS_REQUIRED_CUTOUT)
+  vPositionFromCenter = vPosition + anchorPoint * visualSize + visualOffset + origin * uSize.xy;
+#if defined(IS_REQUIRED_ROUNDED_CORNER)
+  vCutoutCornerRadius = mix(cornerRadius * min(uSize.x, uSize.y), cornerRadius, cornerRadiusPolicy);
+  vCutoutCornerRadius = min(vCutoutCornerRadius, min(uSize.x, uSize.y) * 0.5);
+#endif
+  return vec4(vPositionFromCenter, 0.0, 1.0);
+#else
   return vec4(vPosition + anchorPoint * visualSize + visualOffset + origin * uSize.xy, 0.0, 1.0);
+#endif
 }
 
 void main()
index e29e6a1..e0d08bf 100644 (file)
@@ -1,5 +1,5 @@
-precision mediump float;
-varying mediump vec2 vTexCoord;
+precision highp float;
+varying highp vec2 vTexCoord;
 uniform sampler2D sTexture;
 uniform vec4 uColor;
 
index e7e930f..0183ee4 100644 (file)
@@ -1,12 +1,12 @@
-precision mediump float;
-attribute mediump vec2 aPosition;
-varying mediump vec2 vTexCoord;
-uniform mediump mat4 uMvpMatrix;
-uniform mediump vec3 uSize;
+precision highp float;
+attribute highp vec2 aPosition;
+varying highp vec2 vTexCoord;
+uniform highp mat4 uMvpMatrix;
+uniform highp vec3 uSize;
 
 void main()
 {
-  mediump vec4 vertexPosition = vec4(aPosition * uSize.xy, 0.0, 1.0);
+  highp vec4 vertexPosition = vec4(aPosition * uSize.xy, 0.0, 1.0);
   vTexCoord = aPosition + vec2(0.5);
   gl_Position = uMvpMatrix * vertexPosition;
 }
index 6613b0f..0f0ca14 100644 (file)
@@ -1,15 +1,14 @@
-varying mediump vec2 vTexCoord;
+varying highp vec2 vTexCoord;
 uniform sampler2D sTexture;
-uniform lowp vec4 uColor;
-uniform mediump vec2 uSampleOffsets[NUM_SAMPLES];
-uniform mediump float uSampleWeights[NUM_SAMPLES];
+uniform highp vec2 uSampleOffsets[NUM_SAMPLES];
+uniform highp float uSampleWeights[NUM_SAMPLES];
 
 void main()
 {
-  mediump vec4 col = texture2D(sTexture, vTexCoord + uSampleOffsets[0]) * uSampleWeights[0];
-  for (int i=1; i<NUM_SAMPLES; ++i)
+  highp vec4 col = vec4(0.0);
+  for (int i=0; i<NUM_SAMPLES; ++i)
   {
-    col += texture2D(sTexture, vTexCoord + uSampleOffsets[i]) * uSampleWeights[i];
+    col += (texture2D(sTexture, vTexCoord + uSampleOffsets[i]) + texture2D(sTexture, vTexCoord - uSampleOffsets[i])) * uSampleWeights[i];
   }
   gl_FragColor = col;
 }
index 0a574ce..0b1ea6b 100644 (file)
@@ -1,11 +1,11 @@
 INPUT mediump vec2 vTexCoord;
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
 INPUT highp vec2 vPosition;
-INPUT highp vec2 vRectSize;
-INPUT highp vec2 vOptRectSize;
-INPUT highp float vAliasMargin;
+FLAT INPUT highp vec2 vRectSize;
+FLAT INPUT highp vec2 vOptRectSize;
+FLAT INPUT highp float vAliasMargin;
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-INPUT highp vec4 vCornerRadius;
+FLAT INPUT highp vec4 vCornerRadius;
 #endif
 #endif
 
@@ -14,7 +14,6 @@ uniform mediump float uTextureCoordinateScaleFactor;
 
 uniform sampler2D sTexture; // sampler1D?
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 #ifdef IS_REQUIRED_BORDERLINE
 uniform highp float borderlineWidth;
 uniform highp float borderlineOffset;
@@ -41,8 +40,10 @@ highp float gPotential = 0.0;
 highp float gPotentialRange = 0.0;
 highp float gMaxOutlinePotential = 0.0;
 highp float gMinOutlinePotential = 0.0;
+#ifdef IS_REQUIRED_BORDERLINE
 highp float gMaxInlinePotential = 0.0;
 highp float gMinInlinePotential = 0.0;
+#endif
 
 void calculateCornerRadius()
 {
@@ -56,9 +57,13 @@ void calculateCornerRadius()
 #endif
 }
 
-void calculatePosition()
+void calculateFragmentPosition()
 {
   gFragmentPosition = abs(vPosition) - vRectSize;
+}
+
+void calculatePosition()
+{
   gCenterPosition = -gRadius;
 #ifdef IS_REQUIRED_BORDERLINE
   gCenterPosition += borderlineWidth * (clamp(borderlineOffset, -1.0, 1.0) + 1.0) * 0.5;
@@ -81,24 +86,22 @@ void setupMinMaxPotential()
 #ifdef IS_REQUIRED_BORDERLINE
   gMaxInlinePotential = gMaxOutlinePotential - borderlineWidth;
   gMinInlinePotential = gMinOutlinePotential - borderlineWidth;
-#else
-  gMaxInlinePotential = gMaxOutlinePotential;
-  gMinInlinePotential = gMinOutlinePotential;
 #endif
 
   // reduce defect near edge of rounded corner.
-  gMaxOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
-  gMinOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
+  highp float heuristicEdgeCasePotential = clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, gPotentialRange);
+  gMaxOutlinePotential += heuristicEdgeCasePotential;
+  gMinOutlinePotential += heuristicEdgeCasePotential;
 }
 
-void PreprocessPotential()
-{
-  calculateCornerRadius();
-  calculatePosition();
-  calculatePotential();
-
-  setupMinMaxPotential();
-}
+//void PreprocessPotential()
+//{
+//  calculateCornerRadius();
+//  calculateFragmentPosition();
+//  calculatePosition();
+//  calculatePotential();
+//  setupMinMaxPotential();
+//}
 #endif
 
 
@@ -155,7 +158,7 @@ lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
     // Manual blend operation with premultiplied colors.
     // Final alpha = borderlineColorAlpha + (1.0 - borderlineColorAlpha) * textureColor.a.
     // (Final rgb * alpha) =  borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb
-    // If preMultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
+    // If premultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
     // Else, return vec4((rgb*alpha) / alpha, alpha)
 
     lowp float finalAlpha = mix(textureColor.a, 1.0, borderlineColorAlpha);
@@ -192,9 +195,9 @@ void main()
 {
 #ifdef RADIAL
   mediump float radialTexCoord = ((length(vTexCoord) - 0.5) * uTextureCoordinateScaleFactor) + 0.5;
-  lowp vec4 textureColor = TEXTURE(sTexture, vec2(radialTexCoord, 0.5)) * vec4(mixColor, 1.0) * uColor;
+  lowp vec4 textureColor = TEXTURE(sTexture, vec2(radialTexCoord, 0.5)) * uColor;
 #else
-  lowp vec4 textureColor = TEXTURE(sTexture, vec2(vTexCoord.y, 0.5)) * vec4(mixColor, 1.0) * uColor;
+  lowp vec4 textureColor = TEXTURE(sTexture, vec2(vTexCoord.y, 0.5)) * uColor;
 #endif
 
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
@@ -205,18 +208,36 @@ void main()
   }
   else
   {
-    PreprocessPotential();
+    calculateCornerRadius();
+    calculateFragmentPosition();
+#endif
+
+#if defined(IS_REQUIRED_ROUNDED_CORNER) && !defined(IS_REQUIRED_BORDERLINE)
+    // skip length and etc potential calculation for performance
+    if(gFragmentPosition.x + gFragmentPosition.y < -(gRadius + vAliasMargin) * 2.0)
+    {
+      // Do nothing.
+      OUT_COLOR = textureColor;
+    }
+    else
+#endif
+    {
+#if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
+      calculatePosition();
+      calculatePotential();
+      setupMinMaxPotential();
 #endif
 
 #ifdef IS_REQUIRED_BORDERLINE
-    textureColor = convertBorderlineColor(textureColor);
+      textureColor = convertBorderlineColor(textureColor);
 #endif
-    OUT_COLOR = textureColor;
+      OUT_COLOR = textureColor;
 
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-    mediump float opacity = calculateCornerOpacity();
-    OUT_COLOR *= opacity;
+      mediump float opacity = calculateCornerOpacity();
+      OUT_COLOR *= opacity;
 #endif
+    }
 
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
   }
index 54f042b..95feb12 100644 (file)
@@ -2,11 +2,11 @@ INPUT mediump vec2 aPosition;
 OUTPUT mediump vec2 vTexCoord;
 #if defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
 OUTPUT highp vec2 vPosition;
-OUTPUT highp vec2 vRectSize;
-OUTPUT highp vec2 vOptRectSize;
-OUTPUT highp float vAliasMargin;
+FLAT OUTPUT highp vec2 vRectSize;
+FLAT OUTPUT highp vec2 vOptRectSize;
+FLAT OUTPUT highp float vAliasMargin;
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-OUTPUT highp vec4 vCornerRadius;
+FLAT OUTPUT highp vec4 vCornerRadius;
 #endif
 #endif
 
index bf9f787..13ebeb9 100644 (file)
@@ -1,11 +1,11 @@
 INPUT mediump vec2 vTexCoord;
 #if defined(IS_REQUIRED_DEBUG_VISUAL_SHADER) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
 INPUT highp vec2 vPosition;
-INPUT highp vec2 vRectSize;
-INPUT highp vec2 vOptRectSize;
-INPUT highp float vAliasMargin;
+FLAT INPUT highp vec2 vRectSize;
+FLAT INPUT highp vec2 vOptRectSize;
+FLAT INPUT highp float vAliasMargin;
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-INPUT highp vec4 vCornerRadius;
+FLAT INPUT highp vec4 vCornerRadius;
 #endif
 #endif
 #ifdef IS_REQUIRED_DEBUG_VISUAL_SHADER
@@ -38,8 +38,7 @@ uniform highp vec3 uScale;
 #endif
 
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
-uniform lowp float preMultipliedAlpha;
+uniform lowp float premultipliedAlpha;
 #ifdef IS_REQUIRED_BORDERLINE
 uniform highp float borderlineWidth;
 uniform highp float borderlineOffset;
@@ -78,8 +77,10 @@ highp float gPotential = 0.0;
 highp float gPotentialRange = 0.0;
 highp float gMaxOutlinePotential = 0.0;
 highp float gMinOutlinePotential = 0.0;
+#ifdef IS_REQUIRED_BORDERLINE
 highp float gMaxInlinePotential = 0.0;
 highp float gMinInlinePotential = 0.0;
+#endif
 
 void calculateCornerRadius()
 {
@@ -93,9 +94,13 @@ void calculateCornerRadius()
 #endif
 }
 
-void calculatePosition()
+void calculateFragmentPosition()
 {
   gFragmentPosition = abs(vPosition) - vRectSize;
+}
+
+void calculatePosition()
+{
   gCenterPosition = -gRadius;
 #ifdef IS_REQUIRED_BORDERLINE
   gCenterPosition += borderlineWidth * (clamp(borderlineOffset, -1.0, 1.0) + 1.0) * 0.5;
@@ -118,24 +123,22 @@ void setupMinMaxPotential()
 #ifdef IS_REQUIRED_BORDERLINE
   gMaxInlinePotential = gMaxOutlinePotential - borderlineWidth;
   gMinInlinePotential = gMinOutlinePotential - borderlineWidth;
-#else
-  gMaxInlinePotential = gMaxOutlinePotential;
-  gMinInlinePotential = gMinOutlinePotential;
 #endif
 
   // reduce defect near edge of rounded corner.
-  gMaxOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
-  gMinOutlinePotential += clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, 1.0);
+  highp float heuristicEdgeCasePotential = clamp(-min(gDiff.x, gDiff.y) / max(1.0, gRadius), 0.0, gPotentialRange);
+  gMaxOutlinePotential += heuristicEdgeCasePotential;
+  gMinOutlinePotential += heuristicEdgeCasePotential;
 }
 
-void PreprocessPotential()
-{
-  calculateCornerRadius();
-  calculatePosition();
-  calculatePotential();
-
-  setupMinMaxPotential();
-}
+//void PreprocessPotential()
+//{
+//  calculateCornerRadius();
+//  calculateFragmentPosition();
+//  calculatePosition();
+//  calculatePotential();
+//  setupMinMaxPotential();
+//}
 #endif
 
 #ifdef IS_REQUIRED_BORDERLINE
@@ -158,7 +161,7 @@ lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
 
   lowp vec3  borderlineColorRGB   = borderlineColor.rgb * uActorColor.rgb;
   lowp float borderlineColorAlpha = borderlineColor.a * uActorColor.a;
-  borderlineColorRGB *= mix(1.0, borderlineColorAlpha, preMultipliedAlpha);
+  borderlineColorRGB *= mix(1.0, borderlineColorAlpha, premultipliedAlpha);
 
   // Calculate inside of borderline when alpha is between (0.0  1.0). So we need to apply texture color.
   // If borderlineOpacity is exactly 0.0, we always use whole texture color. In this case, we don't need to run below code.
@@ -178,24 +181,24 @@ lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
       // potential is in texture range.
       lowp float textureAlphaScale = mix(1.0, 0.0, smoothstep(MinTexturelinePotential, MaxTexturelinePotential, potential));
       textureColor.a *= textureAlphaScale;
-      textureColor.rgb *= mix(textureColor.a, textureAlphaScale, preMultipliedAlpha);
+      textureColor.rgb *= mix(textureColor.a, textureAlphaScale, premultipliedAlpha);
     }
 
     borderlineColorAlpha *= borderlineOpacity;
-    borderlineColorRGB *= mix(borderlineColorAlpha, borderlineOpacity, preMultipliedAlpha);
+    borderlineColorRGB *= mix(borderlineColorAlpha, borderlineOpacity, premultipliedAlpha);
     // We use pre-multiplied color to reduce operations.
     // In here, textureColor and borderlineColorRGB is pre-multiplied color now.
 
     // Manual blend operation with premultiplied colors.
     // Final alpha = borderlineColorAlpha + (1.0 - borderlineColorAlpha) * textureColor.a.
     // (Final rgb * alpha) =  borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb
-    // If preMultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
+    // If premultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
     // Else, return vec4((rgb*alpha) / alpha, alpha)
 
     lowp float finalAlpha = mix(textureColor.a, 1.0, borderlineColorAlpha);
     lowp vec3  finalMultipliedRGB = borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb;
     // TODO : Need to find some way without division
-    return vec4(finalMultipliedRGB * mix(1.0 / finalAlpha, 1.0, preMultipliedAlpha), finalAlpha);
+    return vec4(finalMultipliedRGB * mix(1.0 / finalAlpha, 1.0, premultipliedAlpha), finalAlpha);
   }
   return mix(textureColor, vec4(borderlineColorRGB, borderlineColorAlpha), borderlineOpacity);
 }
@@ -360,7 +363,7 @@ mediump vec3 ApplyDebugMixColor(mediump vec4 originColor)
   mediump float colorRate = max(debugColorRateRed, max(debugColorRateGreen, debugColorRateBlue));
   mediump vec3 debugColor = vec3(debugColorRateRed, debugColorRateGreen, debugColorRateBlue);
 
-  debugColor *= mix(1.0, originColor.a, preMultipliedAlpha);
+  debugColor *= mix(1.0, originColor.a, premultipliedAlpha);
 
   return originColor.rgb * (1.0 - colorRate) + debugColor;
 }
@@ -378,9 +381,9 @@ void main()
 #endif
 
 #if defined(IS_REQUIRED_YUV_TO_RGB) || defined(IS_REQUIRED_UNIFIED_YUV_AND_RGB)
-  lowp vec4 textureColor = ConvertYuvToRgba(texCoord) * vec4( mixColor, 1.0 ) * uColor;
+  lowp vec4 textureColor = ConvertYuvToRgba(texCoord) * uColor;
 #else
-  lowp vec4 textureColor = TEXTURE( sTexture, texCoord ) * vec4( mixColor, 1.0 ) * uColor;
+  lowp vec4 textureColor = TEXTURE( sTexture, texCoord ) * uColor;
 #endif
 
 #ifdef IS_REQUIRED_ALPHA_MASKING
@@ -388,7 +391,7 @@ void main()
   maskTexCoord.y = mix(maskTexCoord.y, 1.0-maskTexCoord.y, uYFlipMaskTexture);
   mediump float maskAlpha = TEXTURE(sMaskTexture, maskTexCoord).a;
   textureColor.a *= maskAlpha;
-  textureColor.rgb *= mix(1.0, maskAlpha, preMultipliedAlpha);
+  textureColor.rgb *= mix(1.0, maskAlpha, premultipliedAlpha);
 #endif
 
 #if defined(IS_REQUIRED_DEBUG_VISUAL_SHADER) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
@@ -401,19 +404,37 @@ void main()
   else
 #endif
   {
-    PreprocessPotential();
+    calculateCornerRadius();
+    calculateFragmentPosition();
+#endif
+
+#if defined(IS_REQUIRED_ROUNDED_CORNER) && !defined(IS_REQUIRED_BORDERLINE) && !defined(IS_REQUIRED_DEBUG_VISUAL_SHADER)
+    // skip rounded corner calculate for performance
+    if(gFragmentPosition.x + gFragmentPosition.y < -(gRadius + vAliasMargin) * 2.0)
+    {
+      // Do nothing.
+      OUT_COLOR = textureColor;
+    }
+    else
+#endif
+    {
+#if defined(IS_REQUIRED_DEBUG_VISUAL_SHADER) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
+      calculatePosition();
+      calculatePotential();
+      setupMinMaxPotential();
 #endif
 
 #ifdef IS_REQUIRED_BORDERLINE
-    textureColor = convertBorderlineColor(textureColor);
+      textureColor = convertBorderlineColor(textureColor);
 #endif
-    OUT_COLOR = textureColor;
+      OUT_COLOR = textureColor;
 
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-    mediump float opacity = calculateCornerOpacity();
-    OUT_COLOR.a *= opacity;
-    OUT_COLOR.rgb *= mix(1.0, opacity, preMultipliedAlpha);
+      mediump float opacity = calculateCornerOpacity();
+      OUT_COLOR.a *= opacity;
+      OUT_COLOR.rgb *= mix(1.0, opacity, premultipliedAlpha);
 #endif
+    }
 
 #if defined(IS_REQUIRED_DEBUG_VISUAL_SHADER) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
   }
index 187b6dd..8885cf7 100644 (file)
@@ -2,11 +2,11 @@ INPUT mediump vec2 aPosition;
 OUTPUT mediump vec2 vTexCoord;
 #if defined(IS_REQUIRED_DEBUG_VISUAL_SHADER) || defined(IS_REQUIRED_ROUNDED_CORNER) || defined(IS_REQUIRED_BORDERLINE)
 OUTPUT highp vec2 vPosition;
-OUTPUT highp vec2 vRectSize;
-OUTPUT highp vec2 vOptRectSize;
-OUTPUT highp float vAliasMargin;
+FLAT OUTPUT highp vec2 vRectSize;
+FLAT OUTPUT highp vec2 vOptRectSize;
+FLAT OUTPUT highp float vAliasMargin;
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-OUTPUT highp vec4 vCornerRadius;
+FLAT OUTPUT highp vec4 vCornerRadius;
 #endif
 #endif
 
index 8c355ec..b482d8c 100644 (file)
@@ -6,20 +6,17 @@ uniform sampler2D sDiffuse;
 uniform sampler2D sNormal;
 uniform sampler2D sGloss;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
-uniform lowp float preMultipliedAlpha;
 
 void main()
 {
   vec4 texture = texture2D( sDiffuse, vTexCoord );
   vec3 normal = normalize( texture2D( sNormal, vTexCoord ).xyz * 2.0 - 1.0 );
   vec4 glossMap = texture2D( sGloss, vTexCoord );
-  vec4 visualMixColor = vec4( mixColor, 1.0 );
 
   float lightDiffuse = max( 0.0, dot( normal, normalize( vLightDirection ) ) );
   lightDiffuse = lightDiffuse * 0.5 + 0.5;
 
   float shininess = pow ( max ( dot ( normalize( vHalfVector ), normal ), 0.0 ), 16.0 );
 
-  gl_FragColor = vec4( texture.rgb * uColor.rgb * visualMixColor.rgb * lightDiffuse + shininess * glossMap.rgb, texture.a * uColor.a * visualMixColor.a );
+  gl_FragColor = vec4( texture.rgb * uColor.rgb * lightDiffuse + shininess * glossMap.rgb, texture.a * uColor.a );
 }
index 77f8649..fd240fe 100644 (file)
@@ -4,12 +4,9 @@ varying mediump vec3 vIllumination;
 varying mediump float vSpecular;
 uniform sampler2D sDiffuse;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
-uniform lowp float preMultipliedAlpha;
 
 void main()
 {
   vec4 texture = texture2D( sDiffuse, vTexCoord );
-  vec4 visualMixColor = vec4( mixColor, 1.0 );
-  gl_FragColor = vec4( vIllumination.rgb * texture.rgb * uColor.rgb * visualMixColor.rgb + vSpecular * 0.3, texture.a * uColor.a * visualMixColor.a );
+  gl_FragColor = vec4( vIllumination.rgb * texture.rgb * uColor.rgb + vSpecular * 0.3, texture.a * uColor.a );
 }
index 52b1e47..3f344a7 100644 (file)
@@ -1,10 +1,8 @@
 precision mediump float;
 varying mediump vec3 vIllumination;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
-uniform lowp float preMultipliedAlpha;
 
 void main()
 {
-  gl_FragColor = vec4( vIllumination.rgb * uColor.rgb, uColor.a ) * vec4( mixColor, 1.0 );
+  gl_FragColor = vec4( vIllumination.rgb * uColor.rgb, uColor.a );
 }
index 626a3ff..fbb4bdf 100644 (file)
@@ -3,8 +3,7 @@ varying mediump vec2 vMaskTexCoord;
 uniform sampler2D sTexture;
 uniform sampler2D sMask;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
-uniform lowp float preMultipliedAlpha;
+uniform lowp float premultipliedAlpha;
 uniform mediump float auxiliaryImageAlpha;
 
 void main()
@@ -19,19 +18,19 @@ void main()
 
   mediump float maskAlpha = mask.a * auxiliaryImageAlpha;
 
-  lowp vec3 preMultipliedMaskRGB = mask.rgb * mix(mask.a, 1.0, preMultipliedAlpha) * auxiliaryImageAlpha;
-  lowp vec3 preMultipliedTextureRGB = color.rgb * mix(color.a, 1.0, preMultipliedAlpha);
+  lowp vec3 preMultipliedMaskRGB = mask.rgb * mix(mask.a, 1.0, premultipliedAlpha) * auxiliaryImageAlpha;
+  lowp vec3 preMultipliedTextureRGB = color.rgb * mix(color.a, 1.0, premultipliedAlpha);
 
   // Manual blend operation with premultiplied colors.
   // Final alpha = maskAlpha + (1.0 - maskAlpha) * color.a.
   // (Final rgb * alpha) =  preMultipliedMaskRGB + (1.0 - maskAlpha) * preMultipliedTextureRGB
-  // If preMultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
+  // If premultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
   // Else, return vec4((rgb*alpha) / alpha, alpha)
 
   lowp float finalAlpha = mix(color.a, 1.0, maskAlpha);
   lowp vec3  finalMultipliedRGB = preMultipliedMaskRGB + (1.0 - maskAlpha) * preMultipliedTextureRGB;
 
   // TODO : Need to find some way without division
-  lowp vec4 finalColor = vec4(finalMultipliedRGB * mix(1.0 / finalAlpha, 1.0, preMultipliedAlpha), finalAlpha);
-  gl_FragColor = finalColor * uColor * vec4(mixColor, 1.0);
+  lowp vec4 finalColor = vec4(finalMultipliedRGB * mix(1.0 / finalAlpha, 1.0, premultipliedAlpha), finalAlpha);
+  gl_FragColor = finalColor * uColor;
 }
index acfd7f5..93f8fbc 100644 (file)
@@ -1,10 +1,8 @@
 varying mediump vec2 vTexCoord;
 uniform sampler2D sTexture;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
-uniform lowp float preMultipliedAlpha;
 
 void main()
 {
-  gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor * vec4( mixColor, 1.0 );
+  gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;
 }
index 678a852..751f527 100644 (file)
@@ -3,10 +3,8 @@
 precision mediump float;
 varying mediump vec3 vIllumination;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 
 void main()
 {
-  vec4 baseColor = vec4(mixColor, 1.0) * uColor;
-  gl_FragColor = vec4( vIllumination.rgb * baseColor.rgb, baseColor.a );
+  gl_FragColor = vec4( vIllumination.rgb * uColor.rgb, uColor.a );
 }
diff --git a/dali-toolkit/internal/graphics/shaders/render-effect.frag b/dali-toolkit/internal/graphics/shaders/render-effect.frag
new file mode 100644 (file)
index 0000000..077af76
--- /dev/null
@@ -0,0 +1,73 @@
+precision highp float;
+varying highp vec2 vTexCoord;
+varying highp vec2 vOptRectSize;
+varying highp vec4 vCornerRadius;
+
+uniform lowp vec4 uColor;
+uniform highp vec3 uSize;
+uniform sampler2D sTexture;
+
+highp float nrand(const in vec2 uv)
+{
+  const highp float a = 12.9898, b = 78.233, c = 43758.5453, pi = 3.141592653589793;
+  highp float dt = dot(uv, vec2(a, b)), sn = mod(dt, pi);
+  return fract(sin(sn) * c);
+}
+
+vec3 applyDithering( vec3 inColor )
+{
+  float rnd = nrand(vTexCoord) - 0.5;
+  inColor.rgb += rnd * 0.0039215686;
+  return inColor;
+}
+
+// from https://iquilezles.org/articles/distfunctions
+float roundedBoxSDF(vec2 PixelPositionFromCenter, vec2 RectangleEdgePositionFromCenter, float Radius) {
+    return length(max(PixelPositionFromCenter
+                      - RectangleEdgePositionFromCenter
+                      + Radius
+                      , 0.0))
+           - Radius;
+}
+
+void main()
+{
+  gl_FragColor = texture2D(sTexture, vTexCoord) * uColor;
+  gl_FragColor.rgb = applyDithering(gl_FragColor.rgb);
+
+  highp vec2 location = (vTexCoord.xy - vec2(0.5)) * uSize.xy;
+  // skip most potential calculate for performance
+  if(abs(location.x) < vOptRectSize.x && abs(location.y) < vOptRectSize.y)
+  {
+    // Do nothing.
+  }
+  else
+  {
+    float radius =
+      mix(
+        mix(vCornerRadius.x, vCornerRadius.y, sign(location.x) * 0.5 + 0.5),
+        mix(vCornerRadius.w, vCornerRadius.z, sign(location.x) * 0.5 + 0.5),
+        sign(location.y) * 0.5 + 0.5
+      );
+
+    float edgeSoftness = min(1.0, radius);
+
+    highp vec2 halfSize = uSize.xy * 0.5;
+    location = abs(location);
+
+    // For test, let we just linear function.
+    if(location.x + location.y < halfSize.x + halfSize.y - radius - 2.0 * edgeSoftness)
+    {
+      // Do nothing
+    }
+    else
+    {
+      float distance = roundedBoxSDF(location, halfSize, radius);
+
+      float smoothedAlpha = 1.0 - smoothstep(-edgeSoftness, edgeSoftness, distance);
+
+      // Premultiply alpha feature used.
+      gl_FragColor *= smoothedAlpha;
+    }
+  }
+}
diff --git a/dali-toolkit/internal/graphics/shaders/render-effect.vert b/dali-toolkit/internal/graphics/shaders/render-effect.vert
new file mode 100644 (file)
index 0000000..c46d0dd
--- /dev/null
@@ -0,0 +1,29 @@
+precision highp float;
+
+attribute highp vec2 aPosition;
+
+varying highp vec2 vTexCoord;
+varying highp vec2 vOptRectSize;
+varying highp vec4 vCornerRadius; //output
+
+uniform highp mat4 uMvpMatrix;
+uniform highp vec4 uCornerRadius; //input
+uniform lowp float uCornerRadiusPolicy;
+uniform highp vec3 uSize;
+
+void main()
+{
+  highp vec4 vertexPosition = vec4(aPosition * uSize.xy, 0.0, 1.0);
+  vTexCoord = aPosition + vec2(0.5);
+  gl_Position = uMvpMatrix * vertexPosition;
+
+  highp float minSize = min(uSize.x, uSize.y);
+  vCornerRadius = mix(uCornerRadius * minSize, uCornerRadius, uCornerRadiusPolicy);
+  vCornerRadius = min(vCornerRadius, minSize * 0.5);
+
+  vOptRectSize = uSize.xy * 0.5;
+
+  // Optimize fragment shader. 0.2929 ~= 1.0 - sqrt(0.5)
+  highp float maxRadius = max(max(vCornerRadius.x, vCornerRadius.y), max(vCornerRadius.z, vCornerRadius.w));
+  vOptRectSize -= 0.2929 * maxRadius + min(1.0, maxRadius);
+}
index 25b85a3..876628a 100644 (file)
@@ -1,7 +1,6 @@
 varying highp vec2 vTexCoord;
 uniform sampler2D sTexture;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 
 void main()
 {
@@ -10,5 +9,5 @@ void main()
 
   mediump vec4 textTexture = texture2D( sTexture, vTexCoord );
 
-  gl_FragColor = textTexture * uColor * vec4( mixColor, 1.0 );
+  gl_FragColor = textTexture * uColor;
 }
index 0fc642e..8cd690a 100644 (file)
@@ -14,7 +14,6 @@ uniform lowp float uHasMultipleTextColors;
 #endif
 uniform lowp vec4 uTextColorAnimatable;
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 
 void main()
 {
@@ -48,7 +47,7 @@ void main()
 #endif
 
   // Draw the text as overlay above the style
-  OUT_COLOR = uColor * vec4(mixColor, 1.0) * (
+  OUT_COLOR = uColor * (
 #ifdef IS_REQUIRED_OVERLAY
                    (
 #endif
index 78d8759..c75da53 100644 (file)
@@ -1,7 +1,6 @@
 uniform lowp vec4 uColor;
-uniform lowp vec3 mixColor;
 
 void main()
 {
-  gl_FragColor = uColor * vec4( mixColor, 1.0 );
+  gl_FragColor = uColor;
 }
index 1845ff7..867c797 100644 (file)
@@ -161,15 +161,12 @@ void FastTrackLoadingTask::Load()
 #ifdef TRACE_ENABLED
   uint64_t mStartTimeNanoSceonds = 0;
   uint64_t mEndTimeNanoSceonds   = 0;
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+#endif
+
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK", [&](std::ostringstream& oss) {
     mStartTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << "[u:" << mUrl.GetEllipsedUrl() << "]";
-    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("BEGIN: DALI_IMAGE_FAST_TRACK_UPLOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 
   Devel::PixelBuffer              pixelBuffer;
   std::vector<Devel::PixelBuffer> pixelBuffers;
@@ -232,11 +229,8 @@ void FastTrackLoadingTask::Load()
     }
   }
 
-#ifdef TRACE_ENABLED
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK", [&](std::ostringstream& oss) {
     mEndTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << std::fixed << std::setprecision(3);
     oss << "[";
     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
@@ -247,10 +241,7 @@ void FastTrackLoadingTask::Load()
       oss << "p:" << mPremultiplied << " ";
     }
     oss << "u:" << mUrl.GetEllipsedUrl() << "]";
-    // DALI_TRACE_END(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("END: DALI_IMAGE_FAST_TRACK_UPLOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 }
 
 void FastTrackLoadingTask::MultiplyAlpha(Dali::Devel::PixelBuffer pixelBuffer)
index 31dc6fc..b453a82 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -56,11 +56,11 @@ ImageUrl::~ImageUrl()
       auto& textureManager = GetImplementation(visualFactory).GetTextureManager();
       if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(mUrl))
       {
-        textureManager.RemoveExternalTexture(mUrl);
+        textureManager.RemoveExternalTextureByUrl(mUrl);
       }
       else if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(mUrl))
       {
-        textureManager.RemoveEncodedImageBuffer(mUrl);
+        textureManager.RemoveEncodedImageBufferByUrl(mUrl);
       }
     }
   }
index 5cbd16b..126a981 100644 (file)
@@ -174,16 +174,18 @@ void LoadingTask::Process()
 #ifdef TRACE_ENABLED
   uint64_t mStartTimeNanoSceonds = 0;
   uint64_t mEndTimeNanoSceonds   = 0;
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
-    mStartTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
-    oss << "[u:" << (!!(animatedImageLoading) ? animatedImageLoading.GetUrl() : url.GetEllipsedUrl()) << "]";
-    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_IMAGE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("BEGIN: DALI_IMAGE_LOADING_TASK %s", oss.str().c_str());
-  }
 #endif
 
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_IMAGE_LOADING_TASK", [&](std::ostringstream& oss) {
+    mStartTimeNanoSceonds = GetNanoseconds();
+    oss << "[";
+    if(dimensions.GetWidth() > 0 || dimensions.GetHeight() > 0)
+    {
+      oss << "d:" << dimensions.GetWidth() << "x" << dimensions.GetHeight() << " ";
+    }
+    oss << "u:" << (!!(animatedImageLoading) ? animatedImageLoading.GetUrl() : url.GetEllipsedUrl()) << "]";
+  });
+
   isReady = false;
   if(!isMaskTask)
   {
@@ -196,11 +198,8 @@ void LoadingTask::Process()
   MultiplyAlpha();
   isReady = true;
 
-#ifdef TRACE_ENABLED
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_IMAGE_LOADING_TASK", [&](std::ostringstream& oss) {
     mEndTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << std::fixed << std::setprecision(3);
     oss << "[";
     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
@@ -212,11 +211,12 @@ void LoadingTask::Process()
       oss << "s:" << pixelBuffers[0].GetWidth() << "x" << pixelBuffers[0].GetHeight() << " ";
       oss << "p:" << pixelBuffers[0].IsAlphaPreMultiplied() << " ";
     }
+    if(dimensions.GetWidth() > 0 || dimensions.GetHeight() > 0)
+    {
+      oss << "d:" << dimensions.GetWidth() << "x" << dimensions.GetHeight() << " ";
+    }
     oss << "u:" << (!!(animatedImageLoading) ? animatedImageLoading.GetUrl() : url.GetEllipsedUrl()) << "]";
-    // DALI_TRACE_END(gTraceFilter, "DALI_IMAGE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("END: DALI_IMAGE_LOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 }
 
 bool LoadingTask::IsReady()
diff --git a/dali-toolkit/internal/text/async-text/async-text-interface.h b/dali-toolkit/internal/text/async-text/async-text-interface.h
new file mode 100644 (file)
index 0000000..7fa605e
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_INTERFACE_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_INTERFACE_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
+
+namespace Dali
+{
+class Actor;
+
+namespace Toolkit
+{
+namespace Text
+{
+/**
+ * @brief An interface used by the text-controls which implement async text.
+ */
+class AsyncTextInterface
+{
+public:
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~AsyncTextInterface() = default;
+
+  /**
+   * @brief Setup auto scrolling.
+   *
+   * Passes the render info generated in the worker thread to the text-control.
+   */
+  virtual void AsyncSetupAutoScroll(Text::AsyncTextRenderInfo renderInfo) = 0;
+
+  /**
+   * @brief Called when the text fit changed.
+   */
+  virtual void AsyncTextFitChanged(float pointSize) = 0;
+
+  /**
+   * @brief Called when the async load complete.
+   */
+  virtual void AsyncLoadComplete(Text::AsyncTextRenderInfo renderInfo) = 0;
+
+  /**
+   * @brief Called when the async size computed.
+   */
+  virtual void AsyncSizeComputed(Text::AsyncTextRenderInfo renderInfo) = 0;
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_INTERFACE_H
diff --git a/dali-toolkit/internal/text/async-text/async-text-loader-impl.cpp b/dali-toolkit/internal/text/async-text/async-text-loader-impl.cpp
new file mode 100644 (file)
index 0000000..8974f64
--- /dev/null
@@ -0,0 +1,1412 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS  HEADER
+#include <dali-toolkit/internal/text/async-text/async-text-loader-impl.h>
+
+// EXTERNAL INCLUDES
+#include <cmath>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/pixel-data-integ.h>
+#include <dali/integration-api/trace.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/bidirectional-support.h>
+#include <dali-toolkit/internal/text/character-set-conversion.h>
+#include <dali-toolkit/internal/text/color-segmentation.h>
+#include <dali-toolkit/internal/text/hyphenator.h>
+#include <dali-toolkit/internal/text/markup-processor/markup-processor.h>
+#include <dali-toolkit/internal/text/segmentation.h>
+#include <dali-toolkit/internal/text/shaper.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace
+{
+constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
+
+const float VERTICAL_ALIGNMENT_TABLE[Text::VerticalAlignment::BOTTOM + 1] =
+{
+    0.0f, // VerticalAlignment::TOP
+    0.5f, // VerticalAlignment::CENTER
+    1.0f  // VerticalAlignment::BOTTOM
+};
+
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_ASYNC, false);
+} // namespace
+
+namespace Text
+{
+namespace Internal
+{
+const float TO_POINT_26_DOT_6 = 64.f;
+
+AsyncTextLoader::AsyncTextLoader()
+: mModule(),
+  mTextModel(),
+  mMetrics(),
+  mNumberOfCharacters(0u),
+  mFitActualEllipsis(true),
+  mIsTextDirectionRTL(false),
+  mIsTextMirrored(false),
+  mModuleClearNeeded(false),
+  mMutex()
+{
+  mModule = Dali::Toolkit::Text::AsyncTextModule::New();
+
+  mTextModel = Model::New();
+
+  mTypesetter = Text::Typesetter::New(mTextModel.Get());
+
+  // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
+  mMetrics = Metrics::New(mModule.GetFontClient());
+  mLayoutEngine.SetMetrics(mMetrics);
+}
+
+AsyncTextLoader::~AsyncTextLoader()
+{
+}
+
+void AsyncTextLoader::ClearModule()
+{
+  mModule.ClearCache();
+}
+
+void AsyncTextLoader::SetModuleClearNeeded(bool clear)
+{
+  Dali::Mutex::ScopedLock lock(mMutex);
+  mModuleClearNeeded = clear;
+}
+
+bool AsyncTextLoader::IsModuleClearNeeded()
+{
+  return mModuleClearNeeded;
+}
+
+// Worker thread
+void AsyncTextLoader::Initialize()
+{
+  ClearTextModelData();
+
+  mNumberOfCharacters = 0u;
+  mIsTextDirectionRTL = false;
+  mIsTextMirrored = false;
+
+  // Set the text properties to default
+  mTextModel->mVisualModel->SetUnderlineEnabled(false);
+  mTextModel->mVisualModel->SetUnderlineHeight(0.0f);
+  mTextModel->mVisualModel->SetOutlineWidth(0.0f);
+  mTextModel->mVisualModel->SetShadowOffset(Vector2(0.0f, 0.0f));
+  mTextModel->mVisualModel->SetStrikethroughEnabled(false);
+  mTextModel->mVisualModel->SetStrikethroughHeight(0.0f);
+}
+
+void AsyncTextLoader::ClearTextModelData()
+{
+  mTextModel->mLogicalModel->mText.Clear();
+  mTextModel->mLogicalModel->mScriptRuns.Clear();
+  mTextModel->mLogicalModel->mFontRuns.Clear();
+  mTextModel->mLogicalModel->mColorRuns.Clear();
+  mTextModel->mLogicalModel->mBackgroundColorRuns.Clear();
+  mTextModel->mLogicalModel->mLineBreakInfo.Clear();
+  mTextModel->mLogicalModel->mParagraphInfo.Clear();
+  mTextModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
+  mTextModel->mLogicalModel->mCharacterDirections.Clear();
+  mTextModel->mLogicalModel->mCharacterSpacingCharacterRuns.Clear();
+
+  mTextModel->mLogicalModel->ClearFontDescriptionRuns();
+  mTextModel->mLogicalModel->ClearStrikethroughRuns();
+  mTextModel->mLogicalModel->ClearUnderlineRuns();
+  mTextModel->mLogicalModel->ClearEmbeddedImages();
+  mTextModel->mLogicalModel->ClearAnchors();
+
+  // Free the allocated memory used to store the conversion table in the bidirectional line info run.
+  for(Vector<BidirectionalLineInfoRun>::Iterator it    = mTextModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
+                                                 endIt = mTextModel->mLogicalModel->mBidirectionalLineInfo.End();
+      it != endIt;
+      ++it)
+  {
+    BidirectionalLineInfoRun& bidiLineInfo = *it;
+
+    free(bidiLineInfo.visualToLogicalMap);
+    bidiLineInfo.visualToLogicalMap = NULL;
+  }
+  mTextModel->mLogicalModel->mBidirectionalLineInfo.Clear();
+
+  mTextModel->mVisualModel->ClearCaches();
+  mTextModel->mVisualModel->mGlyphs.Clear();
+  mTextModel->mVisualModel->mGlyphsToCharacters.Clear();
+  mTextModel->mVisualModel->mCharactersToGlyph.Clear();
+  mTextModel->mVisualModel->mCharactersPerGlyph.Clear();
+  mTextModel->mVisualModel->mGlyphsPerCharacter.Clear();
+  mTextModel->mVisualModel->mGlyphPositions.Clear();
+  mTextModel->mVisualModel->mLines.Clear();
+  mTextModel->mVisualModel->mColorIndices.Clear();
+  mTextModel->mVisualModel->mBackgroundColorIndices.Clear();
+}
+
+void AsyncTextLoader::Update(AsyncTextParameters& parameters)
+{
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_UPDATE");
+
+  if(parameters.text.empty())
+  {
+    DALI_LOG_ERROR("Text is empty\n");
+    return;
+  }
+
+  const uint8_t* utf8     = nullptr; // pointer to the first character of the text (encoded in utf8)
+  Length         textSize = 0u;      // The length of the utf8 string.
+
+  Length&           numberOfCharacters = mNumberOfCharacters;
+  Vector<Character> mirroredUtf32Characters;
+
+  Vector<Character>&                     utf32Characters     = mTextModel->mLogicalModel->mText;                       // Characters encoded in utf32.
+  Vector<LineBreakInfo>&                 lineBreakInfo       = mTextModel->mLogicalModel->mLineBreakInfo;              // The line break info.
+  Vector<ScriptRun>&                     scripts             = mTextModel->mLogicalModel->mScriptRuns;                 // Charactes's script.
+  Vector<FontDescriptionRun>&            fontDescriptionRuns = mTextModel->mLogicalModel->mFontDescriptionRuns;        // Desired font descriptions.
+  Vector<FontRun>&                       validFonts          = mTextModel->mLogicalModel->mFontRuns;                   // Validated fonts.
+  Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo   = mTextModel->mLogicalModel->mBidirectionalParagraphInfo; // The bidirectional info per paragraph.
+  Vector<ColorRun>&                      colorRuns           = mTextModel->mLogicalModel->mColorRuns;                  // colors of the text.
+
+
+  // Set the default font's description with the given text parameters.
+  TextAbstraction::FontDescription defaultFontDescription;
+  defaultFontDescription.family = parameters.fontFamily;
+  defaultFontDescription.weight = parameters.fontWeight;
+  defaultFontDescription.width = parameters.fontWidth;
+  defaultFontDescription.slant = parameters.fontSlant;
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Update visual model.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  // Store the size used to layout the text.
+  // control size is used in ElideGlyphs in ViewModel.
+  mTextModel->mVisualModel->mControlSize = Size(parameters.textWidth, parameters.textHeight);
+
+  // Update style properties.
+  mTextModel->mVisualModel->SetTextColor(parameters.textColor);
+
+  if(parameters.isUnderlineEnabled)
+  {
+    mTextModel->mVisualModel->SetUnderlineEnabled(parameters.isUnderlineEnabled);
+    mTextModel->mVisualModel->SetUnderlineType(parameters.underlineType);
+    mTextModel->mVisualModel->SetUnderlineColor(parameters.underlineColor);
+    mTextModel->mVisualModel->SetUnderlineHeight(parameters.underlineHeight);
+    mTextModel->mVisualModel->SetDashedUnderlineWidth(parameters.dashedUnderlineWidth);
+    mTextModel->mVisualModel->SetDashedUnderlineGap(parameters.dashedUnderlineGap);
+  }
+
+  if(parameters.isStrikethroughEnabled)
+  {
+    mTextModel->mVisualModel->SetStrikethroughEnabled(parameters.isStrikethroughEnabled);
+    mTextModel->mVisualModel->SetStrikethroughColor(parameters.strikethroughColor);
+    mTextModel->mVisualModel->SetStrikethroughHeight(parameters.strikethroughHeight);
+  }
+
+  const Vector2& shadowOffset = parameters.shadowOffset;
+  const float    shadowAlpha  = parameters.shadowColor.a;
+  if(fabsf(shadowAlpha) > Math::MACHINE_EPSILON_1 && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
+  {
+    mTextModel->mVisualModel->SetShadowBlurRadius(parameters.shadowBlurRadius);
+    mTextModel->mVisualModel->SetShadowColor(parameters.shadowColor);
+    mTextModel->mVisualModel->SetShadowOffset(parameters.shadowOffset);
+  }
+
+  const uint16_t outlineWidth = parameters.outlineWidth;
+  const float    outlineAlpha = parameters.outlineColor.a;
+  if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1)
+  {
+    mTextModel->mVisualModel->SetOutlineColor(parameters.outlineColor);
+    mTextModel->mVisualModel->SetOutlineWidth(parameters.outlineWidth);
+    mTextModel->mVisualModel->SetOutlineBlurRadius(parameters.outlineBlurRadius);
+    mTextModel->mVisualModel->SetOutlineOffset(parameters.outlineOffset);
+  }
+
+  mTextModel->mVisualModel->SetCutoutEnabled(parameters.cutout);
+  mTextModel->mVisualModel->SetBackgroundWithCutoutEnabled(parameters.backgroundWithCutoutEnabled);
+  mTextModel->mVisualModel->SetBackgroundColorWithCutout(parameters.backgroundColorWithCutout);
+
+  mTextModel->mRemoveFrontInset = parameters.removeFrontInset;
+  mTextModel->mRemoveBackInset  = parameters.removeBackInset;
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Process the markup string if the mark-up processor is enabled.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  MarkupProcessData markupProcessData(colorRuns,
+                                      fontDescriptionRuns,
+                                      mTextModel->mLogicalModel->mEmbeddedItems,
+                                      mTextModel->mLogicalModel->mAnchors,
+                                      mTextModel->mLogicalModel->mUnderlinedCharacterRuns,
+                                      mTextModel->mLogicalModel->mBackgroundColorRuns,
+                                      mTextModel->mLogicalModel->mStrikethroughCharacterRuns,
+                                      mTextModel->mLogicalModel->mBoundedParagraphRuns,
+                                      mTextModel->mLogicalModel->mCharacterSpacingCharacterRuns);
+
+  mTextModel->mVisualModel->SetMarkupProcessorEnabled(parameters.enableMarkup);
+
+  if(parameters.enableMarkup)
+  {
+    // TODO : Currently unable to support anchor clicked events.
+    MarkupPropertyData markupPropertyData(Color::MEDIUM_BLUE, Color::DARK_MAGENTA);
+
+    ProcessMarkupString(parameters.text, markupPropertyData, markupProcessData);
+    textSize = markupProcessData.markupProcessedText.size();
+
+    // This is a bit horrible but std::string returns a (signed) char*
+    utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
+  }
+  else
+  {
+    textSize = parameters.text.size();
+
+    // This is a bit horrible but std::string returns a (signed) char*
+    utf8 = reinterpret_cast<const uint8_t*>(parameters.text.c_str());
+  }
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Convert from utf8 to utf32
+  ////////////////////////////////////////////////////////////////////////////////
+
+  utf32Characters.Resize(textSize);
+
+  // Transform a text array encoded in utf8 into an array encoded in utf32.
+  // It returns the actual number of characters.
+  numberOfCharacters = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
+  utf32Characters.Resize(numberOfCharacters);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the Line and Word Break Info.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
+  SetLineBreakInfo(mModule.GetSegmentation(), utf32Characters, 0u, numberOfCharacters, lineBreakInfo);
+
+  // Hyphenation
+  if(parameters.lineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
+     parameters.lineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
+  {
+    CharacterIndex startIndex          = 0u;
+    CharacterIndex end                 = numberOfCharacters;
+    LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
+
+    for(CharacterIndex index = startIndex; index < end; index++)
+    {
+      CharacterIndex wordEnd = index;
+      while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
+      {
+        wordEnd++;
+      }
+
+      if((wordEnd + 1) == end) // add last char
+      {
+        wordEnd++;
+      }
+
+      Vector<bool> hyphens = GetWordHyphens(mModule.GetHyphenation(), utf32Characters.Begin() + index, wordEnd - index, nullptr);
+
+      for(CharacterIndex i = 0; i < (wordEnd - index); i++)
+      {
+        if(hyphens[i])
+        {
+          *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
+        }
+      }
+
+      index = wordEnd;
+    }
+  }
+
+  // Create the paragraph info.
+  mTextModel->mLogicalModel->CreateParagraphInfo(0u, numberOfCharacters);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the script runs.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  mModule.GetMultilanguageSupport().SetScripts(utf32Characters, 0u, numberOfCharacters, scripts);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Validate Fonts.
+  ////////////////////////////////////////////////////////////////////////////////
+
+
+  float scale = parameters.fontSizeScale;
+
+  TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * scale;
+
+  //Get the number of points per one unit of point-size
+  uint32_t numberOfPointsPerOneUnitOfPointSize = mModule.GetFontClient().GetNumberOfPointsPerOneUnitOfPointSize();
+
+  defaultPointSize = parameters.fontSize * scale * numberOfPointsPerOneUnitOfPointSize;
+
+  // Validates the fonts. If there is a character with no assigned font it sets a default one.
+  // After this call, fonts are validated.
+  mModule.GetMultilanguageSupport().ValidateFonts(mModule.GetFontClient(),
+                                                  utf32Characters,
+                                                  scripts,
+                                                  fontDescriptionRuns,
+                                                  defaultFontDescription,
+                                                  defaultPointSize,
+                                                  scale,
+                                                  0u,
+                                                  numberOfCharacters,
+                                                  validFonts);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the Bidirectional info.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  // Update the layout direction policy to text model.
+  mTextModel->mMatchLayoutDirection = parameters.layoutDirectionPolicy;
+
+  mIsTextMirrored = false;
+  const Length numberOfParagraphs = mTextModel->mLogicalModel->mParagraphInfo.Count();
+
+  bidirectionalInfo.Reserve(numberOfParagraphs);
+
+  // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
+  SetBidirectionalInfo(mModule.GetBidirectionalSupport(),
+                       utf32Characters,
+                       scripts,
+                       lineBreakInfo,
+                       0u,
+                       numberOfCharacters,
+                       bidirectionalInfo,
+                       (mTextModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
+                       parameters.layoutDirection);
+
+  if(0u != bidirectionalInfo.Count())
+  {
+    // Only set the character directions if there is right to left characters.
+    Vector<CharacterDirection>& directions = mTextModel->mLogicalModel->mCharacterDirections;
+    GetCharactersDirection(mModule.GetBidirectionalSupport(),
+                           bidirectionalInfo,
+                           numberOfCharacters,
+                           0u,
+                           numberOfCharacters,
+                           directions);
+
+    // This paragraph has right to left text. Some characters may need to be mirrored.
+    // TODO: consider if the mirrored string can be stored as well.
+
+    mIsTextMirrored = GetMirroredText(mModule.GetBidirectionalSupport(),
+                                      utf32Characters,
+                                      directions,
+                                      bidirectionalInfo,
+                                      0u,
+                                      numberOfCharacters,
+                                      mirroredUtf32Characters);
+  }
+  else
+  {
+    // There is no right to left characters. Clear the directions vector.
+    mTextModel->mLogicalModel->mCharacterDirections.Clear();
+  }
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the glyphs. Text shaping
+  ////////////////////////////////////////////////////////////////////////////////
+
+  Vector<GlyphInfo>&      glyphs                = mTextModel->mVisualModel->mGlyphs;
+  Vector<CharacterIndex>& glyphsToCharactersMap = mTextModel->mVisualModel->mGlyphsToCharacters;
+  Vector<Length>&         charactersPerGlyph    = mTextModel->mVisualModel->mCharactersPerGlyph;
+  Vector<GlyphIndex>      newParagraphGlyphs;
+  newParagraphGlyphs.Reserve(numberOfParagraphs);
+
+  const Length currentNumberOfGlyphs = glyphs.Count();
+
+  const Vector<Character>& textToShape = mIsTextMirrored ? mirroredUtf32Characters : utf32Characters;
+
+  // Shapes the text.
+  ShapeText(mModule.GetShaping(),
+            mModule.GetFontClient(),
+            textToShape,
+            lineBreakInfo,
+            scripts,
+            validFonts,
+            0u,
+            0u,
+            numberOfCharacters,
+            glyphs,
+            glyphsToCharactersMap,
+            charactersPerGlyph,
+            newParagraphGlyphs);
+
+  // Create the 'number of glyphs' per character and the glyph to character conversion tables.
+  mTextModel->mVisualModel->CreateGlyphsPerCharacterTable(0u, 0u, numberOfCharacters);
+  mTextModel->mVisualModel->CreateCharacterToGlyphTable(0u, 0u, numberOfCharacters);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the glyph's metrics.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  const Length numberOfGlyphs = static_cast<Length>(glyphs.Count()) - currentNumberOfGlyphs;
+
+  mMetrics->GetGlyphMetrics(glyphs.Begin(), numberOfGlyphs);
+
+  GlyphInfo* glyphsBuffer = glyphs.Begin();
+
+  // Update the width and advance of all new paragraph characters.
+  for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
+  {
+    const GlyphIndex index = *it;
+    GlyphInfo&       glyph = *(glyphsBuffer + index);
+
+    glyph.xBearing = 0.f;
+    glyph.width    = 0.f;
+    glyph.advance  = 0.f;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Set the color runs in glyphs.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  // Set the color runs in glyphs.
+  SetColorSegmentationInfo(mTextModel->mLogicalModel->mColorRuns,
+                           mTextModel->mVisualModel->mCharactersToGlyph,
+                           mTextModel->mVisualModel->mGlyphsPerCharacter,
+                           0u,
+                           0u,
+                           numberOfCharacters,
+                           mTextModel->mVisualModel->mColors,
+                           mTextModel->mVisualModel->mColorIndices);
+
+  // Set the background color runs in glyphs.
+  SetColorSegmentationInfo(mTextModel->mLogicalModel->mBackgroundColorRuns,
+                           mTextModel->mVisualModel->mCharactersToGlyph,
+                           mTextModel->mVisualModel->mGlyphsPerCharacter,
+                           0u,
+                           0u,
+                           numberOfCharacters,
+                           mTextModel->mVisualModel->mBackgroundColors,
+                           mTextModel->mVisualModel->mBackgroundColorIndices);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Update visual model for markup style.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  if(mTextModel->mVisualModel->IsMarkupProcessorEnabled())
+  {
+    const Vector<UnderlinedCharacterRun>&       underlinedCharacterRuns       = mTextModel->mLogicalModel->mUnderlinedCharacterRuns;
+    const Vector<StrikethroughCharacterRun>&    strikethroughCharacterRuns    = mTextModel->mLogicalModel->mStrikethroughCharacterRuns;
+    const Vector<CharacterSpacingCharacterRun>& characterSpacingCharacterRuns = mTextModel->mLogicalModel->mCharacterSpacingCharacterRuns;
+    const Vector<GlyphIndex>&                   charactersToGlyph             = mTextModel->mVisualModel->mCharactersToGlyph;
+    const Vector<Length>&                       glyphsPerCharacter            = mTextModel->mVisualModel->mGlyphsPerCharacter;
+
+
+    ////////////////////////////////////////////////////////////////////////////////
+    // Markup underline
+    ////////////////////////////////////////////////////////////////////////////////
+
+    // Should clear previous underline runs.
+    mTextModel->mVisualModel->mUnderlineRuns.Clear();
+
+    for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
+    {
+      CharacterIndex characterIndex     = it->characterRun.characterIndex;
+      Length         numberOfCharacters = it->characterRun.numberOfCharacters;
+
+      if(numberOfCharacters == 0)
+      {
+        continue;
+      }
+
+      // Create one run for all glyphs of all run's characters that has same properties
+      // This enhance performance and reduce the needed memory to store glyphs-runs
+      UnderlinedGlyphRun underlineGlyphRun;
+      underlineGlyphRun.properties = it->properties;
+      underlineGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
+      underlineGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+
+      for(Length index = 1u; index < numberOfCharacters; index++)
+      {
+        underlineGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+      }
+
+      mTextModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
+    }
+
+    // Reset flag. The updates have been applied from logical to visual.
+    mTextModel->mLogicalModel->mUnderlineRunsUpdated = false;
+
+
+    ////////////////////////////////////////////////////////////////////////////////
+    // Markup strikethrough
+    ////////////////////////////////////////////////////////////////////////////////
+
+    // Should clear previous strikethrough runs.
+    mTextModel->mVisualModel->mStrikethroughRuns.Clear();
+
+    for(Vector<StrikethroughCharacterRun>::ConstIterator it = strikethroughCharacterRuns.Begin(), endIt = strikethroughCharacterRuns.End(); it != endIt; ++it)
+    {
+      CharacterIndex characterIndex     = it->characterRun.characterIndex;
+      Length         numberOfCharacters = it->characterRun.numberOfCharacters;
+
+      if(numberOfCharacters == 0)
+      {
+        continue;
+      }
+
+      StrikethroughGlyphRun strikethroughGlyphRun;
+      strikethroughGlyphRun.properties              = it->properties;
+      strikethroughGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
+      strikethroughGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+
+      for(Length index = 1u; index < numberOfCharacters; index++)
+      {
+        strikethroughGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+      }
+
+      mTextModel->mVisualModel->mStrikethroughRuns.PushBack(strikethroughGlyphRun);
+    }
+
+    // Reset flag. The updates have been applied from logical to visual.
+    mTextModel->mLogicalModel->mStrikethroughRunsUpdated = false;
+
+
+    ////////////////////////////////////////////////////////////////////////////////
+    // Markup character spacing
+    ////////////////////////////////////////////////////////////////////////////////
+
+    // Should clear previous character spacing runs.
+    mTextModel->mVisualModel->mCharacterSpacingRuns.Clear();
+
+    for(Vector<CharacterSpacingCharacterRun>::ConstIterator it = characterSpacingCharacterRuns.Begin(), endIt = characterSpacingCharacterRuns.End(); it != endIt; ++it)
+    {
+      const CharacterIndex& characterIndex     = it->characterRun.characterIndex;
+      const Length&         numberOfCharacters = it->characterRun.numberOfCharacters;
+
+      if(numberOfCharacters == 0)
+      {
+        continue;
+      }
+
+      CharacterSpacingGlyphRun characterSpacingGlyphRun;
+      characterSpacingGlyphRun.value                   = it->value;
+      characterSpacingGlyphRun.glyphRun.glyphIndex     = charactersToGlyph[characterIndex];
+      characterSpacingGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+
+      for(Length index = 1u; index < numberOfCharacters; index++)
+      {
+        characterSpacingGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+      }
+
+      mTextModel->mVisualModel->mCharacterSpacingRuns.PushBack(characterSpacingGlyphRun);
+    }
+    mTextModel->mLogicalModel->mCharacterSpacingRunsUpdated = false;
+  }
+}
+
+Size AsyncTextLoader::Layout(AsyncTextParameters& parameters, bool& updated)
+{
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_LAYOUT");
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Layout the text.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  Length& numberOfCharacters = mNumberOfCharacters;
+
+  // Calculate the number of glyphs to layout.
+  const Vector<GlyphIndex>& charactersToGlyph        = mTextModel->mVisualModel->mCharactersToGlyph;
+  const Vector<Length>&     glyphsPerCharacter       = mTextModel->mVisualModel->mGlyphsPerCharacter;
+  const GlyphIndex* const   charactersToGlyphBuffer  = charactersToGlyph.Begin();
+  const Length* const       glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
+
+  const CharacterIndex startIndex      = 0u;
+  const CharacterIndex lastIndex       = numberOfCharacters > 0u ? numberOfCharacters - 1u : 0u;
+  const GlyphIndex     startGlyphIndex = 0u;
+
+  // Make sure the index is not out of bound
+  if(charactersToGlyph.Count() != glyphsPerCharacter.Count() ||
+     numberOfCharacters > charactersToGlyph.Count() ||
+     (lastIndex > charactersToGlyph.Count() && charactersToGlyph.Count() > 0u))
+  {
+    DALI_LOG_ERROR("Attempting to access invalid buffer\n");
+    DALI_LOG_ERROR("Current text is: %s\n", parameters.text.c_str());
+    DALI_LOG_ERROR("startIndex: %u, lastIndex: %u, requestedNumberOfCharacters: %u, charactersToGlyph.Count = %lu, glyphsPerCharacter.Count = %lu\n", startIndex, lastIndex, numberOfCharacters, charactersToGlyph.Count(), glyphsPerCharacter.Count());
+    return Size::ZERO;
+  }
+
+  const Length numberOfGlyphs      = (numberOfCharacters > 0u) ? *(charactersToGlyphBuffer + lastIndex) + *(glyphsPerCharacterBuffer + lastIndex) - startGlyphIndex : 0u;
+  const Length totalNumberOfGlyphs = mTextModel->mVisualModel->mGlyphs.Count();
+
+  if(0u == totalNumberOfGlyphs)
+  {
+    mTextModel->mVisualModel->SetLayoutSize(Size::ZERO);
+
+    // Nothing else to do if there is no glyphs.
+    DALI_LOG_RELEASE_INFO("no glyphs\n");
+    return Size::ZERO;
+  }
+
+  const Text::Layout::Engine::Type layoutType = parameters.isMultiLine ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
+  mLayoutEngine.SetLayout(layoutType);
+
+  // Set minimun line size, line spacing, relative line size.
+  mLayoutEngine.SetDefaultLineSize(parameters.minLineSize);
+  mLayoutEngine.SetDefaultLineSpacing(parameters.lineSpacing);
+  mLayoutEngine.SetRelativeLineSize(parameters.relativeLineSize);
+
+  // Set vertical line alignment.
+  mTextModel->mVerticalLineAlignment = parameters.verticalLineAlignment;
+
+  // Set character spacing.
+  mTextModel->mVisualModel->SetCharacterSpacing(parameters.characterSpacing);
+
+  // Set the layout parameters.
+  Size textLayoutArea(parameters.textWidth, parameters.textHeight);
+
+  mTextModel->mLineWrapMode          = parameters.lineWrapMode;
+  mTextModel->mIgnoreSpacesAfterText = false;
+
+  // Set the layout parameters.
+  Layout::Parameters layoutParameters(textLayoutArea, mTextModel, mModule.GetFontClient(), mModule.GetBidirectionalSupport());
+
+  // Resize the vector of positions to have the same size than the vector of glyphs.
+  Vector<Vector2>& glyphPositions = mTextModel->mVisualModel->mGlyphPositions;
+  glyphPositions.Resize(totalNumberOfGlyphs);
+
+  // The initial glyph and the number of glyphs to layout.
+  layoutParameters.startGlyphIndex        = startGlyphIndex;
+  layoutParameters.numberOfGlyphs         = numberOfGlyphs;
+  layoutParameters.startLineIndex         = 0u;
+  layoutParameters.estimatedNumberOfLines = 1u;
+  layoutParameters.interGlyphExtraAdvance = 0.f;
+
+  // Whether the last character is a new paragraph character.
+  const Character* const textBuffer = mTextModel->mLogicalModel->mText.Begin();
+  layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(textBuffer + (mTextModel->mLogicalModel->mText.Count() - 1u)));
+
+  // Update the ellipsis
+  bool ellipsisEnabled      = parameters.ellipsis;
+  mTextModel->mElideEnabled = ellipsisEnabled;
+  mTextModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
+
+  auto ellipsisPosition = parameters.ellipsisPosition;
+  mTextModel->mEllipsisPosition = ellipsisPosition;
+  mTextModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
+
+  // Update the visual model.
+  Size newLayoutSize; // The size of the text after it has been laid-out.
+  bool isAutoScrollEnabled            = parameters.isAutoScrollEnabled;
+  bool isAutoScrollMaxTextureExceeded = parameters.isAutoScrollMaxTextureExceeded;
+  bool isHiddenInputEnabled           = false;
+
+  updated = mLayoutEngine.LayoutText(layoutParameters,
+                                     newLayoutSize,
+                                     ellipsisEnabled,
+                                     isAutoScrollEnabled,
+                                     isAutoScrollMaxTextureExceeded,
+                                     isHiddenInputEnabled,
+                                     ellipsisPosition);
+
+  mIsTextDirectionRTL = false;
+
+  if(!mTextModel->mVisualModel->mLines.Empty())
+  {
+    mIsTextDirectionRTL = mTextModel->mVisualModel->mLines[0u].direction;
+  }
+
+  // Store the actual size of the text after it has been laid-out.
+  mTextModel->mVisualModel->SetLayoutSize(newLayoutSize);
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Align the text.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  mTextModel->mHorizontalAlignment = parameters.horizontalAlignment;
+
+  Vector<LineRun>& lines = mTextModel->mVisualModel->mLines; // The laid out lines.
+
+  // Calculate the horizontal offset according with the given alignment.
+  float alignmentOffset = 0.f;
+
+  // Need to align with the control's size as the text may contain lines
+  // starting either with left to right text or right to left.
+  mLayoutEngine.Align(textLayoutArea,
+                      0u,
+                      numberOfCharacters,
+                      parameters.horizontalAlignment,
+                      lines,
+                      alignmentOffset,
+                      parameters.layoutDirection,
+                      (mTextModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS));
+
+  // Calculate vertical offset.
+  Size layoutSize = mTextModel->mVisualModel->GetLayoutSize();
+
+  switch(parameters.verticalAlignment)
+  {
+    case VerticalAlignment::TOP:
+    {
+      mTextModel->mScrollPosition.y = 0.f;
+      break;
+    }
+    case VerticalAlignment::CENTER:
+    {
+      mTextModel->mScrollPosition.y = floorf(0.5f * (textLayoutArea.height - layoutSize.height));
+      break;
+    }
+    case VerticalAlignment::BOTTOM:
+    {
+      mTextModel->mScrollPosition.y = textLayoutArea.height - layoutSize.height;
+      break;
+    }
+  }
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("ControlSize : %f, %f, LayoutSize : %f, %f\n", textLayoutArea.x, textLayoutArea.y, newLayoutSize.x, newLayoutSize.y);
+  }
+#endif
+
+  return newLayoutSize;
+}
+
+AsyncTextRenderInfo AsyncTextLoader::Render(AsyncTextParameters& parameters)
+{
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_RENDER");
+
+  // render test
+  mTypesetter->SetFontClient(mModule.GetFontClient());
+
+  // Check whether it is a markup text with multiple text colors
+  const Vector4* const colorsBuffer          = mTextModel->GetColors();
+  bool                 hasMultipleTextColors = (NULL != colorsBuffer);
+
+  // Check whether the text contains any color glyph
+  bool containsColorGlyph = false;
+
+  const Text::GlyphInfo* const glyphsBuffer   = mTextModel->GetGlyphs();
+  const Text::Length           numberOfGlyphs = mTextModel->GetNumberOfGlyphs();
+  for(Text::Length glyphIndex = 0; glyphIndex < numberOfGlyphs; glyphIndex++)
+  {
+    // Retrieve the glyph's info.
+    const Text::GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
+
+    // Whether the current glyph is a color one.
+    if(mModule.GetFontClient().IsColorGlyph(glyphInfo->fontId, glyphInfo->index))
+    {
+      containsColorGlyph = true;
+      break;
+    }
+  }
+
+  // Check whether the text contains any style colors (e.g. underline color, shadow color, etc.)
+  bool           shadowEnabled = false;
+  const Vector2& shadowOffset  = mTextModel->GetShadowOffset();
+  if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
+  {
+    shadowEnabled = true;
+  }
+
+  const bool outlineEnabled              = mTextModel->GetOutlineWidth() > Math::MACHINE_EPSILON_1;
+  const bool backgroundEnabled           = mTextModel->IsBackgroundEnabled();
+  const bool markupOrSpannedText         = parameters.enableMarkup || mTextModel->IsSpannedTextPlaced();
+  const bool markupUnderlineEnabled      = markupOrSpannedText && mTextModel->IsMarkupUnderlineSet();
+  const bool markupStrikethroughEnabled  = markupOrSpannedText && mTextModel->IsMarkupStrikethroughSet();
+  const bool underlineEnabled            = mTextModel->IsUnderlineEnabled() || markupUnderlineEnabled;
+  const bool strikethroughEnabled        = mTextModel->IsStrikethroughEnabled() || markupStrikethroughEnabled;
+  const bool backgroundMarkupSet         = mTextModel->IsMarkupBackgroundColorSet();
+  const bool cutoutEnabled               = mTextModel->IsCutoutEnabled();
+  const bool backgroundWithCutoutEnabled = mTextModel->IsBackgroundWithCutoutEnabled();
+  const bool styleEnabled                = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet || cutoutEnabled || backgroundWithCutoutEnabled);
+  const bool isOverlayStyle              = underlineEnabled || strikethroughEnabled;
+
+  // Create RGBA texture if the text contains emojis or multiple text colors, otherwise L8 texture
+  Pixel::Format textPixelFormat = (containsColorGlyph || hasMultipleTextColors || cutoutEnabled) ? Pixel::RGBA8888 : Pixel::L8;
+
+  // The width is the control's width, height is the minimum height of the text.
+  // This calculated layout size determines the size of the pixel data buffer.
+  Size layoutSize = mTextModel->mVisualModel->GetLayoutSize();
+  layoutSize.x    = parameters.textWidth;
+
+  if(shadowOffset.y > Math::MACHINE_EPSILON_1)
+  {
+    layoutSize.y += shadowOffset.y;
+  }
+
+  float outlineWidth = mTextModel->GetOutlineWidth();
+  layoutSize.y += outlineWidth * 2.0f;
+  layoutSize.y = std::min(layoutSize.y, parameters.textHeight);
+
+  if(cutoutEnabled)
+  {
+    // We need to store the offset including padding and vertical alignment.
+    float xOffset = parameters.padding.start;
+    float yOffset = parameters.padding.top + std::round((parameters.textHeight - layoutSize.y) * VERTICAL_ALIGNMENT_TABLE[parameters.verticalAlignment]);
+    mTextModel->mVisualModel->SetOffsetWithCutout(Vector2(xOffset, yOffset));
+
+    // The layout size is set to the text control size including padding.
+    layoutSize.x = parameters.textWidth + (parameters.padding.start + parameters.padding.end);
+    layoutSize.y = parameters.textHeight + (parameters.padding.top + parameters.padding.bottom);
+  }
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("ControlSize : %f, %f, LayoutSize : %f, %f\n", parameters.textWidth, parameters.textHeight, layoutSize.x, layoutSize.y);
+  }
+#endif
+
+  // Check the text direction
+  Toolkit::DevelText::TextDirection::Type textDirection = mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
+
+  // Set information for creating pixel datas.
+  AsyncTextRenderInfo renderInfo;
+  renderInfo.width  = static_cast<uint32_t>(layoutSize.x);
+  renderInfo.height = static_cast<uint32_t>(layoutSize.y);
+
+  // Set the direction of text.
+  renderInfo.isTextDirectionRTL = mIsTextDirectionRTL;
+
+  Devel::PixelBuffer cutoutData;
+  if(cutoutEnabled)
+  {
+    cutoutData = mTypesetter->RenderWithPixelBuffer(layoutSize, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
+
+    // Make transparent buffer.
+    // If the cutout is enabled, a separate texture is not used for the text.
+    Devel::PixelBuffer buffer = mTypesetter->CreateFullBackgroundBuffer(1, 1, Color::TRANSPARENT);
+    renderInfo.textPixelData = Devel::PixelBuffer::Convert(buffer);
+
+    // Set the flag of cutout.
+    renderInfo.isCutout = cutoutEnabled && (cutoutData != nullptr);
+  }
+  else
+  {
+    // Create a pixel data for the text without any styles
+    renderInfo.textPixelData = mTypesetter->Render(layoutSize, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
+  }
+
+  if(styleEnabled)
+  {
+    if(renderInfo.isCutout)
+    {
+      float cutoutAlpha = mTextModel->GetDefaultColor().a;
+      renderInfo.stylePixelData = mTypesetter->RenderWithCutout(layoutSize, textDirection, cutoutData, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888, cutoutAlpha);
+    }
+    else
+    {
+      // Create RGBA pixel data for all the text styles (without the text itself)
+      renderInfo.stylePixelData = mTypesetter->Render(layoutSize, textDirection, Text::Typesetter::RENDER_NO_TEXT, false, Pixel::RGBA8888);
+    }
+  }
+  if(isOverlayStyle)
+  {
+    // Create RGBA pixel data for all the overlay styles
+    renderInfo.overlayStylePixelData = mTypesetter->Render(layoutSize, textDirection, Text::Typesetter::RENDER_OVERLAY_STYLE, false, Pixel::RGBA8888);
+  }
+  if(containsColorGlyph && !hasMultipleTextColors)
+  {
+    // Create a L8 pixel data as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
+    renderInfo.maskPixelData = mTypesetter->Render(layoutSize, textDirection, Text::Typesetter::RENDER_MASK, false, Pixel::L8);
+  }
+  if(parameters.isAutoScrollEnabled)
+  {
+    // This will be uploaded in async text interface's setup auto scroll.
+    renderInfo.autoScrollPixelData = mTypesetter->Render(layoutSize, textDirection, Text::Typesetter::RENDER_TEXT_AND_STYLES, true, Pixel::RGBA8888); // ignore the horizontal alignment
+  }
+
+  renderInfo.hasMultipleTextColors = hasMultipleTextColors;
+  renderInfo.containsColorGlyph    = containsColorGlyph;
+  renderInfo.styleEnabled          = styleEnabled;
+  renderInfo.isOverlayStyle        = isOverlayStyle;
+  renderInfo.manualRendered        = parameters.manualRender;
+  renderInfo.lineCount             = mTextModel->GetNumberOfLines();
+
+  if(cutoutEnabled)
+  {
+    renderInfo.renderedSize = Size(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
+  }
+  else
+  {
+    renderInfo.renderedSize = Size(parameters.textWidth, parameters.textHeight);
+  }
+
+  return renderInfo;
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderText(AsyncTextParameters& parameters)
+{
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_RENDER_TEXT");
+
+  if(parameters.requestType == Async::RENDER_CONSTRAINT)
+  {
+    Size textNaturalSize = ComputeNaturalSize(parameters);
+    // textWidth is widthConstraint
+    if(parameters.textWidth > textNaturalSize.width)
+    {
+      parameters.textWidth = textNaturalSize.width;
+    }
+  }
+
+  if(parameters.requestType == Async::RENDER_FIXED_WIDTH || parameters.requestType == Async::RENDER_CONSTRAINT)
+  {
+    // In case of CONSTRAINT, the natural size has already been calculated.
+    // So we can skip Initialize and Update at this stage.
+    // Only the layout is newly calculated to obtain the height.
+    bool layoutOnly = (parameters.requestType == Async::RENDER_CONSTRAINT);
+    float height = ComputeHeightForWidth(parameters, parameters.textWidth, layoutOnly);
+
+    // textHeight is heightConstraint.
+    if(parameters.textHeight < height)
+    {
+      bool layoutUpdated = false;
+      // Re-layout is required to apply new height.
+      Layout(parameters, layoutUpdated);
+    }
+    else
+    {
+      parameters.textHeight = height;
+    }
+
+    mTextModel->mVisualModel->mControlSize = Size(parameters.textWidth, parameters.textHeight);
+  }
+  else
+  {
+    Initialize();
+    Update(parameters);
+    bool layoutUpdated = false;
+    Layout(parameters, layoutUpdated);
+  }
+
+  return Render(parameters);
+}
+
+float AsyncTextLoader::ComputeHeightForWidth(AsyncTextParameters& parameters, float width, bool layoutOnly)
+{
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("ComputeHeightForWidth, width:%f, layoutOnly:%d\n", width, layoutOnly);
+  }
+#endif
+
+  float actualWidth  = parameters.textWidth;
+  float actualHeight = parameters.textHeight;
+
+  parameters.textWidth  = width;
+  parameters.textHeight = MAX_FLOAT;
+
+  if(!layoutOnly)
+  {
+    Initialize();
+    Update(parameters);
+  }
+
+  bool layoutUpdated = false;
+  Size layoutSize = Layout(parameters, layoutUpdated);
+
+  // Restore actual size.
+  parameters.textWidth  = actualWidth;
+  parameters.textHeight = actualHeight;
+  mTextModel->mVisualModel->mControlSize = Size(parameters.textWidth, parameters.textHeight);
+
+  return layoutSize.height;
+}
+
+Size AsyncTextLoader::ComputeNaturalSize(AsyncTextParameters& parameters)
+{
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("ComputeNaturalSize\n");
+  }
+#endif
+
+  float actualWidth  = parameters.textWidth;
+  float actualHeight = parameters.textHeight;
+
+  // To measure natural size, set the size of the control to the maximum.
+  parameters.textWidth  = MAX_FLOAT;
+  parameters.textHeight = MAX_FLOAT;
+
+  Initialize();
+  Update(parameters);
+  bool layoutUpdated = false;
+
+  Size naturalSize = Layout(parameters, layoutUpdated);
+
+  // Restore actual size.
+  parameters.textWidth  = actualWidth;
+  parameters.textHeight = actualHeight;
+  mTextModel->mVisualModel->mControlSize = Size(parameters.textWidth, parameters.textHeight);
+
+  return naturalSize;
+}
+
+AsyncTextRenderInfo AsyncTextLoader::GetHeightForWidth(AsyncTextParameters& parameters)
+{
+  float height = ComputeHeightForWidth(parameters, parameters.textWidth, false);
+  AsyncTextRenderInfo renderInfo;
+  renderInfo.renderedSize.width  = parameters.textWidth;
+  renderInfo.renderedSize.height = height;
+  renderInfo.requestType         = Async::COMPUTE_HEIGHT_FOR_WIDTH;
+  renderInfo.lineCount           = mTextModel->GetNumberOfLines();
+
+  return renderInfo;
+}
+
+AsyncTextRenderInfo AsyncTextLoader::GetNaturalSize(AsyncTextParameters& parameters)
+{
+  Size textNaturalSize = ComputeNaturalSize(parameters);
+  AsyncTextRenderInfo renderInfo;
+  renderInfo.renderedSize = textNaturalSize;
+  renderInfo.requestType  = Async::COMPUTE_NATURAL_SIZE;
+  renderInfo.lineCount    = mTextModel->GetNumberOfLines();
+
+  return renderInfo;
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderAutoScroll(AsyncTextParameters& parameters)
+{
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_RENDER_AUTO_SCROLL");
+
+  Size controlSize(parameters.textWidth, parameters.textHeight);
+
+  // As relayout of text may not be done at this point natural size is used to get size. Single line scrolling only.
+  Size textNaturalSize = ComputeNaturalSize(parameters);
+  textNaturalSize.width += (parameters.padding.start + parameters.padding.end);
+  textNaturalSize.height += (parameters.padding.top + parameters.padding.bottom);
+
+  if(parameters.requestType == Async::RENDER_FIXED_WIDTH || parameters.requestType == Async::RENDER_CONSTRAINT)
+  {
+    // The real height calculated during layout should be set.
+    parameters.textHeight = textNaturalSize.height - (parameters.padding.top + parameters.padding.bottom);
+    controlSize.height = parameters.textHeight;
+    mTextModel->mVisualModel->mControlSize = Size(parameters.textWidth, parameters.textHeight);
+  }
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("natural size : %f, %f, control size : %f, %f\n", textNaturalSize.x, textNaturalSize.y, controlSize.x, controlSize.y);
+  }
+#endif
+
+  // Calculate the actual gap before scrolling wraps.
+  int     textPadding = std::max(controlSize.x - textNaturalSize.x, 0.0f);
+  float   wrapGap     = std::max(parameters.autoScrollGap, textPadding);
+  Vector2 textureSize = textNaturalSize + Vector2(wrapGap, 0.0f); // Add the gap as a part of the texture.
+
+  // Calculate a size of texture for text scrolling
+  Size      verifiedSize   = textureSize;
+  const int maxTextureSize = parameters.maxTextureSize;
+
+  // If the texture size width exceed maxTextureSize, modify the visual model size and enabled the ellipsis.
+  if(verifiedSize.width > maxTextureSize)
+  {
+    verifiedSize.width = maxTextureSize;
+    if(textNaturalSize.width > maxTextureSize)
+    {
+      float actualWidth  = parameters.textWidth;
+      float actualHeight = parameters.textHeight;
+
+      parameters.textWidth  = verifiedSize.width;
+      parameters.textHeight = textNaturalSize.height;
+      parameters.isAutoScrollMaxTextureExceeded = true;
+
+      bool layoutUpdated = false;
+
+      // Re-layout is required to apply ellipsis.
+      Layout(parameters, layoutUpdated);
+
+      parameters.textWidth  = actualWidth;
+      parameters.textHeight = actualHeight;
+    }
+    wrapGap = std::max(maxTextureSize - textNaturalSize.width, 0.0f);
+  }
+
+  uint32_t actualWidth = parameters.textWidth;
+  parameters.textWidth = verifiedSize.width;
+
+  AsyncTextRenderInfo renderInfo = Render(parameters);
+
+  // Restore actual size.
+  parameters.textWidth = actualWidth;
+
+  // Store the control size and calculated wrap gap in render info.
+  renderInfo.controlSize       = controlSize;
+  renderInfo.autoScrollWrapGap = wrapGap;
+  renderInfo.renderedSize      = controlSize;
+
+  return renderInfo;
+}
+
+bool AsyncTextLoader::CheckForTextFit(AsyncTextParameters& parameters, float pointSize, const Size& allowedSize)
+{
+  parameters.fontSize = pointSize;
+
+  Initialize();
+  Update(parameters);
+  bool layoutUpdated = false;
+  Size layoutSize = Layout(parameters, layoutUpdated);
+
+  if(!layoutUpdated || layoutSize.width > allowedSize.width || layoutSize.height > allowedSize.height)
+  {
+    return false;
+  }
+  return true;
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderTextFit(AsyncTextParameters& parameters)
+{
+  if(parameters.requestType == Async::RENDER_CONSTRAINT)
+  {
+    Size textNaturalSize = ComputeNaturalSize(parameters);
+    // textWidth is widthConstraint
+    if(parameters.textWidth > textNaturalSize.width)
+    {
+      parameters.textWidth = textNaturalSize.width;
+    }
+  }
+
+  if(parameters.requestType == Async::RENDER_FIXED_WIDTH || parameters.requestType == Async::RENDER_CONSTRAINT)
+  {
+    // In case of CONSTRAINT, the natural size has already been calculated.
+    // So we can skip Initialize and Update at this stage.
+    // Only the layout is newly calculated to obtain the height.
+    bool layoutOnly = (parameters.requestType == Async::RENDER_CONSTRAINT);
+    float height = ComputeHeightForWidth(parameters, parameters.textWidth, layoutOnly);
+
+    // textHeight is heightConstraint
+    if(parameters.textHeight > height)
+    {
+      parameters.textHeight = height;
+    }
+    DALI_LOG_WARNING("TextFit requires a fixed size. Render with natural size : %f, %f\n", parameters.textWidth, parameters.textHeight);
+  }
+
+  if(parameters.isTextFitArrayEnabled)
+  {
+#ifdef TRACE_ENABLED
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+    {
+      DALI_LOG_RELEASE_INFO("AsyncTextLoader::RenderTextFit -> TextFitArray\n");
+    }
+#endif
+
+    std::vector<DevelTextLabel::FitOption> fitOptions = parameters.textFitArray;
+    int numberOfFitOptions = static_cast<int>(fitOptions.size());
+    if(numberOfFitOptions == 0)
+    {
+      DALI_LOG_ERROR("fitOptions is empty, render with default value, point size:%f, min line size:%f\n", parameters.fontSize, parameters.minLineSize);
+      fitOptions.push_back(DevelTextLabel::FitOption(parameters.fontSize, parameters.minLineSize));
+      numberOfFitOptions = 1;
+    }
+
+    mFitActualEllipsis  = parameters.ellipsis;
+    parameters.ellipsis = false;
+
+    Size allowedSize(parameters.textWidth, parameters.textHeight);
+
+    // Sort in ascending order by PointSize.
+    std::sort(fitOptions.begin(), fitOptions.end(), compareByPointSize);
+
+    // Decide whether to use binary search.
+    // If MinLineSize is not sorted in ascending order,
+    // binary search cannot guarantee that it will always find the best value.
+    bool  binarySearch    = true;
+    float prevMinLineSize = 0.0f;
+    for(DevelTextLabel::FitOption& option : fitOptions)
+    {
+      float optionMinLineSize = option.GetMinLineSize();
+      if(prevMinLineSize > optionMinLineSize)
+      {
+        binarySearch = false;
+        break;
+      }
+      prevMinLineSize = optionMinLineSize;
+    }
+
+    // Set the first FitOption(Minimum PointSize) to the best value.
+    // If the search does not find an optimal value, the minimum PointSize will be used to text fit.
+    DevelTextLabel::FitOption firstOption = fitOptions.front();
+    bool  bestSizeUpdatedLatest = false;
+    float bestPointSize         = firstOption.GetPointSize();
+    float bestMinLineSize       = firstOption.GetMinLineSize();
+
+    if(binarySearch)
+    {
+      int left = 0u;
+      int right = numberOfFitOptions - 1;
+
+      while (left <= right)
+      {
+        int mid = left + (right - left) / 2;
+        DevelTextLabel::FitOption option = fitOptions[mid];
+        float testPointSize   = option.GetPointSize();
+        float testMinLineSize = option.GetMinLineSize();
+        parameters.minLineSize = testMinLineSize;
+
+        if(CheckForTextFit(parameters, testPointSize, allowedSize))
+        {
+          bestSizeUpdatedLatest = true;
+          bestPointSize   = testPointSize;
+          bestMinLineSize = testMinLineSize;
+          left = mid + 1;
+        }
+        else
+        {
+          bestSizeUpdatedLatest = false;
+          right = mid - 1;
+        }
+      }
+    }
+    else
+    {
+      // If binary search is not possible, search sequentially starting from the largest PointSize.
+      for(auto it = fitOptions.rbegin(); it != fitOptions.rend(); ++it)
+      {
+        DevelTextLabel::FitOption option = *it;
+        float testPointSize   = option.GetPointSize();
+        float testMinLineSize = option.GetMinLineSize();
+        parameters.minLineSize = testMinLineSize;
+
+        if(CheckForTextFit(parameters, testPointSize, allowedSize))
+        {
+          bestSizeUpdatedLatest = true;
+          bestPointSize   = testPointSize;
+          bestMinLineSize = testMinLineSize;
+          break;
+        }
+        else
+        {
+          bestSizeUpdatedLatest = false;
+        }
+      }
+    }
+
+    // Best point size was not updated. re-run so the TextFit should be fitted really.
+    if(!bestSizeUpdatedLatest)
+    {
+      parameters.ellipsis    = mFitActualEllipsis;
+      parameters.minLineSize = bestMinLineSize;
+      CheckForTextFit(parameters, bestPointSize, allowedSize);
+    }
+
+    return Render(parameters);
+  }
+  else if(parameters.isTextFitEnabled)
+  {
+#ifdef TRACE_ENABLED
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+    {
+      DALI_LOG_RELEASE_INFO("AsyncTextLoader::RenderTextFit -> TextFit\n");
+    }
+#endif
+
+    float minPointSize           = parameters.textFitMinSize;
+    float maxPointSize           = parameters.textFitMaxSize;
+    float pointInterval          = parameters.textFitStepSize;
+
+    mFitActualEllipsis  = parameters.ellipsis;
+    parameters.ellipsis = false;
+    float bestPointSize = minPointSize;
+
+    Size allowedSize(parameters.textWidth, parameters.textHeight);
+
+    // check zero value
+    if(pointInterval < 1.f)
+    {
+      parameters.textFitStepSize = pointInterval = 1.0f;
+    }
+
+    uint32_t pointSizeRange = static_cast<uint32_t>(ceil((maxPointSize - minPointSize) / pointInterval));
+
+    // Ensure minPointSize + pointSizeRange * pointInverval >= maxPointSize
+    while(minPointSize + static_cast<float>(pointSizeRange) * pointInterval < maxPointSize)
+    {
+      ++pointSizeRange;
+    }
+
+    uint32_t bestSizeIndex = 0;
+    uint32_t minIndex      = bestSizeIndex + 1u;
+    uint32_t maxIndex      = pointSizeRange + 1u;
+
+    bool bestSizeUpdatedLatest = false;
+    // Find best size as binary search.
+    // Range format as [l r). (left closed, right opened)
+    // It mean, we already check all i < l is valid, and r <= i is invalid.
+    // Below binary search will check m = (l+r)/2 point.
+    // Search area sperate as [l m) or [m+1 r)
+    //
+    // Basically, we can assume that 0 (minPointSize) is always valid.
+    // Now, we will check [1 pointSizeRange] range s.t. pointSizeRange mean the maxPointSize
+    while(minIndex < maxIndex)
+    {
+      uint32_t    testIndex     = minIndex + ((maxIndex - minIndex) >> 1u);
+      const float testPointSize = std::min(maxPointSize, minPointSize + static_cast<float>(testIndex) * pointInterval);
+
+      if(CheckForTextFit(parameters, testPointSize, allowedSize))
+      {
+        bestSizeUpdatedLatest = true;
+
+        bestSizeIndex = testIndex;
+        minIndex      = testIndex + 1u;
+      }
+      else
+      {
+        bestSizeUpdatedLatest = false;
+        maxIndex              = testIndex;
+      }
+    }
+    bestPointSize = std::min(maxPointSize, minPointSize + static_cast<float>(bestSizeIndex) * pointInterval);
+
+    // Best point size was not updated. re-run so the TextFit should be fitted really.
+    if(!bestSizeUpdatedLatest)
+    {
+      parameters.ellipsis = mFitActualEllipsis;
+      CheckForTextFit(parameters, bestPointSize, allowedSize);
+    }
+
+    return Render(parameters);
+  }
+  else
+  {
+    DALI_LOG_ERROR("There is no TextFit information in AsyncTextParameters. It returns empty AsyncTextRenderInfo.\n");
+    AsyncTextRenderInfo renderInfo;
+    return renderInfo;
+  }
+}
+
+} // namespace Internal
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/async-text-loader-impl.h b/dali-toolkit/internal/text/async-text/async-text-loader-impl.h
new file mode 100644 (file)
index 0000000..988b878
--- /dev/null
@@ -0,0 +1,230 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_IMPL_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_IMPL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
+
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
+#include <dali-toolkit/internal/text/layouts/layout-engine.h>
+#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
+#include <dali-toolkit/internal/text/rendering/text-typesetter.h>
+#include <dali-toolkit/internal/text/text-model.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace Internal
+{
+
+/**
+ * Implementation of the AsyncTextLoader
+ */
+class AsyncTextLoader : public BaseObject
+{
+public:
+  /**
+   * Constructor
+   */
+  AsyncTextLoader();
+
+  /**
+   * Destructor
+   */
+  ~AsyncTextLoader();
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::ClearModule()
+   */
+  void ClearModule();
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::SetModuleClearNeeded()
+   */
+  void SetModuleClearNeeded(bool clear);
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::IsModuleClearNeeded()
+   */
+  bool IsModuleClearNeeded();
+
+
+  // Worker thread
+  /**
+   * @copydoc Dali::AsyncTextLoader::RenderText()
+   */
+  AsyncTextRenderInfo RenderText(AsyncTextParameters& parameters);
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::RenderTextFit()
+   */
+  AsyncTextRenderInfo RenderTextFit(AsyncTextParameters& parameters);
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::RenderAutoScroll()
+   */
+  AsyncTextRenderInfo RenderAutoScroll(AsyncTextParameters& parameters);
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::GetNaturalSize()
+   */
+  AsyncTextRenderInfo GetNaturalSize(AsyncTextParameters& parameters);
+
+  /**
+   * @copydoc Dali::AsyncTextLoader::GetHeightForWidth()
+   */
+  AsyncTextRenderInfo GetHeightForWidth(AsyncTextParameters& parameters);
+
+private:
+  // Worker thread
+  /**
+   * @brief Initializes internal fields.
+   *
+   * @param[in] parameters All options required to render text.
+   */
+  void Initialize();
+
+  /**
+   * @brief Clear completely data of the text model.
+   */
+  void ClearTextModelData();
+
+  /**
+   * @brief Update text model to render.
+   *
+   * @param[in] parameters All options required to render text.
+   */
+  void Update(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Layout the updated text model to render.
+   *
+   * @param[in] parameters All options required to render text.
+   * @param[out] updated true if the text has been laid-out. false means the given width is too small to layout even a single character.
+   *
+   * @return The size of the text after it has been laid-out.
+   */
+  Size Layout(AsyncTextParameters& parameters, bool& updated);
+
+  /**
+   * @brief Off screend render the updated text model to render.
+   *
+   * @param[in] parameters All options required to render text.
+   */
+  AsyncTextRenderInfo Render(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Compute natural size of text.
+   *
+   * @param[in] parameters All options required to compute size of text.
+   *
+   * @return The natural size of text.
+   */
+  Size ComputeNaturalSize(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Compute height for width of text.
+   *
+   * @param[in] parameters All options required to compute height of text.
+   * @param[in] width The width of text to compute.
+   * @param[in] layoutOnly If there is no need to Initialize/Update, only the Layout is performed.
+   *
+   * @return The height for width of text.
+   */
+  float ComputeHeightForWidth(AsyncTextParameters& parameters, float width, bool layoutOnly);
+
+  /**
+   * @brief Check if the text fits.
+   *
+   * @param[in] parameters Parameters of the text to check text fit.
+   * @param[in] pointSize The point size of the text to check text fit.
+   * @param[in] allowedSize The size of the layout to check text fit.
+   *
+   * @return True if the size of layout performed with parameters information fits, otherwise false.
+   */
+  bool CheckForTextFit(AsyncTextParameters& parameters, float pointSize, const Size& allowedSize);
+
+private:
+  /**
+   * private method
+   */
+
+private:
+  // Undefined copy constructor.
+  AsyncTextLoader(const AsyncTextLoader&);
+
+  // Undefined assignment constructor.
+  AsyncTextLoader& operator=(const AsyncTextLoader&);
+
+private:
+  /**
+   * private field
+   */
+  Text::AsyncTextModule mModule;
+
+  Text::ModelPtr        mTextModel;
+  MetricsPtr            mMetrics;
+  Text::Layout::Engine  mLayoutEngine;
+  Text::TypesetterPtr   mTypesetter;
+
+  Length                mNumberOfCharacters;
+  bool                  mFitActualEllipsis      : 1; // Used to store actual ellipses during TextFit calculations. Do not use it in other sections.
+  bool                  mIsTextDirectionRTL     : 1; // The direction of the first line after layout completion.
+  bool                  mIsTextMirrored         : 1;
+  bool                  mModuleClearNeeded      : 1;
+
+  Mutex                 mMutex;
+}; // class AsyncTextLoader
+
+inline bool compareByPointSize(DevelTextLabel::FitOption& lhs, DevelTextLabel::FitOption& rhs)
+{
+  return lhs.GetPointSize() < rhs.GetPointSize();
+}
+
+} // namespace Internal
+
+inline static Internal::AsyncTextLoader& GetImplementation(AsyncTextLoader& asyncTextLoader)
+{
+  DALI_ASSERT_ALWAYS(asyncTextLoader && "async text loader handle is empty");
+  BaseObject& handle = asyncTextLoader.GetBaseObject();
+  return static_cast<Internal::AsyncTextLoader&>(handle);
+}
+
+inline static const Internal::AsyncTextLoader& GetImplementation(const AsyncTextLoader& asyncTextLoader)
+{
+  DALI_ASSERT_ALWAYS(asyncTextLoader && "async text loader handle is empty");
+  const BaseObject& handle = asyncTextLoader.GetBaseObject();
+  return static_cast<const Internal::AsyncTextLoader&>(handle);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_IMPL_H
diff --git a/dali-toolkit/internal/text/async-text/async-text-loader.cpp b/dali-toolkit/internal/text/async-text/async-text-loader.cpp
new file mode 100644 (file)
index 0000000..1aaeebb
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-loader-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+AsyncTextLoader::AsyncTextLoader()
+{
+}
+
+AsyncTextLoader::~AsyncTextLoader()
+{
+}
+
+AsyncTextLoader::AsyncTextLoader(Internal::AsyncTextLoader* implementation)
+: BaseHandle(implementation)
+{
+}
+
+void AsyncTextLoader::ClearModule()
+{
+  GetImplementation(*this).ClearModule();
+}
+
+void AsyncTextLoader::SetModuleClearNeeded(bool clear)
+{
+  GetImplementation(*this).SetModuleClearNeeded(clear);
+}
+
+bool AsyncTextLoader::IsModuleClearNeeded()
+{
+  return GetImplementation(*this).IsModuleClearNeeded();
+}
+
+AsyncTextLoader AsyncTextLoader::New()
+{
+  auto asyncTextLoaderImpl = new Internal::AsyncTextLoader();
+
+  return AsyncTextLoader(asyncTextLoaderImpl);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderText(AsyncTextParameters& parameters)
+{
+  return GetImplementation(*this).RenderText(parameters);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderTextFit(AsyncTextParameters& parameters)
+{
+  return GetImplementation(*this).RenderTextFit(parameters);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderAutoScroll(AsyncTextParameters& parameters)
+{
+  return GetImplementation(*this).RenderAutoScroll(parameters);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::GetNaturalSize(AsyncTextParameters& parameters)
+{
+  return GetImplementation(*this).GetNaturalSize(parameters);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::GetHeightForWidth(AsyncTextParameters& parameters)
+{
+  return GetImplementation(*this).GetHeightForWidth(parameters);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/async-text-loader.h b/dali-toolkit/internal/text/async-text/async-text-loader.h
new file mode 100644 (file)
index 0000000..324979f
--- /dev/null
@@ -0,0 +1,379 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-module.h>
+#include <dali-toolkit/internal/text/text-model-interface.h>
+#include <dali-toolkit/public-api/text/text-enumerations.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor-enumerations.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/rendering/visual-renderer.h>
+
+// DEVEL INCLUDES
+#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace Internal DALI_INTERNAL
+{
+class AsyncTextLoader;
+
+} // namespace DALI_INTERNAL
+
+namespace Async
+{
+  enum RequestType
+  {
+    RENDER_FIXED_SIZE,
+    RENDER_FIXED_WIDTH,
+    RENDER_CONSTRAINT,
+    COMPUTE_NATURAL_SIZE,
+    COMPUTE_HEIGHT_FOR_WIDTH,
+  };
+  const char* const RequestTypeName[] =
+  {
+    "RENDER_FIXED_SIZE",
+    "RENDER_FIXED_WIDTH",
+    "RENDER_CONSTRAINT",
+    "COMPUTE_NATURAL_SIZE",
+    "COMPUTE_HEIGHT_FOR_WIDTH"
+  };
+} // namespace Async
+
+struct AsyncTextParameters
+{
+  AsyncTextParameters()
+  : requestType{Async::RENDER_FIXED_SIZE},
+    manualRender{false},
+    maxTextureSize{0},
+    text{},
+    fontSize{0.f},
+    textColor{Color::BLACK},
+    fontFamily{},
+    fontWeight{FontWeight::NONE},
+    fontWidth{FontWidth::NONE},
+    fontSlant{FontSlant::NONE},
+    isMultiLine{false},
+    ellipsis{true},
+    enableMarkup{false},
+    removeFrontInset{true},
+    removeBackInset{true},
+    minLineSize{0.f},
+    lineSpacing{0.f},
+    relativeLineSize{1.f},
+    characterSpacing{0.f},
+    fontSizeScale{1.f},
+    textWidth{0.f},
+    textHeight{0.f},
+    padding{0u, 0u, 0u, 0u},
+    horizontalAlignment{Text::HorizontalAlignment::BEGIN},
+    verticalAlignment{Text::VerticalAlignment::TOP},
+    verticalLineAlignment{DevelText::VerticalLineAlignment::TOP},
+    lineWrapMode{Text::LineWrap::WORD},
+    layoutDirection{Dali::LayoutDirection::LEFT_TO_RIGHT},
+    layoutDirectionPolicy{DevelText::MatchLayoutDirection::INHERIT},
+    ellipsisPosition{DevelText::EllipsisPosition::END},
+    isUnderlineEnabled{false},
+    underlineType{Text::Underline::SOLID},
+    underlineColor{Color::BLACK},
+    underlineHeight{0.f},
+    dashedUnderlineWidth{2.f},
+    dashedUnderlineGap{1.f},
+    isStrikethroughEnabled{false},
+    strikethroughColor{Color::BLACK},
+    strikethroughHeight{0.f},
+    shadowBlurRadius{0.f},
+    shadowColor{Color::BLACK},
+    shadowOffset{},
+    outlineWidth{0u},
+    outlineColor{Color::WHITE},
+    outlineBlurRadius{0.f},
+    outlineOffset{},
+    isTextFitEnabled{false},
+    textFitMinSize{10.f},
+    textFitMaxSize{100.f},
+    textFitStepSize{1.f},
+    isTextFitArrayEnabled{false},
+    textFitArray{},
+    isAutoScrollEnabled{false},
+    autoScrollStopMode{TextLabel::AutoScrollStopMode::FINISH_LOOP},
+    autoScrollSpeed{1},
+    autoScrollLoopCount{1},
+    autoScrollLoopDelay{0.0f},
+    autoScrollGap{0},
+    isAutoScrollMaxTextureExceeded{false},
+    cutout{false},
+    backgroundWithCutoutEnabled{false},
+    backgroundColorWithCutout{Color::TRANSPARENT}
+  {
+  }
+
+  ~AsyncTextParameters()
+  {
+  }
+
+  Async::RequestType requestType;
+  bool               manualRender : 1;
+
+  int         maxTextureSize; ///< The maximum size of texture.
+  std::string text;           ///< The text to be rendered encoded in utf8.
+  float       fontSize;       ///< The font's size (in points).
+  Vector4     textColor;      ///< The default text's color. Default is white.
+
+  std::string fontFamily;     ///< The font's family.
+  FontWeight  fontWeight;     ///< The font's weight.
+  FontWidth   fontWidth;      ///< The font's width.
+  FontSlant   fontSlant;      ///< The font's slant.
+
+  bool isMultiLine      : 1; ///< Whether the multi-line layout is enabled.
+  bool ellipsis         : 1; ///< Whether the ellipsis layout option is enabled.
+  bool enableMarkup     : 1; ///< Whether the mark-up processor is enabled.
+  bool removeFrontInset : 1; ///< Whether to ignore xBearing of the first glyph. Default is true.
+  bool removeBackInset  : 1; ///< Whether to ignore advance of the last glyph. Default is true.
+
+  float minLineSize;      ///< The line's minimum size (in pixels).
+  float lineSpacing;      ///< The default extra space between lines in points. (in pixels).
+  float relativeLineSize; ///< The relative height of the line (a factor that will be multiplied by text height).
+  float characterSpacing; ///< The space between characters.
+  float fontSizeScale;    ///< The font's size scale.
+
+  float textWidth;        ///< The width in pixels of the boundaries where the text is going to be laid-out.
+  float textHeight;       ///< The height in pixels of the boundaries where the text is going to be laid-out.
+  Extents  padding;       ///< The padding of the boundaries where the text is going to be laid-out.
+
+  Text::HorizontalAlignment::Type        horizontalAlignment;   ///< The horizontal alignment: one of {BEGIN, CENTER, END}.
+  Text::VerticalAlignment::Type          verticalAlignment;     ///< The vertical alignment: one of {TOP, CENTER, BOTTOM}.
+  DevelText::VerticalLineAlignment::Type verticalLineAlignment; ///< The vertical line alignment: one of {TOP, MIDDLE, BOTTOM}.
+  Text::LineWrap::Mode                   lineWrapMode;          ///< The line wrap mode: one of {WORD, CHARACTER, HYPHENATION, MIXED}.
+  Dali::LayoutDirection::Type            layoutDirection;       ///< The layout direction: one of {LEFT_TO_RIGHT, RIGHT_TO_LEFT}.
+  DevelText::MatchLayoutDirection        layoutDirectionPolicy; ///< The policy used to set the text layout direction : one of {INHERIT, LOCALE, CONTENTS}.
+  DevelText::EllipsisPosition::Type      ellipsisPosition;      ///< The position of the ellipsis glyph: one of {END, START, MIDDLE}.
+
+  bool                  isUnderlineEnabled : 1;     ///< Underline properties
+  Text::Underline::Type underlineType;
+  Vector4               underlineColor;
+  float                 underlineHeight;
+  float                 dashedUnderlineWidth;
+  float                 dashedUnderlineGap;
+
+  bool                  isStrikethroughEnabled : 1; ///< Strikethrough properties
+  Vector4               strikethroughColor;
+  float                 strikethroughHeight;
+
+  float                 shadowBlurRadius;           ///< Shadow properties
+  Vector4               shadowColor;
+  Vector2               shadowOffset;
+
+  uint16_t              outlineWidth;               ///< Outline properties
+  Vector4               outlineColor;
+  float                 outlineBlurRadius;
+  Vector2               outlineOffset;
+
+  bool                  isTextFitEnabled : 1;       ///< TextFit
+  float                 textFitMinSize;
+  float                 textFitMaxSize;
+  float                 textFitStepSize;
+
+  bool                                   isTextFitArrayEnabled : 1; ///< TextFitArray
+  std::vector<DevelTextLabel::FitOption> textFitArray;
+
+  bool                                   isAutoScrollEnabled : 1;   ///< Auto scroll
+  TextLabel::AutoScrollStopMode::Type    autoScrollStopMode;
+  int                                    autoScrollSpeed;
+  int                                    autoScrollLoopCount;
+  float                                  autoScrollLoopDelay;
+  int                                    autoScrollGap;
+  bool                                   isAutoScrollMaxTextureExceeded : 1;
+
+  bool cutout                      : 1; ///< Cutout enabled flag
+  bool backgroundWithCutoutEnabled : 1; ///< Background with cutout enabled flag.
+  Vector4 backgroundColorWithCutout;    ///< Background color with cutout.
+};
+
+struct AsyncTextRenderInfo
+{
+  AsyncTextRenderInfo()
+  : requestType(Async::RENDER_FIXED_SIZE),
+    textPixelData(),
+    stylePixelData(),
+    overlayStylePixelData(),
+    maskPixelData(),
+    autoScrollPixelData(),
+    width(0u),
+    height(0u),
+    controlSize(),
+    renderedSize(),
+    lineCount(0),
+    autoScrollWrapGap(0.f),
+    hasMultipleTextColors(false),
+    containsColorGlyph(false),
+    styleEnabled(false),
+    isOverlayStyle(false),
+    isTextDirectionRTL(false),
+    isCutout(false),
+    manualRendered(false)
+  {
+  }
+
+  ~AsyncTextRenderInfo()
+  {
+  }
+  Async::RequestType requestType;
+  PixelData          textPixelData;
+  PixelData          stylePixelData;
+  PixelData          overlayStylePixelData;
+  PixelData          maskPixelData;
+  PixelData          autoScrollPixelData;
+  uint32_t           width;
+  uint32_t           height;
+  Size               controlSize;
+  Size               renderedSize;
+  int                lineCount;
+  float              autoScrollWrapGap;
+  bool               hasMultipleTextColors : 1;
+  bool               containsColorGlyph    : 1;
+  bool               styleEnabled          : 1;
+  bool               isOverlayStyle        : 1;
+  bool               isTextDirectionRTL    : 1;
+  bool               isCutout              : 1;
+  bool               manualRendered        : 1;
+};
+
+/**
+ * AsyncTextLoader
+ *
+ */
+class AsyncTextLoader : public BaseHandle
+{
+public:
+  /**
+   * @brief Create an uninitialized AsyncTextLoader handle.
+   *
+   */
+  AsyncTextLoader();
+
+  /**
+   * @brief Destructor
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~AsyncTextLoader();
+
+  /**
+   * @brief This constructor is used by AsyncTextLoader::Get().
+   *
+   * @param[in] implementation a pointer to the internal async text loader object.
+   */
+  explicit DALI_INTERNAL AsyncTextLoader(Internal::AsyncTextLoader* implementation);
+
+  /**
+   * @brief Create a handle to the new AsyncTextLoader instance.
+   *
+   * @return A handle to the AsyncTextLoader.
+   */
+  static AsyncTextLoader New();
+
+  /**
+   * @brief Clear the cache of the async text module.
+   */
+  void ClearModule();
+
+  /**
+   * @brief Sets a flag indicating that module's cache clearing is needed.
+   *
+   * When the async text loader is available, clear is processed on the main thread.
+   *
+   * @param[in] clear Whether to clear the cache or not.
+   */
+  void SetModuleClearNeeded(bool clear);
+
+  /**
+   * @brief Whether module's cache clearing is needed.
+   *
+   * @return A flag that indicates whether the cache should be cleared or not.
+   */
+  bool IsModuleClearNeeded();
+
+  /**
+   * @brief Renders text into a pixel buffer.
+   *
+   * @param[in] parameters All options required to render text.
+   *
+   * @return An AsyncTextRenderInfo.
+   */
+  AsyncTextRenderInfo RenderText(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Renders text into a pixel buffer.
+   *
+   * @param[in] parameters All options required to render text.
+   *
+   * @return An AsyncTextRenderInfo.
+   */
+  AsyncTextRenderInfo RenderTextFit(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Renders text into a pixel buffer.
+   *
+   * @param[in] parameters All options required to render text.
+   *
+   * @return An AsyncTextRenderInfo.
+   */
+  AsyncTextRenderInfo RenderAutoScroll(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Gets the natural size of text.
+   *
+   * @param[in] parameters All options required to compute text.
+   *
+   * @return An AsyncTextRenderInfo.
+   */
+  AsyncTextRenderInfo GetNaturalSize(AsyncTextParameters& parameters);
+
+  /**
+   * @brief Gets the height for width of text.
+   *
+   * @param[in] parameters All options required to compute text.
+   *
+   * @return An AsyncTextRenderInfo.
+   */
+  AsyncTextRenderInfo GetHeightForWidth(AsyncTextParameters& parameters);
+
+public:
+  // Default copy and move operator
+  AsyncTextLoader(const AsyncTextLoader& rhs) = default;
+  AsyncTextLoader(AsyncTextLoader&& rhs)      = default;
+  AsyncTextLoader& operator=(const AsyncTextLoader& rhs) = default;
+  AsyncTextLoader& operator=(AsyncTextLoader&& rhs) = default;
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_LOADER_H
diff --git a/dali-toolkit/internal/text/async-text/async-text-manager-impl.cpp b/dali-toolkit/internal/text/async-text/async-text-manager-impl.cpp
new file mode 100644 (file)
index 0000000..ad31e86
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/async-text/async-text-manager-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/devel-api/common/singleton-service.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
+
+// INTERNAL INCLUDES
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace
+{
+const char*    DALI_TEXT_NUMBER_OF_ASYNC_TEXT_LOADER("DALI_TEXT_NUMBER_OF_ASYNC_TEXT_LOADER");
+const int      DEFAULT_NUMBER_OF_LOADER = 4;
+const int      MINIMUM_NUMBER_OF_LOADER = 1;
+const int      MAXIMUM_NUMBER_OF_LOADER = 16;
+const uint32_t EMPTY_TASK_ID            = 0u;
+
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_ASYNC, false);
+} // namespace
+
+namespace Text
+{
+namespace Internal
+{
+
+AsyncTextManager::AsyncTextManager()
+: mLocale(),
+  mTaskId(0u),
+  mAvailableLoaders(),
+  mRunningLoaders(),
+  mWaitingTasks(),
+  mRunningTasks()
+{
+  // Check environment variable for DALI_TEXT_NUMBER_OF_ASYNC_TEXT_LOADER
+  auto numberOfLoaderString = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_TEXT_NUMBER_OF_ASYNC_TEXT_LOADER);
+  int  numberOfLoader       = numberOfLoaderString ? std::atoi(numberOfLoaderString) : DEFAULT_NUMBER_OF_LOADER;
+
+  numberOfLoader = std::clamp(numberOfLoader, MINIMUM_NUMBER_OF_LOADER, MAXIMUM_NUMBER_OF_LOADER);
+  DALI_LOG_RELEASE_INFO("Number of async text loaders:%d\n", numberOfLoader);
+
+  for(int i = 0; i < numberOfLoader; i++)
+  {
+    Text::AsyncTextLoader loader = Text::AsyncTextLoader::New();
+    mAvailableLoaders.push_back(loader);
+  }
+
+  if(Dali::Adaptor::IsAvailable())
+  {
+    Dali::Adaptor::Get().LocaleChangedSignal().Connect(this, &AsyncTextManager::OnLocaleChanged);
+  }
+
+  // This function should be called after all AsyncTextModule(FontClient) are created.
+  TextAbstraction::FontClient::Get().ApplyCustomFontDirectories();
+  // TODO : In the near future, we may need to address potential thread-safety issues related to the FontConfig handle.
+}
+
+AsyncTextManager::~AsyncTextManager()
+{
+}
+
+Text::AsyncTextManager AsyncTextManager::Get()
+{
+  Text::AsyncTextManager asyncTextManagerHandle;
+
+  SingletonService service(SingletonService::Get());
+  if(service)
+  {
+    // Check whether the singleton is already created
+    Dali::BaseHandle handle = service.GetSingleton(typeid(Text::AsyncTextManager));
+    if(handle)
+    {
+      // If so, downcast the handle
+      AsyncTextManager* impl = dynamic_cast<Internal::AsyncTextManager*>(handle.GetObjectPtr());
+      asyncTextManagerHandle = Text::AsyncTextManager(impl);
+    }
+    else // create and register the object
+    {
+      asyncTextManagerHandle = Text::AsyncTextManager(new AsyncTextManager);
+      service.Register(typeid(asyncTextManagerHandle), asyncTextManagerHandle);
+    }
+  }
+
+  return asyncTextManagerHandle;
+}
+
+void AsyncTextManager::OnLocaleChanged(std::string locale)
+{
+  if(mLocale != locale)
+  {
+    mLocale = locale;
+
+    for(auto& loader : mAvailableLoaders)
+    {
+      loader.ClearModule();
+    }
+
+    // When the Loader is in running state, just set the flag and clear it when it becomes available.
+    for(auto& loader : mRunningLoaders)
+    {
+      loader.SetModuleClearNeeded(true);
+    }
+  }
+}
+
+bool AsyncTextManager::IsAvailableLoader()
+{
+  return mAvailableLoaders.size() > 0u;
+}
+
+Text::AsyncTextLoader AsyncTextManager::GetAvailableLoader()
+{
+  Text::AsyncTextLoader loader = mAvailableLoaders.back();
+  if(loader.IsModuleClearNeeded())
+  {
+    loader.ClearModule();
+    loader.SetModuleClearNeeded(false);
+  }
+
+  mAvailableLoaders.pop_back();
+  mRunningLoaders.push_back(loader);
+  return loader;
+}
+
+uint32_t AsyncTextManager::RequestLoad(AsyncTextParameters& parameters, TextLoadObserver* observer)
+{
+  // Each task must have its own unique id.
+  mTaskId++;
+
+  auto task = new Dali::Toolkit::Internal::TextLoadingTask(mTaskId, parameters, MakeCallback(this, &AsyncTextManager::LoadComplete));
+
+  LoadElement element(task, observer, parameters);
+
+  if(observer)
+  {
+#ifdef TRACE_ENABLED
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+    {
+      DALI_LOG_RELEASE_INFO("RequestLoad -> connect DestructionSignal to observer : %p, task : %u\n", observer, mTaskId);
+    }
+#endif
+    observer->ConnectDestructionSignal();
+    observer->DestructionSignal().Connect(this, &AsyncTextManager::ObserverDestroyed);
+  }
+
+  if(IsAvailableLoader())
+  {
+    // Add element to running map.
+    mRunningTasks[mTaskId] = element;
+
+    // Loader move available list -> running list.
+    Text::AsyncTextLoader loader = GetAvailableLoader();
+    task->SetLoader(loader);
+#ifdef TRACE_ENABLED
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+    {
+      DALI_LOG_RELEASE_INFO("RequestLoad -> ob : %p, add task : %u\n", element.mObserver, mTaskId);
+    }
+#endif
+  }
+  else
+  {
+    // There is no available loader, add element to waiting queue.
+    mWaitingTasks[mTaskId] = element;
+#ifdef TRACE_ENABLED
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+    {
+      DALI_LOG_RELEASE_INFO("RequestLoad -> ob : %p, add waiting task : %u\n", element.mObserver, mTaskId);
+    }
+#endif
+  }
+
+  Dali::AsyncTaskManager::Get().AddTask(task);
+
+  return mTaskId;
+}
+
+void AsyncTextManager::RequestCancel(uint32_t taskId)
+{
+  auto it = mWaitingTasks.find(taskId);
+  if(it != mWaitingTasks.end())
+  {
+    if(it->second.mObserver)
+    {
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        DALI_LOG_RELEASE_INFO("RequestCancel -> ob : %p, remove wating task : %u\n", it->second.mObserver, taskId);
+      }
+#endif
+      if(it->second.mObserver->DisconnectDestructionSignal())
+      {
+        it->second.mObserver->DestructionSignal().Disconnect(this, &AsyncTextManager::ObserverDestroyed);
+      }
+    }
+    Dali::AsyncTaskManager::Get().RemoveTask(it->second.mTask);
+    mWaitingTasks.erase(it);
+    return;
+  }
+  else
+  {
+    auto it = mRunningTasks.find(taskId);
+    if(it != mRunningTasks.end())
+    {
+      if(it->second.mObserver)
+      {
+#ifdef TRACE_ENABLED
+        if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+        {
+          DALI_LOG_RELEASE_INFO("RequestCancel -> ob : %p, remove running task : %u\n", it->second.mObserver, taskId);
+        }
+#endif
+        if(it->second.mObserver->DisconnectDestructionSignal())
+        {
+          it->second.mObserver->DestructionSignal().Disconnect(this, &AsyncTextManager::ObserverDestroyed);
+        }
+      }
+      mRunningTasks.erase(it);
+      return;
+    }
+  }
+
+  DALI_LOG_ERROR("There is no task in the Waiting queue and Running queue : %u\n", taskId);
+}
+
+void AsyncTextManager::LoadComplete(Toolkit::Internal::TextLoadingTaskPtr task)
+{
+  uint32_t taskId = task->GetId();
+
+  if(taskId == EMPTY_TASK_ID)
+  {
+    // Ignore callback from empty task.
+    return;
+  }
+
+  auto it = mRunningTasks.find(taskId);
+  if(it != mRunningTasks.end())
+  {
+    // Find task, execute load complete.
+    if(it->second.mObserver)
+    {
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        DALI_LOG_RELEASE_INFO("LoadComplete -> ob : %p, remove task : %u\n", it->second.mObserver, taskId);
+      }
+#endif
+      // TODO : If it fails for any reason, false should be sent.
+      bool success = true;
+      if(it->second.mObserver->DisconnectDestructionSignal())
+      {
+        it->second.mObserver->DestructionSignal().Disconnect(this, &AsyncTextManager::ObserverDestroyed);
+      }
+      it->second.mObserver->LoadComplete(success, TextLoadObserver::TextInformation(task->mRenderInfo, task->mParameters));
+    }
+    else
+    {
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        DALI_LOG_RELEASE_INFO("LoadComplete -> observer destroyed -> remove task : %u\n", taskId);
+      }
+#endif
+    }
+    // If a task has completed, we souhld to remove the element from running map.
+    mRunningTasks.erase(it);
+  }
+  else
+  {
+    DALI_LOG_WARNING("LoadComplete -> Running task already removed! : %u\n", taskId);
+  }
+
+  for(auto iter = mRunningLoaders.begin(); iter != mRunningLoaders.end(); ++iter)
+  {
+    if((*iter) == task->mLoader)
+    {
+      // Since the task has completed, move the loader to the available list.
+      mRunningLoaders.erase(iter);
+      mAvailableLoaders.push_back(task->mLoader);
+      break;
+    }
+  }
+
+  if(!mWaitingTasks.empty() && IsAvailableLoader())
+  {
+    // Loader move available list -> running list.
+    Text::AsyncTextLoader loader = GetAvailableLoader();
+
+    // Takes out the oldest waiting queue.
+    auto        item    = mWaitingTasks.begin();
+    LoadElement element = item->second;
+    mWaitingTasks.erase(item);
+
+    if(element.mObserver)
+    {
+      // Puts it into the running map.
+      uint32_t watingTaskId       = element.mTask->GetId();
+      mRunningTasks[watingTaskId] = element;
+
+      // Set loader and ready to process.
+      element.mTask->SetLoader(loader);
+
+      // TODO : AsyncTaskManager does not know that the IsReady has changed after the task is added.
+      // Wake up by adding an empty task to AsyncTaskManager.
+      WakeUpAsyncTaskManager();
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        DALI_LOG_RELEASE_INFO("LoadComplete task : %u -> new task -> ob : %p, add task : %u\n", taskId, element.mObserver, watingTaskId);
+      }
+#endif
+    }
+  }
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("LoadComplete -> available loaders : %lu, running loaders : %lu, waiting tasks : %lu\n", mAvailableLoaders.size(), mRunningLoaders.size(), mWaitingTasks.size());
+  }
+#endif
+}
+
+void AsyncTextManager::ObserverDestroyed(TextLoadObserver* observer)
+{
+#ifdef TRACE_ENABLED
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("ObserverDestroyed observer : %p\n", observer);
+  }
+#endif
+  for(auto it = mRunningTasks.begin(); it != mRunningTasks.end();)
+  {
+    if(it->second.mObserver == observer)
+    {
+      it->second.mObserver = nullptr;
+      it                   = mRunningTasks.erase(it);
+    }
+    else
+    {
+      ++it;
+    }
+  }
+
+  for(auto it = mWaitingTasks.begin(); it != mWaitingTasks.end();)
+  {
+    if(it->second.mObserver == observer)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(it->second.mTask);
+      it->second.mObserver = nullptr;
+      it                   = mWaitingTasks.erase(it);
+    }
+    else
+    {
+      ++it;
+    }
+  }
+}
+
+void AsyncTextManager::WakeUpAsyncTaskManager()
+{
+  auto emptyTask = new Dali::Toolkit::Internal::TextLoadingTask(EMPTY_TASK_ID, MakeCallback(this, &AsyncTextManager::LoadComplete));
+  Dali::AsyncTaskManager::Get().AddTask(emptyTask);
+}
+
+} // namespace Internal
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/async-text-manager-impl.h b/dali-toolkit/internal/text/async-text/async-text-manager-impl.h
new file mode 100644 (file)
index 0000000..c0ae870
--- /dev/null
@@ -0,0 +1,163 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_MANAGER_IMPL_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_MANAGER_IMPL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/base-object.h>
+#include <map>
+#include <queue>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-manager.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace Internal
+{
+/**
+ * @brief Async text manager implementation. @see Text::AsyncTextManager.
+ */
+class AsyncTextManager : public BaseObject, public ConnectionTracker
+{
+public:
+  /**
+   * Constructor
+   */
+  AsyncTextManager();
+
+  /**
+   * Destructor
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~AsyncTextManager();
+
+  /**
+   * @copydoc Dali::AsyncTextManager::Get()
+   */
+  static Text::AsyncTextManager Get();
+
+  /**
+   * @brief Callback function for when the locale is changed.
+   * @param[in] locale The new system locale.
+   */
+  void OnLocaleChanged(std::string locale);
+
+  /**
+   * @copydoc Dali::AsyncTextManager::RequestLoad()
+   */
+  uint32_t RequestLoad(AsyncTextParameters& parameters, TextLoadObserver* observer);
+
+  /**
+   * @copydoc Dali::AsyncTextManager::RequestCancel()
+   */
+  void RequestCancel(uint32_t taskId);
+
+  /**
+   * @copydoc Dali::AsyncTextManager::LoadComplete()
+   */
+  void LoadComplete(Toolkit::Internal::TextLoadingTaskPtr task);
+
+private:
+  /**
+   * Structure to hold info about a text load during NotifyObservers.
+   */
+  struct LoadElement
+  {
+    LoadElement()
+    : mTask(),
+      mObserver(nullptr),
+      mParameters()
+    {
+    }
+
+    LoadElement(Toolkit::Internal::TextLoadingTaskPtr task, TextLoadObserver* observer, const AsyncTextParameters& parameters)
+    : mTask(task),
+      mObserver(observer),
+      mParameters(parameters)
+    {
+    }
+
+    Toolkit::Internal::TextLoadingTaskPtr mTask;       ///< Task.
+    TextLoadObserver*                     mObserver;   ///< Observer of text load.
+    AsyncTextParameters                   mParameters; ///< Text parameters to load.
+  };
+
+  /**
+   * Whether there is an available loader.
+   */
+  bool IsAvailableLoader();
+
+  /**
+   * Returns available loader.
+   */
+  Text::AsyncTextLoader GetAvailableLoader();
+
+  /**
+   * This is called by the AsyncTextManager when an observer is destroyed.
+   * We use the callback to know when to remove an observer from our notify list.
+   * @param[in] observer The observer that generated the callback.
+   */
+  void ObserverDestroyed(TextLoadObserver* observer);
+
+  /**
+   * This is a temporary function.
+   * The AsyncTaskManager does not know that the IsReady has changed after the task is added.
+   * In this function, wake up by adding an empty task to AsyncTaskManager.
+   */
+  void WakeUpAsyncTaskManager();
+
+private:
+  std::string mLocale; ///< System locale.
+  uint32_t    mTaskId; ///< Id for managing the requested task.
+
+  std::vector<Text::AsyncTextLoader> mAvailableLoaders; ///< List of available async text loader.
+  std::vector<Text::AsyncTextLoader> mRunningLoaders;   ///< List of running async text loader.
+  std::map<uint32_t, LoadElement>    mWaitingTasks;     ///< Waiting tasks, key is task id.
+  std::map<uint32_t, LoadElement>    mRunningTasks;     ///< Running tasks, key is task id.
+};
+
+} // namespace Internal
+
+inline static Internal::AsyncTextManager& GetImplementation(AsyncTextManager& asyncTextManager)
+{
+  DALI_ASSERT_ALWAYS(asyncTextManager && "async text manager handle is empty");
+  BaseObject& handle = asyncTextManager.GetBaseObject();
+  return static_cast<Internal::AsyncTextManager&>(handle);
+}
+
+inline static const Internal::AsyncTextManager& GetImplementation(const AsyncTextManager& asyncTextManager)
+{
+  DALI_ASSERT_ALWAYS(asyncTextManager && "async text manager handle is empty");
+  const BaseObject& handle = asyncTextManager.GetBaseObject();
+  return static_cast<const Internal::AsyncTextManager&>(handle);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_MANAGER_IMPL_H
diff --git a/dali-toolkit/internal/text/async-text/async-text-manager.cpp b/dali-toolkit/internal/text/async-text/async-text-manager.cpp
new file mode 100644 (file)
index 0000000..2f1e46c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/async-text/async-text-manager.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-manager-impl.h>
+
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+AsyncTextManager::AsyncTextManager()
+{
+}
+
+AsyncTextManager::~AsyncTextManager()
+{
+}
+
+AsyncTextManager::AsyncTextManager(Internal::AsyncTextManager* implementation)
+: BaseHandle(implementation)
+{
+}
+
+AsyncTextManager AsyncTextManager::Get()
+{
+  return Internal::AsyncTextManager::Get();
+}
+
+uint32_t AsyncTextManager::RequestLoad(AsyncTextParameters& parameters, TextLoadObserver* observer)
+{
+  return GetImplementation(*this).RequestLoad(parameters, observer);
+}
+
+void AsyncTextManager::RequestCancel(uint32_t taskId)
+{
+  GetImplementation(*this).RequestCancel(taskId);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/async-text-manager.h b/dali-toolkit/internal/text/async-text/async-text-manager.h
new file mode 100644 (file)
index 0000000..6c7034a
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_MANAGER_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_MANAGER_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
+#include <dali-toolkit/internal/text/async-text/text-load-observer.h>
+#include <dali-toolkit/internal/text/async-text/text-loading-task.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace Internal DALI_INTERNAL
+{
+class AsyncTextManager;
+
+} // namespace DALI_INTERNAL
+
+/**
+ * AsyncTextManager manages an async text loader and handles async text load requests for a text visual.
+ */
+class AsyncTextManager : public BaseHandle
+{
+public:
+  /**
+   * @brief Create an uninitialized AsyncTextManager handle.
+   */
+  AsyncTextManager();
+
+  /**
+   * @brief Destructor
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~AsyncTextManager();
+
+  /**
+   * @brief This constructor is used by AsyncTextManager::Get().
+   *
+   * @param[in] implementation A pointer to the internal async text manager object.
+   */
+  explicit DALI_INTERNAL AsyncTextManager(Internal::AsyncTextManager* implementation);
+
+  /**
+   * @brief Retrieve a handle to the AsyncTextManager instance.
+   *
+   * @return A handle to the AsyncTextManager.
+   */
+  static AsyncTextManager Get();
+
+  /**
+   * @brief Make a request to asynchronously load text.
+   *
+   * TextLoadingTask is created and registered with the AsyncTaskManager.
+   * The result is received through the AsyncTextManager::LoadComplete.
+   *
+   * @param[in] parameters A parameters to load text.
+   * @param[in] observer An observer to notify load complete.
+   *
+   * @return A requested task id.
+   */
+  uint32_t RequestLoad(AsyncTextParameters& parameters, TextLoadObserver* observer);
+
+  /**
+   * @brief Request to cancel the task.
+   *
+   * Remove tasks from the waiting and running queues.
+   * Observer's LoadComplete will not be called in the running queue.
+   *
+   * @param[in] taskId A task Id to be canceled.
+   */
+  void RequestCancel(uint32_t taskId);
+
+public:
+  // Default copy and move operator
+  AsyncTextManager(const AsyncTextManager& rhs) = default;
+  AsyncTextManager(AsyncTextManager&& rhs)      = default;
+  AsyncTextManager& operator=(const AsyncTextManager& rhs) = default;
+  AsyncTextManager& operator=(AsyncTextManager&& rhs) = default;
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_MANAGER_H
diff --git a/dali-toolkit/internal/text/async-text/async-text-module-impl.cpp b/dali-toolkit/internal/text/async-text/async-text-module-impl.cpp
new file mode 100644 (file)
index 0000000..e5b4edf
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS  HEADER
+#include <dali-toolkit/internal/text/async-text/async-text-module-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace
+{
+} // namespace
+
+namespace Text
+{
+namespace Internal
+{
+
+AsyncTextModule::AsyncTextModule()
+: mBidirectionalSupport(),
+  mFontClient(),
+  mShaping(),
+  mSegmentation(),
+  mHyphenation(),
+  mMultilanguageSupport()
+{
+  bool connnectLocaleChangedSignal = false;
+
+  mBidirectionalSupport = TextAbstraction::BidirectionalSupport::New();
+  mFontClient           = TextAbstraction::FontClient::New();
+  mShaping              = TextAbstraction::Shaping::New();
+  mSegmentation         = TextAbstraction::Segmentation::New();
+  mHyphenation          = TextAbstraction::Hyphenation::New();
+  mMultilanguageSupport = Text::MultilanguageSupport::New(connnectLocaleChangedSignal);
+
+  mFontClient.InitDefaultFontDescription();
+}
+
+AsyncTextModule::~AsyncTextModule()
+{
+}
+
+void AsyncTextModule::ClearCache()
+{
+  mFontClient.ClearCacheOnLocaleChanged();
+  mFontClient.InitDefaultFontDescription();
+  mMultilanguageSupport.ClearCache();
+}
+
+TextAbstraction::BidirectionalSupport& AsyncTextModule::GetBidirectionalSupport()
+{
+  return mBidirectionalSupport;
+}
+
+TextAbstraction::FontClient& AsyncTextModule::GetFontClient()
+{
+  return mFontClient;
+}
+
+TextAbstraction::Shaping& AsyncTextModule::GetShaping()
+{
+  return mShaping;
+}
+
+TextAbstraction::Segmentation& AsyncTextModule::GetSegmentation()
+{
+  return mSegmentation;
+}
+
+TextAbstraction::Hyphenation& AsyncTextModule::GetHyphenation()
+{
+  return mHyphenation;
+}
+
+Text::MultilanguageSupport& AsyncTextModule::GetMultilanguageSupport()
+{
+  return mMultilanguageSupport;
+}
+
+} // namespace Internal
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/async-text-module-impl.h b/dali-toolkit/internal/text/async-text/async-text-module-impl.h
new file mode 100644 (file)
index 0000000..8a9665c
--- /dev/null
@@ -0,0 +1,134 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_MODULE_IMPL_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_MODULE_IMPL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-module.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace Internal
+{
+/**
+ * Implementation of the AsyncTextModule
+ */
+class AsyncTextModule : public BaseObject
+{
+public:
+  /**
+   * Constructor
+   */
+  AsyncTextModule();
+
+  /**
+   * Destructor
+   */
+  ~AsyncTextModule();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::ClearCache()
+   */
+  void ClearCache();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::GetBidirectionalSupport()
+   */
+  TextAbstraction::BidirectionalSupport& GetBidirectionalSupport();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::GetFontClient()
+   */
+  TextAbstraction::FontClient& GetFontClient();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::GetShaping()
+   */
+  TextAbstraction::Shaping& GetShaping();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::GetSegmentation()
+   */
+  TextAbstraction::Segmentation& GetSegmentation();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::GetHyphenation()
+   */
+  TextAbstraction::Hyphenation& GetHyphenation();
+
+  /**
+   * @copydoc Dali::AsyncTextModule::GetMultilanguageSupport()
+   */
+  Text::MultilanguageSupport& GetMultilanguageSupport();
+
+private:
+  /**
+   * private method
+   */
+
+private:
+  // Undefined copy constructor.
+  AsyncTextModule(const AsyncTextModule&);
+
+  // Undefined assignment constructor.
+  AsyncTextModule& operator=(const AsyncTextModule&);
+
+private:
+  /**
+   * private field
+   */
+  TextAbstraction::BidirectionalSupport mBidirectionalSupport; ///< Handle to the bidirectional support
+  TextAbstraction::FontClient           mFontClient;           ///< Handle to the font client
+  TextAbstraction::Shaping              mShaping;              ///< Handle to the shaping
+  TextAbstraction::Segmentation         mSegmentation;         ///< Handle to the segmentation
+  TextAbstraction::Hyphenation          mHyphenation;          ///< Handle to the hyphenation
+  Text::MultilanguageSupport            mMultilanguageSupport; ///< Handle to the multilanguage support
+
+}; // class AsyncTextModule
+
+} // namespace Internal
+
+inline static Internal::AsyncTextModule& GetImplementation(AsyncTextModule& asyncTextModule)
+{
+  DALI_ASSERT_ALWAYS(asyncTextModule && "async text module handle is empty");
+  BaseObject& handle = asyncTextModule.GetBaseObject();
+  return static_cast<Internal::AsyncTextModule&>(handle);
+}
+
+inline static const Internal::AsyncTextModule& GetImplementation(const AsyncTextModule& asyncTextModule)
+{
+  DALI_ASSERT_ALWAYS(asyncTextModule && "async text module handle is empty");
+  const BaseObject& handle = asyncTextModule.GetBaseObject();
+  return static_cast<const Internal::AsyncTextModule&>(handle);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_MODULE_IMPL_H
diff --git a/dali-toolkit/internal/text/async-text/async-text-module.cpp b/dali-toolkit/internal/text/async-text/async-text-module.cpp
new file mode 100644 (file)
index 0000000..fd2ff97
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/async-text/async-text-module.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-module-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+AsyncTextModule::AsyncTextModule()
+{
+}
+
+AsyncTextModule::~AsyncTextModule()
+{
+}
+
+AsyncTextModule::AsyncTextModule(Internal::AsyncTextModule* implementation)
+: BaseHandle(implementation)
+{
+}
+
+AsyncTextModule AsyncTextModule::New()
+{
+  auto asyncTextModuleImpl = new Internal::AsyncTextModule();
+
+  return AsyncTextModule(asyncTextModuleImpl);
+}
+
+void AsyncTextModule::ClearCache()
+{
+  GetImplementation(*this).ClearCache();
+}
+
+TextAbstraction::BidirectionalSupport& AsyncTextModule::GetBidirectionalSupport()
+{
+  return GetImplementation(*this).GetBidirectionalSupport();
+}
+
+TextAbstraction::FontClient& AsyncTextModule::GetFontClient()
+{
+  return GetImplementation(*this).GetFontClient();
+}
+
+TextAbstraction::Shaping& AsyncTextModule::GetShaping()
+{
+  return GetImplementation(*this).GetShaping();
+}
+
+TextAbstraction::Segmentation& AsyncTextModule::GetSegmentation()
+{
+  return GetImplementation(*this).GetSegmentation();
+}
+
+TextAbstraction::Hyphenation& AsyncTextModule::GetHyphenation()
+{
+  return GetImplementation(*this).GetHyphenation();
+}
+
+Text::MultilanguageSupport& AsyncTextModule::GetMultilanguageSupport()
+{
+  return GetImplementation(*this).GetMultilanguageSupport();
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/async-text-module.h b/dali-toolkit/internal/text/async-text/async-text-module.h
new file mode 100644 (file)
index 0000000..fb31a05
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef DALI_TOOLKIT_TEXT_ASYNC_TEXT_MODULE_H
+#define DALI_TOOLKIT_TEXT_ASYNC_TEXT_MODULE_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/multi-language-support.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor-enumerations.h>
+#include <dali/public-api/object/base-handle.h>
+
+#include <dali/devel-api/text-abstraction/bidirectional-support.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/hyphenation.h>
+#include <dali/devel-api/text-abstraction/segmentation.h>
+#include <dali/devel-api/text-abstraction/shaping.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace Internal DALI_INTERNAL
+{
+class AsyncTextModule;
+
+} // namespace DALI_INTERNAL
+
+/**
+ * AsyncTextModule
+ *
+ */
+class AsyncTextModule : public BaseHandle
+{
+public:
+  /**
+   * @brief Create an uninitialized AsyncTextModule handle.
+   *
+   */
+  AsyncTextModule();
+
+  /**
+   * @brief Destructor
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~AsyncTextModule();
+
+  /**
+   * @brief This constructor is used by AsyncTextModule::New().
+   *
+   * @param[in] implementation a pointer to the internal async text module object.
+   */
+  explicit DALI_INTERNAL AsyncTextModule(Internal::AsyncTextModule* implementation);
+
+  /**
+   * @brief Create a handle to the new AsyncTextModule instance.
+   *
+   * @return A handle to the AsyncTextModule.
+   */
+  static AsyncTextModule New();
+
+  /**
+   * @brief Clear cache of modules.
+   */
+  void ClearCache();
+
+  /**
+   * @brief BidirectionalSupport to use in AsyncTextLoader.
+   */
+  TextAbstraction::BidirectionalSupport& GetBidirectionalSupport();
+
+  /**
+   * @brief FontClient to use in AsyncTextLoader.
+   */
+  TextAbstraction::FontClient& GetFontClient();
+
+  /**
+   * @brief Shaping to use in AsyncTextLoader.
+   */
+  TextAbstraction::Shaping& GetShaping();
+
+  /**
+   * @brief Segmentation to use in AsyncTextLoader.
+   */
+  TextAbstraction::Segmentation& GetSegmentation();
+
+  /**
+   * @brief Hyphenation to use in AsyncTextLoader.
+   */
+  TextAbstraction::Hyphenation& GetHyphenation();
+
+  /**
+   * @brief MultilanguageSupport to use in AsyncTextLoader.
+   */
+  Text::MultilanguageSupport& GetMultilanguageSupport();
+
+
+public:
+  // Default copy and move operator
+  AsyncTextModule(const AsyncTextModule& rhs) = default;
+  AsyncTextModule(AsyncTextModule&& rhs)      = default;
+  AsyncTextModule& operator=(const AsyncTextModule& rhs) = default;
+  AsyncTextModule& operator=(AsyncTextModule&& rhs) = default;
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ASYNC_TEXT_MODULE_H
diff --git a/dali-toolkit/internal/text/async-text/text-load-observer.cpp b/dali-toolkit/internal/text/async-text/text-load-observer.cpp
new file mode 100644 (file)
index 0000000..d56e6c8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/async-text/text-load-observer.h>
+
+// INTERNAL INCLUDES
+
+namespace Dali
+{
+namespace Toolkit
+{
+TextLoadObserver::TextInformation::TextInformation(const Text::AsyncTextRenderInfo& renderInfo, const Text::AsyncTextParameters& parameters)
+: renderInfo(renderInfo),
+  parameters(parameters)
+{
+}
+
+TextLoadObserver::TextLoadObserver()
+: mDestructionSignalConnect(0)
+{
+}
+
+TextLoadObserver::~TextLoadObserver()
+{
+  if(!mDestructionSignal.Empty())
+  {
+    mDestructionSignal.Emit(this);
+  }
+}
+
+TextLoadObserver::DestructionSignalType& TextLoadObserver::DestructionSignal()
+{
+  return mDestructionSignal;
+}
+
+void TextLoadObserver::ConnectDestructionSignal()
+{
+  mDestructionSignalConnect++;
+}
+
+bool TextLoadObserver::DisconnectDestructionSignal()
+{
+  mDestructionSignalConnect--;
+  if(mDestructionSignalConnect < 0)
+  {
+    mDestructionSignalConnect = 0;
+  }
+  return !mDestructionSignalConnect;
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/text-load-observer.h b/dali-toolkit/internal/text/async-text/text-load-observer.h
new file mode 100644 (file)
index 0000000..8545660
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef DALI_TOOLKIT_INTERNAL_TEXT_LOAD_OBSERVER_H
+#define DALI_TOOLKIT_INTERNAL_TEXT_LOAD_OBSERVER_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/signals/dali-signal.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+/**
+ * @brief Base class used to observe the load status of a async text.
+ *
+ * Derived class must implement the LoadComplete method which is
+ * executed once the text is ready to render.
+ */
+class TextLoadObserver
+{
+public:
+  typedef Signal<void(TextLoadObserver*)> DestructionSignalType; ///< Signal prototype for the Destruction Signal.
+
+  struct TextInformation
+  {
+    TextInformation(const Text::AsyncTextRenderInfo& renderInfo, const Text::AsyncTextParameters& parameters);
+    TextInformation();
+
+    Text::AsyncTextRenderInfo renderInfo;
+    Text::AsyncTextParameters parameters;
+  };
+
+public:
+  /**
+   * @brief Constructor.
+   */
+  TextLoadObserver();
+
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~TextLoadObserver();
+
+  /**
+   * The action to be taken once the async load has finished.
+   * And in case of text loading, this method is called after off screen rendering.
+   * This should be overridden by the deriving class.
+   *
+   * @param[in] loadSuccess True if the text load was successful.
+   * @param[in] textureInformation Structure that contains loaded text information.
+   */
+  virtual void LoadComplete(bool loadSuccess, const TextInformation& textInformation) = 0;
+
+  /**
+   * @brief Returns the destruction signal.
+   * This is emitted when the observer is destroyed.
+   * This is used by the observer notifier to mark this observer as destroyed (IE. It no longer needs notifying).
+   */
+  DestructionSignalType& DestructionSignal();
+
+  /**
+   * @brief Each time DestructionSignal is connected, the count increases by 1.
+   */
+  void ConnectDestructionSignal();
+
+  /**
+   * @brief Each time DestructionSignal is disconnected, the count decreases by 1.
+   * @return If the count reaches 0, true is returned.
+   */
+  bool DisconnectDestructionSignal();
+
+public:
+private:
+  DestructionSignalType mDestructionSignal;        ///< The destruction signal emitted when the observer is destroyed.
+  int                   mDestructionSignalConnect; ///< The number of times DestructionSignal is connected.
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_TEXT_LOAD_OBSERVER_H
diff --git a/dali-toolkit/internal/text/async-text/text-loading-task.cpp b/dali-toolkit/internal/text/async-text/text-loading-task.cpp
new file mode 100644 (file)
index 0000000..10740e4
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/async-text/text-loading-task.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/thread-settings.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_ASYNC, false);
+} // namespace
+
+TextLoadingTask::TextLoadingTask(const uint32_t id, const Text::AsyncTextParameters& parameters, CallbackBase* callback)
+: AsyncTask(callback),
+  mId(id),
+  mParameters(parameters),
+  mRenderInfo(),
+  mIsReady(false),
+  mMutex()
+{
+}
+
+TextLoadingTask::TextLoadingTask(const uint32_t id, CallbackBase* callback)
+: AsyncTask(callback),
+  mId(id),
+  mIsReady(true),
+  mMutex()
+{
+  // Empty task for wake up the async task manger.
+}
+
+TextLoadingTask::~TextLoadingTask()
+{
+}
+
+uint32_t TextLoadingTask::GetId()
+{
+  return mId;
+}
+
+void TextLoadingTask::SetLoader(Text::AsyncTextLoader& loader)
+{
+  Dali::Mutex::ScopedLock lock(mMutex);
+  mLoader  = loader;
+  mIsReady = true;
+}
+
+void TextLoadingTask::Process()
+{
+  if(mId == 0u)
+  {
+    return;
+  }
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_LOADING_TASK_PROCESS");
+  Load();
+}
+
+bool TextLoadingTask::IsReady()
+{
+  return mIsReady;
+}
+
+void TextLoadingTask::Load()
+{
+  switch(mParameters.requestType)
+  {
+    case Text::Async::RENDER_FIXED_SIZE:
+    case Text::Async::RENDER_FIXED_WIDTH:
+    case Text::Async::RENDER_CONSTRAINT:
+    {
+      if(mParameters.isAutoScrollEnabled && !mParameters.isMultiLine)
+      {
+#ifdef TRACE_ENABLED
+        if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+        {
+          DALI_LOG_RELEASE_INFO("RenderAutoScroll\n");
+        }
+#endif
+        mRenderInfo = mLoader.RenderAutoScroll(mParameters);
+      }
+      else if(mParameters.isTextFitEnabled || mParameters.isTextFitArrayEnabled)
+      {
+#ifdef TRACE_ENABLED
+        if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+        {
+          DALI_LOG_RELEASE_INFO("RenderTextFit\n");
+        }
+#endif
+        mRenderInfo = mLoader.RenderTextFit(mParameters);
+      }
+      else
+      {
+#ifdef TRACE_ENABLED
+        if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+        {
+          DALI_LOG_RELEASE_INFO("RenderText\n");
+        }
+#endif
+        mRenderInfo = mLoader.RenderText(mParameters);
+      }
+      break;
+    }
+    case Text::Async::COMPUTE_NATURAL_SIZE:
+    {
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        DALI_LOG_RELEASE_INFO("GetNaturalSize\n");
+      }
+#endif
+      mRenderInfo = mLoader.GetNaturalSize(mParameters);
+      break;
+    }
+    case Text::Async::COMPUTE_HEIGHT_FOR_WIDTH:
+    {
+#ifdef TRACE_ENABLED
+      if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+      {
+        DALI_LOG_RELEASE_INFO("GetHeightForWidth\n");
+      }
+#endif
+      mRenderInfo = mLoader.GetHeightForWidth(mParameters);
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unexpected request type recieved : %d\n", mParameters.requestType);
+      break;
+    }
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/async-text/text-loading-task.h b/dali-toolkit/internal/text/async-text/text-loading-task.h
new file mode 100644 (file)
index 0000000..196461e
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef DALI_TOOLKIT_TEXT_LOADING_TASK_H
+#define DALI_TOOLKIT_TEXT_LOADING_TASK_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-loader.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class TextLoadingTask;
+using TextLoadingTaskPtr = IntrusivePtr<TextLoadingTask>;
+
+/**
+ * The task of loading text(update, layout and render).
+ */
+class TextLoadingTask : public AsyncTask
+{
+public:
+  /**
+   * Constructor.
+   * @param [in] id The task id assigned from async text manager.
+   * @param [in] paramaters The async text parameters.
+   * @param [in] callback The callback that is called when the operation is completed.
+   */
+  TextLoadingTask(const uint32_t                   id,
+                  const Text::AsyncTextParameters& paramaters,
+                  CallbackBase*                    callback);
+
+  /**
+   * Constructor, empty task for wake up the async task manger.
+   * @param [in] id The task id assigned from async text manager.
+   * @param [in] callback The callback that is called when the operation is completed.
+   */
+  TextLoadingTask(const uint32_t id,
+                  CallbackBase*  callback);
+
+  /**
+   * Destructor.
+   */
+  ~TextLoadingTask() override;
+
+  /**
+   * Returns the id assigned when creating a task.
+   * @return The task id.
+   */
+  uint32_t GetId();
+
+  /**
+   * Set async text loader to process.
+   * The task becomes ready to process.
+   * @param [in] loader The async text loader, a loader can only process one task at a time.
+   * @return The task id.
+   */
+  void SetLoader(Text::AsyncTextLoader& loader);
+
+public: // Implementation of AsyncTask
+  /**
+   * @copydoc Dali::AsyncTask::Process()
+   */
+  void Process() override;
+
+  /**
+   * @copydoc Dali::AsyncTask::IsReady()
+   */
+  bool IsReady() override;
+
+  /**
+   * @copydoc Dali::AsyncTask::GetTaskName()
+   */
+  std::string_view GetTaskName() const override
+  {
+    return "TextLoadingTask";
+  }
+
+private:
+  // Undefined
+  TextLoadingTask(const TextLoadingTask& queue);
+
+  // Undefined
+  TextLoadingTask& operator=(const TextLoadingTask& queue);
+
+  /**
+   * Load the text
+   */
+  void Load();
+
+public:
+  uint32_t                  mId;
+  Text::AsyncTextLoader     mLoader;
+  Text::AsyncTextParameters mParameters;
+  Text::AsyncTextRenderInfo mRenderInfo;
+
+private:
+  bool  mIsReady : 1; ///< Whether this task ready to run
+  Mutex mMutex;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_LOADING_TASK_H
index b158e7c..2b3c87e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/text-abstraction/bidirectional-support.h>
 #include <memory.h>
 
 namespace Dali
@@ -28,7 +27,9 @@ namespace Toolkit
 {
 namespace Text
 {
-void SetBidirectionalInfo(const Vector<Character>&               text,
+
+void SetBidirectionalInfo(TextAbstraction::BidirectionalSupport& bidirectionalSupport,
+                          const Vector<Character>&               text,
                           const Vector<ScriptRun>&               scripts,
                           const Vector<LineBreakInfo>&           lineBreakInfo,
                           CharacterIndex                         startIndex,
@@ -66,9 +67,6 @@ void SetBidirectionalInfo(const Vector<Character>&               text,
   // Pointer to the line break info buffer.
   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
 
-  // Handle to the bidirectional info module in text-abstraction.
-  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
-
   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
 
   bool hasRightToLeftScript = false;
@@ -142,18 +140,16 @@ void SetBidirectionalInfo(const Vector<Character>&               text,
   }
 }
 
-void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
-                 Vector<BidirectionalLineInfoRun>&    lineInfoRuns,
-                 BidirectionalLineRunIndex            bidiLineIndex,
-                 CharacterIndex                       startIndex,
-                 Length                               numberOfCharacters,
-                 CharacterIndex                       startIndexInSecondHalfLine,
-                 Length                               numberOfCharactersInSecondHalfLine,
-                 CharacterDirection                   direction)
+void ReorderLine(TextAbstraction::BidirectionalSupport& bidirectionalSupport,
+                 const BidirectionalParagraphInfoRun&   bidirectionalParagraphInfo,
+                 Vector<BidirectionalLineInfoRun>&      lineInfoRuns,
+                 BidirectionalLineRunIndex              bidiLineIndex,
+                 CharacterIndex                         startIndex,
+                 Length                                 numberOfCharacters,
+                 CharacterIndex                         startIndexInSecondHalfLine,
+                 Length                                 numberOfCharactersInSecondHalfLine,
+                 CharacterDirection                     direction)
 {
-  // Handle to the bidirectional info module in text-abstraction.
-  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
-
   // Creates a bidirectional info for the line run.
   BidirectionalLineInfoRun lineInfoRun;
   lineInfoRun.characterRun.characterIndex     = startIndex;
@@ -208,7 +204,8 @@ void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo
   lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun);
 }
 
-bool GetMirroredText(const Vector<Character>&                     text,
+bool GetMirroredText(TextAbstraction::BidirectionalSupport&       bidirectionalSupport,
+                     const Vector<Character>&                     text,
                      const Vector<CharacterDirection>&            directions,
                      const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
                      CharacterIndex                               startIndex,
@@ -217,9 +214,6 @@ bool GetMirroredText(const Vector<Character>&                     text,
 {
   bool hasTextMirrored = false;
 
-  // Handle to the bidirectional info module in text-abstraction.
-  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
-
   mirroredText = text;
 
   Character*          mirroredTextBuffer = mirroredText.Begin();
@@ -259,15 +253,13 @@ bool GetMirroredText(const Vector<Character>&                     text,
   return hasTextMirrored;
 }
 
-void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+void GetCharactersDirection(TextAbstraction::BidirectionalSupport&       bidirectionalSupport,
+                            const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
                             Length                                       totalNumberOfCharacters,
                             CharacterIndex                               startIndex,
                             Length                                       numberOfCharacters,
                             Vector<CharacterDirection>&                  directions)
 {
-  // Handle to the bidirectional info module in text-abstraction.
-  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
-
   // Resize the vector.
   directions.Resize(totalNumberOfCharacters);
 
index 054f50d..c44e232 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_BIDIRECTIONAL_SUPPORT_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/bidirectional-support.h>
 #include <dali/public-api/actors/actor-enumerations.h>
 #include <dali/public-api/common/dali-vector.h>
 
@@ -37,6 +38,7 @@ namespace Text
 /**
  * Sets the bidirectional info into the logical model.
  *
+ * @param[in] bidirectionalSupport BidirectionalSupport to use in this function.
  * @param[in] text Vector of UTF-32 characters.
  * @param[in] scripts Vector containing the script runs for the whole text.
  * @param[in] lineBreakInfo The line break info.
@@ -46,7 +48,8 @@ namespace Text
  * @param[in] matchLayoutDirection Whether match for layout direction or not.
  * @param[in] layoutDirection The direction of the layout.
  */
-void SetBidirectionalInfo(const Vector<Character>&               text,
+void SetBidirectionalInfo(TextAbstraction::BidirectionalSupport& bidirectionalSupport,
+                          const Vector<Character>&               text,
                           const Vector<ScriptRun>&               scripts,
                           const Vector<LineBreakInfo>&           lineBreakInfo,
                           CharacterIndex                         startIndex,
@@ -58,6 +61,7 @@ void SetBidirectionalInfo(const Vector<Character>&               text,
 /**
  * @brief Sets the visual to logical map table for a given line.
  *
+ * @param[in] bidirectionalSupport BidirectionalSupport to use in this function.
  * @param[in] bidirectionalParagraphInfo The paragraph's bidirectional info.
  * @param[out] lineInfoRuns Line runs with the visual to logical conversion maps.
  * @param[in] bidiLineIndex Index to the line's bidirectional info.
@@ -67,18 +71,20 @@ void SetBidirectionalInfo(const Vector<Character>&               text,
  * @param[in] numberOfCharactersInSecondHalfLine The number of characters for the second half of line..
  * @param[in] direction The direction of the line.
  */
-void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
-                 Vector<BidirectionalLineInfoRun>&    lineInfoRuns,
-                 BidirectionalLineRunIndex            bidiLineIndex,
-                 CharacterIndex                       startIndex,
-                 Length                               numberOfCharacters,
-                 CharacterIndex                       startIndexInSecondHalfLine,
-                 Length                               numberOfCharactersInSecondHalfLine,
-                 CharacterDirection                   direction);
+void ReorderLine(TextAbstraction::BidirectionalSupport& bidirectionalSupport,
+                 const BidirectionalParagraphInfoRun&   bidirectionalParagraphInfo,
+                 Vector<BidirectionalLineInfoRun>&      lineInfoRuns,
+                 BidirectionalLineRunIndex              bidiLineIndex,
+                 CharacterIndex                         startIndex,
+                 Length                                 numberOfCharacters,
+                 CharacterIndex                         startIndexInSecondHalfLine,
+                 Length                                 numberOfCharactersInSecondHalfLine,
+                 CharacterDirection                     direction);
 
 /**
  * @brief Replaces any character in the right to left paragraphs which could be mirrored.
  *
+ * @param[in] bidirectionalSupport BidirectionalSupport to use in this function.
  * @param[in] text The text.
  * @param[in] directions Vector with the direction of each paragraph.
  * @param[in] bidirectionalInfo Vector with the bidirectional infor for each paragraph.
@@ -88,7 +94,8 @@ void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo
  *
  * @return @e true if a character has been replaced.
  */
-bool GetMirroredText(const Vector<Character>&                     text,
+bool GetMirroredText(TextAbstraction::BidirectionalSupport&       bidirectionalSupport,
+                     const Vector<Character>&                     text,
                      const Vector<CharacterDirection>&            directions,
                      const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
                      CharacterIndex                               startIndex,
@@ -101,13 +108,15 @@ bool GetMirroredText(const Vector<Character>&                     text,
  * @pre The @p logicalModel needs to have a text set.
  * @pre The @p logicalModel needs to have the bidirectional info indices for each paragraph set.
  *
+ * @param[in] bidirectionalSupport BidirectionalSupport to use in this function.
  * @param[in] bidirectionalInfo Vector with the bidirectional infor for each paragraph.
  * @param[in] totalNumberOfCharacters The number of characters of the whole text.
  * @param[in] startIndex The character from where the direction info is set.
  * @param[in] numberOfCharacters The number of characters.
  * @param[out] directions The direction, @e false is left to right and @e true is right to left, of each character of the paragraph.
  */
-void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+void GetCharactersDirection(TextAbstraction::BidirectionalSupport&       bidirectionalSupport,
+                            const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
                             Length                                       totalNumberOfCharacters,
                             CharacterIndex                               startIndex,
                             Length                                       numberOfCharacters,
index fb4eb5f..3d579e9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -171,10 +171,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     // is not shaped together).
     lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
 
-    SetLineBreakInfo(utf32Characters,
-                     startIndex,
-                     requestedNumberOfCharacters,
-                     lineBreakInfo);
+    TextAbstraction::Segmentation segmentation = TextAbstraction::Segmentation::Get();
+    SetLineBreakInfo(segmentation, utf32Characters, startIndex, requestedNumberOfCharacters, lineBreakInfo);
 
     if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
        impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
@@ -182,6 +180,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
       CharacterIndex end                 = startIndex + requestedNumberOfCharacters;
       LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
 
+      TextAbstraction::Hyphenation hyphenation = TextAbstraction::Hyphenation::Get();
+
       for(CharacterIndex index = startIndex; index < end; index++)
       {
         CharacterIndex wordEnd = index;
@@ -195,7 +195,7 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
           wordEnd++;
         }
 
-        Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
+        Vector<bool> hyphens = GetWordHyphens(hyphenation, utf32Characters.Begin() + index, wordEnd - index, nullptr);
 
         for(CharacterIndex i = 0; i < (wordEnd - index); i++)
         {
@@ -274,7 +274,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
 
       // Validates the fonts. If there is a character with no assigned font it sets a default one.
       // After this call, fonts are validated.
-      multilanguageSupport.ValidateFonts(utf32Characters,
+      multilanguageSupport.ValidateFonts(impl.mFontClient,
+                                         utf32Characters,
                                          scripts,
                                          fontDescriptionRuns,
                                          defaultFontDescription,
@@ -295,8 +296,11 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = impl.mModel->mLogicalModel->mBidirectionalParagraphInfo;
     bidirectionalInfo.Reserve(numberOfParagraphs);
 
+    TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
-    SetBidirectionalInfo(utf32Characters,
+    SetBidirectionalInfo(bidirectionalSupport,
+                         utf32Characters,
                          scripts,
                          lineBreakInfo,
                          startIndex,
@@ -309,7 +313,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
     {
       // Only set the character directions if there is right to left characters.
       Vector<CharacterDirection>& directions = impl.mModel->mLogicalModel->mCharacterDirections;
-      GetCharactersDirection(bidirectionalInfo,
+      GetCharactersDirection(bidirectionalSupport,
+                             bidirectionalInfo,
                              numberOfCharacters,
                              startIndex,
                              requestedNumberOfCharacters,
@@ -318,7 +323,8 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
       // This paragraph has right to left text. Some characters may need to be mirrored.
       // TODO: consider if the mirrored string can be stored as well.
 
-      textMirrored = GetMirroredText(utf32Characters,
+      textMirrored = GetMirroredText(bidirectionalSupport,
+                                     utf32Characters,
                                      directions,
                                      bidirectionalInfo,
                                      startIndex,
@@ -357,8 +363,13 @@ bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask o
   if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations))
   {
     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
+
+    TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
+
     // Shapes the text.
-    ShapeText(textToShape,
+    ShapeText(shaping,
+              impl.mFontClient,
+              textToShape,
               lineBreakInfo,
               scripts,
               validFonts,
index 10a76f1..a33ebe3 100644 (file)
@@ -1677,12 +1677,11 @@ bool Controller::Impl::IsScrollable(const Vector2& displacement)
     {
       const Vector2& targetSize     = mModel->mVisualModel->mControlSize;
       const Vector2& layoutSize     = mModel->mVisualModel->GetLayoutSize();
-      const Vector2& scrollPosition = mModel->mScrollPosition;
 
       if(isHorizontalScrollEnabled)
       {
-        const float displacementX = displacement.x;
-        const float positionX     = scrollPosition.x + displacementX;
+        const float scrollPositionX = std::max(mModel->mScrollPosition.x, -(layoutSize.width - targetSize.width));
+        const float positionX       = scrollPositionX + displacement.x;
         if(layoutSize.width > targetSize.width && -positionX > 0.f && -positionX < layoutSize.width - targetSize.width)
         {
           isScrollable = true;
@@ -1691,9 +1690,9 @@ bool Controller::Impl::IsScrollable(const Vector2& displacement)
 
       if(isVerticalScrollEnabled)
       {
-        const float displacementY = displacement.y;
-        const float positionY     = scrollPosition.y + displacementY;
-        if(layoutSize.height > targetSize.height && -positionY > 0 && -positionY < layoutSize.height - targetSize.height)
+        const float scrollPositionY = std::max(mModel->mScrollPosition.y, -(layoutSize.height - targetSize.height));
+        const float positionY       = scrollPositionY + displacement.y;
+        if(layoutSize.height > targetSize.height && -positionY > 0.f && -positionY < layoutSize.height - targetSize.height)
         {
           isScrollable = true;
         }
@@ -2057,6 +2056,7 @@ void Controller::Impl::ClearFontData()
 void Controller::Impl::ClearStyleData()
 {
   mModel->mLogicalModel->mColorRuns.Clear();
+  mModel->mLogicalModel->mBackgroundColorRuns.Clear();
   mModel->mLogicalModel->ClearFontDescriptionRuns();
   mModel->mLogicalModel->ClearStrikethroughRuns();
   mModel->mLogicalModel->ClearUnderlineRuns();
index 0b404cc..f172b17 100644 (file)
@@ -346,6 +346,7 @@ struct Controller::Impl
     mHiddenInput(NULL),
     mInputFilter(nullptr),
     mTextFitContentSize(),
+    mRawText(),
     mTextFitArray(),
     mRecalculateNaturalSize(true),
     mMarkupProcessorEnabled(false),
@@ -375,7 +376,8 @@ struct Controller::Impl
     mIsLayoutDirectionChanged(false),
     mIsUserInteractionEnabled(true),
     mProcessorRegistered(false),
-    mTextCutout(false)
+    mTextCutout(false),
+    mRenderMode(DevelTextLabel::Render::SYNC)
   {
     mModel = Model::New();
 
@@ -384,7 +386,7 @@ struct Controller::Impl
     {
       mClipboard = Clipboard::Get();
     }
-
+    mView.SetFontClient(mFontClient);
     mView.SetVisualModel(mModel->mVisualModel);
     mView.SetLogicalModel(mModel->mLogicalModel);
 
@@ -1074,6 +1076,7 @@ public:
   HiddenText*                  mHiddenInput;                ///< Avoid allocating this when the user does not specify hidden input mode.
   std::unique_ptr<InputFilter> mInputFilter;                ///< Avoid allocating this when the user does not specify input filter mode.
   Vector2                      mTextFitContentSize;         ///< Size of Text fit content
+  std::string                  mRawText;                    ///< Raw text including markup tag.
 
   std::vector<Toolkit::DevelTextLabel::FitOption> mTextFitArray; ///< List of FitOption for TextFitArray operation.
 
@@ -1111,6 +1114,8 @@ public:
   bool  mProcessorRegistered : 1;      ///< Whether the text controller registered into processor or not.
   bool  mTextCutout : 1;               ///< Whether the text cutout enabled.
 
+  DevelTextLabel::Render::Mode mRenderMode; ///< Render mode of the text. (SYNC, ASYNC_AUTO, ASYNC_MANUAL)
+
 private:
   friend ControllerImplEventHandler;
   friend ControllerImplModelUpdater;
index de4c776..b195e87 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -826,8 +826,10 @@ bool Controller::Relayouter::DoRelayout(Controller::Impl& impl, const Size& size
       return true;
     }
 
+    TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+
     // Set the layout parameters.
-    Layout::Parameters layoutParameters(size, impl.mModel);
+    Layout::Parameters layoutParameters(size, impl.mModel, impl.mFontClient, bidirectionalSupport);
 
     // Resize the vector of positions to have the same size than the vector of glyphs.
     Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
@@ -877,13 +879,14 @@ bool Controller::Relayouter::DoRelayout(Controller::Impl& impl, const Size& size
     }
 
     Size newLayoutSize;
-    viewUpdated               = impl.mLayoutEngine.LayoutText(layoutParameters,
+    viewUpdated = impl.mLayoutEngine.LayoutText(layoutParameters,
                                                 newLayoutSize,
                                                 elideTextEnabled,
                                                 isAutoScrollEnabled,
                                                 isAutoScrollMaxTextureExceeded,
                                                 isHiddenInputEnabled,
                                                 ellipsisPosition);
+
     impl.mIsAutoScrollEnabled = isAutoScrollEnabled;
     layoutTooSmall = !viewUpdated;
 
index 59da80a..049682a 100644 (file)
@@ -82,6 +82,10 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string&
     }
   }
 
+  // Stores raw text including markup tags.
+  // Currently, this raw text cannot be updated when it is editable.
+  impl.mRawText = text;
+
   if(!text.empty())
   {
     ModelPtr&        model        = impl.mModel;
index da697ba..dfeed9e 100644 (file)
@@ -375,6 +375,16 @@ bool Controller::IsShowingRealText() const
   return mImpl->IsShowingRealText();
 }
 
+void Controller::SetRenderMode(DevelTextLabel::Render::Mode renderMode)
+{
+  mImpl->mRenderMode = renderMode;
+}
+
+DevelTextLabel::Render::Mode Controller::GetRenderMode()
+{
+  return mImpl->mRenderMode;
+}
+
 void Controller::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
 {
   mImpl->SetLineWrapMode(lineWrapMode);
@@ -471,6 +481,14 @@ float Controller::GetTextFitPointSize() const
   return mImpl->mFontDefaults ? mImpl->mFontDefaults->mFitPointSize : 0.0f;
 }
 
+void Controller::SetTextFitPointSize(float pointSize)
+{
+  EnsureCreated(mImpl->mFontDefaults);
+  mImpl->mFontDefaults->mFitPointSize = pointSize;
+  mImpl->mFontDefaults->sizeDefined   = true;
+  mImpl->ClearFontData();
+}
+
 void Controller::SetTextFitLineSize(float lineSize)
 {
   mImpl->mTextFitLineSize = lineSize;
@@ -558,6 +576,11 @@ void Controller::GetText(std::string& text) const
   mImpl->GetText(text);
 }
 
+void Controller::GetRawText(std::string& text) const
+{
+  text = mImpl->mRawText;
+}
+
 Length Controller::GetNumberOfCharacters() const
 {
   return mImpl->GetNumberOfCharacters();
@@ -1568,6 +1591,11 @@ const Vector4 Controller::GetBackgroundColorWithCutout() const
   return mImpl->mModel->mVisualModel->GetBackgroundColorWithCutout();
 }
 
+void Controller::SetOffsetWithCutout(const Vector2& offset)
+{
+  mImpl->mModel->mVisualModel->SetOffsetWithCutout(offset);
+}
+
 Controller::UpdateTextType Controller::Relayout(const Size& size, Dali::LayoutDirection::Type layoutDirection)
 {
   return Relayouter::Relayout(*this, size, layoutDirection);
@@ -1660,7 +1688,7 @@ void Controller::RequestProcessInputStyleChangedSignals()
   if(Dali::Adaptor::IsAvailable() && !mImpl->mProcessorRegistered)
   {
     mImpl->mProcessorRegistered = true;
-    Dali::Adaptor::Get().RegisterProcessor(*this, true);
+    Dali::Adaptor::Get().RegisterProcessorOnce(*this, true);
   }
 }
 
@@ -1797,17 +1825,17 @@ void Controller::PasteClipboardItemEvent(uint32_t id, const char* mimeType, cons
   }
 
   // text-controller allows only plain text type.
-  if(!strncmp(mimeType, MIME_TYPE_TEXT_PLAIN, strlen(MIME_TYPE_TEXT_PLAIN)))
+  if(!strncmp(mimeType, MIME_TYPE_TEXT_PLAIN, strlen(MIME_TYPE_TEXT_PLAIN) + 1 /* Compare include null-terminated char */))
   {
     EventHandler::PasteClipboardItemEvent(*this, data);
   }
-  else if(!strncmp(mimeType, MIME_TYPE_HTML, strlen(MIME_TYPE_HTML)))
+  else if(!strncmp(mimeType, MIME_TYPE_HTML, strlen(MIME_TYPE_HTML) + 1 /* Compare include null-terminated char */))
   {
     // This does not mean that text controls can parse html.
     // This is temporary code, as text controls do not support html type data.
     // Simply remove the tags inside the angle brackets.
     // Once multiple types and data can be stored in the clipboard, this code should be removed.
-    std::regex reg("<[^>]*>");
+    std::regex  reg("<[^>]*>");
     std::string result = regex_replace(data, reg, "");
 
     EventHandler::PasteClipboardItemEvent(*this, result.c_str());
@@ -1957,7 +1985,6 @@ void Controller::Process(bool postProcess)
   {
     Dali::Adaptor& adaptor = Dali::Adaptor::Get();
 
-    adaptor.UnregisterProcessor(*this, true);
     mImpl->mProcessorRegistered = false;
 
     if(NULL == mImpl->mIdleCallback)
@@ -1995,7 +2022,7 @@ Controller::~Controller()
   {
     if(mImpl->mProcessorRegistered)
     {
-      Dali::Adaptor::Get().UnregisterProcessor(*this, true);
+      Dali::Adaptor::Get().UnregisterProcessorOnce(*this, true);
     }
     if(mImpl->mIdleCallback)
     {
index 20ad0f0..867cdd8 100644 (file)
@@ -521,6 +521,13 @@ public: // Configure the text controller.
   float GetTextFitPointSize() const;
 
   /**
+   * @brief Sets the text fit point size.
+   *
+   * @param[in] pointSize The fited point size.
+   */
+  void SetTextFitPointSize(float pointSize);
+
+  /**
    * @brief Sets whether the text fit properties have changed.
    *
    * @param[in] changed Whether to changed the text fit.
@@ -810,6 +817,14 @@ public: // Update.
   void GetText(std::string& text) const;
 
   /**
+   * @brief Retrieve raw text previously set.
+   *
+   * @note Raw text including markup tag.
+   * @param[out] text A string of UTF-8 characters.
+   */
+  void GetRawText(std::string& text) const;
+
+  /**
    * @brief Retrieves number of characters previously set.
    *
    * @return A length of string of UTF-32 characters.
@@ -1704,6 +1719,13 @@ public: // Default style & Input style
    */
   const Vector4 GetBackgroundColorWithCutout() const;
 
+  /**
+   * @brief Sets offset with cutout.
+   *
+   * @param[in] offset The offset.
+   */
+  void SetOffsetWithCutout(const Vector2& offset);
+
 public: // Queries & retrieves.
   /**
    * @brief Return the layout engine.
@@ -1991,6 +2013,22 @@ public: // Queries & retrieves.
    */
   bool IsShowingRealText() const;
 
+  /**
+   * @brief Sets the render mode of the text.
+   *
+   * By default is DevelTextLabel::Render::Sync.
+   *
+   * @param[in] renderMode Render mode of the text.
+   */
+  void SetRenderMode(DevelTextLabel::Render::Mode renderMode);
+
+  /**
+   * @brief Gets the render mode of the text.
+   * @return The value of render mode of the text.
+   */
+  DevelTextLabel::Render::Mode GetRenderMode();
+
+
 public: // Relayout.
   /**
    * @brief Triggers a relayout which updates View (if necessary).
index 66f99a3..1550e92 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
 #include <dali-toolkit/internal/text/hyphenator.h>
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/text-abstraction/hyphenation.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/character-set-conversion.h>
@@ -34,9 +33,10 @@ namespace Text
 {
 const char* UTF8 = "UTF-8";
 
-Vector<bool> GetWordHyphens(const Character* word,
-                            Length           wordSize,
-                            const char*      lang)
+Vector<bool> GetWordHyphens(TextAbstraction::Hyphenation& hyphenation,
+                            const Character*              word,
+                            Length                        wordSize,
+                            const char*                   lang)
 {
   Vector<bool> hyphens;
 
@@ -46,8 +46,6 @@ Vector<bool> GetWordHyphens(const Character* word,
     return hyphens;
   }
 
-  TextAbstraction::Hyphenation hyphenation = TextAbstraction::Hyphenation::Get();
-
   // first get the needed encoding
   std::string text;
   if(strcmp(hyphenation.GetDictionaryEncoding(lang), UTF8) == 0)
index eef2246..9bb6437 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_HYPHENATOR_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/hyphenation.h>
 #include <dali/public-api/common/dali-vector.h>
 
 // INTERNAL INCLUDES
@@ -34,15 +35,17 @@ namespace Text
 /**
  * Gets a vector booleans that indicates possible hyphen locations.
  *
+ * @param[in] hyphenation Hyphenation to use in this function.
  * @param[in] word the word to get possible hyphens for.
  * @param[in] wordSize the word size in bytes.
  * @param[in] lang the language for the word
  *
  * @return vector of boolean, true if possible to hyphenate at this character position.
  */
-Vector<bool> GetWordHyphens(const Character* word,
-                            Length           wordSize,
-                            const char*      lang);
+Vector<bool> GetWordHyphens(TextAbstraction::Hyphenation& hyphenation,
+                            const Character*              word,
+                            Length                        wordSize,
+                            const char*                   lang);
 
 } // namespace Text
 
index e78a544..328f86e 100644 (file)
@@ -555,8 +555,11 @@ struct Engine::Impl
     {
       Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
 
+      TextAbstraction::BidirectionalSupport bidirectionalSupport = parameters.bidirectionalSupport;
+
       // Sets the visual to logical map tables needed to reorder the text.
-      ReorderLine(bidirectionalParagraphInfo,
+      ReorderLine(bidirectionalSupport,
+                  bidirectionalParagraphInfo,
                   bidirectionalLinesInfo,
                   bidiParameters.bidiLineIndex,
                   lineLayout.characterIndex,
@@ -648,8 +651,11 @@ struct Engine::Impl
     // Remove current reordered line.
     bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
 
+    TextAbstraction::BidirectionalSupport bidirectionalSupport = parameters.bidirectionalSupport;
+
     // Re-build the conversion table without the removed glyphs.
-    ReorderLine(bidirectionalParagraphInfo,
+    ReorderLine(bidirectionalSupport,
+                bidirectionalParagraphInfo,
                 bidirectionalLinesInfo,
                 bidiParameters.bidiLineIndex,
                 lineLayout.characterIndex,
@@ -1090,7 +1096,7 @@ struct Engine::Impl
         hyphenGlyph        = GlyphInfo();
         hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
 
-        TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+        TextAbstraction::FontClient fontClient = parameters.fontClient;
         hyphenGlyph.index                      = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
 
         mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
@@ -1415,7 +1421,7 @@ struct Engine::Impl
         }
       }
 
-      const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
+      const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && (layoutBidiParameters.bidiLineIndex < bidirectionalLinesInfo.Count())) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
 
       if((nullptr != bidirectionalLineInfo) &&
          !bidirectionalLineInfo->isIdentity &&
index f8a62b3..0e82787 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_LAYOUT_PARAMETERS_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/bidirectional-support.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/public-api/math/vector2.h>
 
 // INTERNAL INCLUDES
@@ -46,11 +48,17 @@ struct Parameters
    *
    * @param[in] boundingBox The size of the box containing the text.
    * @param[in,out] textModel The text's model.
+   * @param[in] fontClient The FontClient to use obtain glyph information.
+   * @param[in] bidirectionalSupport The BidirectionalSupport for using lines reorders.
    */
-  Parameters(const Vector2& boundingBox,
-             ModelPtr       textModel)
+  Parameters(const Vector2&                         boundingBox,
+             ModelPtr                               textModel,
+             TextAbstraction::FontClient&           fontClient,
+             TextAbstraction::BidirectionalSupport& bidirectionalSupport)
   : boundingBox{boundingBox},
     textModel{textModel},
+    fontClient{fontClient},
+    bidirectionalSupport{bidirectionalSupport},
     lineBidirectionalInfoRunsBuffer{nullptr},
     numberOfBidirectionalInfoRuns{0u},
     startGlyphIndex{0u},
@@ -62,16 +70,18 @@ struct Parameters
   {
   }
 
-  Vector2                   boundingBox; ///< The size of the box containing the text.
-  ModelPtr                  textModel;
-  BidirectionalLineInfoRun* lineBidirectionalInfoRunsBuffer; ///< Bidirectional conversion tables per line.
-  Length                    numberOfBidirectionalInfoRuns;   ///< The number of lines with bidirectional info.
-  GlyphIndex                startGlyphIndex;                 ///< Index to the first glyph to layout.
-  Length                    numberOfGlyphs;                  ///< The number of glyphs to layout.
-  LineIndex                 startLineIndex;                  ///< The line index where to insert the new lines.
-  Length                    estimatedNumberOfLines;          ///< The estimated number of lines.
-  float                     interGlyphExtraAdvance;          ///< Extra advance added to each glyph.
-  bool                      isLastNewParagraph : 1;          ///< Whether the last character is a new paragraph character.
+  Vector2                               boundingBox;                     ///< The size of the box containing the text.
+  ModelPtr                              textModel;                       ///< The text's model.
+  TextAbstraction::FontClient           fontClient;                      ///< FontClient to use obtain glyph information.
+  TextAbstraction::BidirectionalSupport bidirectionalSupport;            ///< BidirectionalSupport for using lines reorders.
+  BidirectionalLineInfoRun*             lineBidirectionalInfoRunsBuffer; ///< Bidirectional conversion tables per line.
+  Length                                numberOfBidirectionalInfoRuns;   ///< The number of lines with bidirectional info.
+  GlyphIndex                            startGlyphIndex;                 ///< Index to the first glyph to layout.
+  Length                                numberOfGlyphs;                  ///< The number of glyphs to layout.
+  LineIndex                             startLineIndex;                  ///< The line index where to insert the new lines.
+  Length                                estimatedNumberOfLines;          ///< The estimated number of lines.
+  float                                 interGlyphExtraAdvance;          ///< Extra advance added to each glyph.
+  bool                                  isLastNewParagraph : 1;          ///< Whether the last character is a new paragraph character.
 };
 
 } // namespace Layout
index 41ba5ac..c5a823a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/common/singleton-service.h>
-#include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
@@ -42,13 +41,6 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULT
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
 
 const Dali::Toolkit::Text::Character UTF32_A = 0x0041;
-
-// TODO : Customization required for these values.
-constexpr std::size_t MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE = 63;
-constexpr std::size_t MAX_DEFAULT_FONTS_CACHE_SIZE             = 15;
-
-constexpr int VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT = 8;
-constexpr int DEFAULT_FONTS_REMAIN_COUNT             = 2;
 } // namespace
 
 namespace Text
@@ -173,7 +165,13 @@ void CheckFontSupportsCharacter(
                 *(defaultFontPerScriptCacheBuffer + script) = defaultFontsPerScript;
               }
             }
-            defaultFontsPerScript->Cache(currentFontDescription, fontId);
+
+            // the fontId is cached only if it has not been cached before.
+            if(!isValidCachedDefaultFont)
+            {
+              defaultFontsPerScript->Cache(currentFontDescription, fontId);
+            }
+
             isValidFont = true;
           }
         }
@@ -201,21 +199,14 @@ bool ValidateFontsPerScript::IsValidFont(FontId fontId) const
 void ValidateFontsPerScript::Cache(FontId fontId)
 {
   mValidFonts.PushBack(fontId);
-  if(MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE < mValidFonts.Count())
-  {
-    // Clear cache but remaind some last items.
-    const auto offset = mValidFonts.Count() - VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT;
-    for(int i = 0; i < VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT; ++i)
-    {
-      mValidFonts[i] = std::move(mValidFonts[offset + i]);
-    }
-    mValidFonts.Resize(VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT);
-  }
+
+  return;
 }
 
 FontId DefaultFonts::FindFont(TextAbstraction::FontClient&            fontClient,
                               const TextAbstraction::FontDescription& description,
-                              PointSize26Dot6                         size) const
+                              PointSize26Dot6                         size,
+                              Character                               character) const
 {
   for(std::vector<CacheItem>::const_iterator it    = mFonts.begin(),
                                              endIt = mFonts.end();
@@ -228,7 +219,8 @@ FontId DefaultFonts::FindFont(TextAbstraction::FontClient&            fontClient
        ((TextAbstraction::FontWidth::NONE == description.width) || (description.width == item.description.width)) &&
        ((TextAbstraction::FontSlant::NONE == description.slant) || (description.slant == item.description.slant)) &&
        (size == fontClient.GetPointSize(item.fontId)) &&
-       (description.family.empty() || (description.family == item.description.family)))
+       (description.family.empty() || (description.family == item.description.family)) &&
+       fontClient.IsCharacterSupportedByFont(item.fontId, character))
     {
       return item.fontId;
     }
@@ -243,19 +235,11 @@ void DefaultFonts::Cache(const TextAbstraction::FontDescription& description, Fo
   item.description = description;
   item.fontId      = fontId;
   mFonts.push_back(item);
-  if(MAX_DEFAULT_FONTS_CACHE_SIZE < mFonts.size())
-  {
-    // Clear cache but remaind some last items.
-    const auto offset = mFonts.size() - DEFAULT_FONTS_REMAIN_COUNT;
-    for(int i = 0; i < DEFAULT_FONTS_REMAIN_COUNT; ++i)
-    {
-      mFonts[i] = std::move(mFonts[offset + i]);
-    }
-    mFonts.resize(DEFAULT_FONTS_REMAIN_COUNT);
-  }
+
+  return;
 }
 
-MultilanguageSupport::MultilanguageSupport()
+MultilanguageSupport::MultilanguageSupport(bool connectLocaleChangedSignal)
 : mDefaultFontPerScriptCache(),
   mValidFontsPerScriptCache(),
   mLocale(std::string())
@@ -268,7 +252,7 @@ MultilanguageSupport::MultilanguageSupport()
   // Reserves space to cache the valid fonts and access them with the script as an index.
   mValidFontsPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL);
 
-  if(Dali::Adaptor::IsAvailable())
+  if(connectLocaleChangedSignal && Dali::Adaptor::IsAvailable())
   {
     Dali::Adaptor::Get().LocaleChangedSignal().Connect(this, &MultilanguageSupport::OnLocaleChanged);
   }
@@ -387,6 +371,10 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
   // Whether the first valid script is a right to left script.
   bool isParagraphRTL = false;
 
+  // Whether there is an RTL marker in the invalid script.
+  // This variable was added to solve the problem that autoscroll does not work properly when there are only RTL Marker and LTR Text.
+  bool hasRTLMarker = false;
+
   // Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'.
   Length numberOfAllScriptCharacters = 0u;
 
@@ -449,6 +437,7 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
     {
       // Check if whether is right to left markup and Keeps true if the previous value was true.
       currentScriptRun.isRightToLeft = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftMark(character);
+      hasRTLMarker = hasRTLMarker || TextAbstraction::IsRightToLeftMark(character);
 
       // Count all these characters to be added into a script.
       ++numberOfAllScriptCharacters;
@@ -548,12 +537,14 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
       // Adds the white spaces which are at the begining of the script.
       numberOfAllScriptCharacters++;
       AddCurrentScriptAndCreatNewScript(script,
-                                        TextAbstraction::IsRightToLeftScript(script),
+                                        hasRTLMarker ? true : TextAbstraction::IsRightToLeftScript(script),
                                         true,
                                         currentScriptRun,
                                         numberOfAllScriptCharacters,
                                         scripts,
                                         scriptIndex);
+
+      hasRTLMarker = false;
     }
     else
     {
@@ -597,7 +588,8 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
   }
 }
 
-void MultilanguageSupport::ValidateFonts(const Vector<Character>&                text,
+void MultilanguageSupport::ValidateFonts(TextAbstraction::FontClient&            fontClient,
+                                         const Vector<Character>&                text,
                                          const Vector<ScriptRun>&                scripts,
                                          const Vector<FontDescriptionRun>&       fontDescriptions,
                                          const TextAbstraction::FontDescription& defaultFontDescription,
@@ -653,9 +645,6 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
   currentFontRun.isBoldRequired                  = false;
   currentFontRun.isItalicRequired                = false;
 
-  // Get the font client.
-  TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
-
   const Character* const textBuffer = text.Begin();
 
   // Iterators of the script runs.
@@ -724,7 +713,8 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
       // This cache stores fall-back fonts.
       cachedDefaultFontId = defaultFonts->FindFont(fontClient,
                                                    currentFontDescription,
-                                                   currentFontPointSize);
+                                                   currentFontPointSize,
+                                                   character);
     }
 
     // Whether the cached default font is valid.
index 2f0a904..d33f785 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_MULTI_LANGUAGE_SUPPORT_IMPL_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -116,7 +116,8 @@ struct DefaultFonts
    */
   FontId FindFont(TextAbstraction::FontClient&            fontClient,
                   const TextAbstraction::FontDescription& description,
-                  PointSize26Dot6                         size) const;
+                  PointSize26Dot6                         size,
+                  Character                               character) const;
 
   /**
    * @brief Cache a default font for the given @p size.
@@ -139,7 +140,7 @@ public:
   /**
    * Constructor
    */
-  MultilanguageSupport();
+  MultilanguageSupport(bool connectLocaleChangedSignal = true);
 
   /**
    * Destructor
@@ -164,7 +165,8 @@ public:
   /**
    * @copydoc Dali::MultilanguageSupport::ValidateFonts()
    */
-  void ValidateFonts(const Vector<Character>&                text,
+  void ValidateFonts(TextAbstraction::FontClient&            fontClient,
+                     const Vector<Character>&                text,
                      const Vector<ScriptRun>&                scripts,
                      const Vector<FontDescriptionRun>&       fontDescriptions,
                      const TextAbstraction::FontDescription& defaultFontDescription,
@@ -181,7 +183,7 @@ public:
   void OnLocaleChanged(std::string locale);
 
   /**
-   * @brief Clear font caches when locale changed.
+   * @copydoc Dali::MultilanguageSupport::ClearCache()
    */
   void ClearCache();
 
index fd930c8..e81d7d8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -45,6 +45,13 @@ MultilanguageSupport MultilanguageSupport::Get()
   return Internal::MultilanguageSupport::Get();
 }
 
+MultilanguageSupport MultilanguageSupport::New(bool connectLocaleChangedSignal)
+{
+  auto multilanguageSupportImpl = new Internal::MultilanguageSupport(connectLocaleChangedSignal);
+
+  return MultilanguageSupport(multilanguageSupportImpl);
+}
+
 void MultilanguageSupport::SetScripts(const Vector<Character>& text,
                                       CharacterIndex           startIndex,
                                       Length                   numberOfCharacters,
@@ -56,7 +63,8 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
                                       scripts);
 }
 
-void MultilanguageSupport::ValidateFonts(const Vector<Character>&                text,
+void MultilanguageSupport::ValidateFonts(TextAbstraction::FontClient&            fontClient,
+                                         const Vector<Character>&                text,
                                          const Vector<ScriptRun>&                scripts,
                                          const Vector<FontDescriptionRun>&       fontDescriptions,
                                          const TextAbstraction::FontDescription& defaultFontDescription,
@@ -66,7 +74,8 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
                                          Length                                  numberOfCharacters,
                                          Vector<FontRun>&                        fonts)
 {
-  GetImplementation(*this).ValidateFonts(text,
+  GetImplementation(*this).ValidateFonts(fontClient,
+                                         text,
                                          scripts,
                                          fontDescriptions,
                                          defaultFontDescription,
@@ -77,6 +86,11 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
                                          fonts);
 }
 
+void MultilanguageSupport::ClearCache()
+{
+  GetImplementation(*this).ClearCache();
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index 5d15956..22c3c66 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_MULTI_LANGUAGE_SUPPORT_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/public-api/common/dali-vector.h>
 #include <dali/public-api/object/base-handle.h>
 
@@ -72,6 +73,14 @@ public:
   static MultilanguageSupport Get();
 
   /**
+   * @brief Create a handle to the new MultilanguageSupport instance.
+   *
+   * @param[in] connectLocaleChangedSignal Whether to connect Locale changed signal, default is true.
+   * @return A handle to the MultilanguageSupport.
+   */
+  static MultilanguageSupport New(bool connectLocaleChangedSignal);
+
+  /**
    * @brief Sets the scripts of the whole text.
    *
    * Scripts are used to validate and set default fonts and to shape the text in further steps.
@@ -105,6 +114,7 @@ public:
    * If a font has been set by the application developer, this method checks if the font supports the character.
    * If it doesn't, this method replaces it by a default one.
    *
+   * @param[in] fontClient FontClient to use in this function.
    * @param[in] text Vector of UTF-32 characters.
    * @param[in] scripts Vector containing the script runs for the whole text.
    * @param[in] fontDescriptions The fonts set through the mark-up string or the input style set through the property system.
@@ -115,7 +125,8 @@ public:
    * @param[in] numberOfCharacters The number of characters to set the font.
    * @param[out] fonts The validated fonts.
    */
-  void ValidateFonts(const Vector<Character>&                text,
+  void ValidateFonts(TextAbstraction::FontClient&            fontClient,
+                     const Vector<Character>&                text,
                      const Vector<ScriptRun>&                scripts,
                      const Vector<FontDescriptionRun>&       fontDescriptions,
                      const TextAbstraction::FontDescription& defaultFontDescription,
@@ -125,6 +136,11 @@ public:
                      Length                                  numberOfCharacters,
                      Vector<FontRun>&                        fonts);
 
+  /**
+   * @brief Clear font caches when locale changed.
+   */
+  void ClearCache();
+
 public:
   // Default copy and move operator
   MultilanguageSupport(const MultilanguageSupport& rhs) = default;
diff --git a/dali-toolkit/internal/text/rendering/text-typesetter-impl.cpp b/dali-toolkit/internal/text/rendering/text-typesetter-impl.cpp
new file mode 100644 (file)
index 0000000..fcf2c56
--- /dev/null
@@ -0,0 +1,1409 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/rendering/text-typesetter-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
+#include <dali/public-api/common/constants.h>
+#include <dali/public-api/math/math-utils.h>
+#include <memory.h>
+#include <cmath>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
+#include <dali-toolkit/internal/text/character-spacing-glyph-run.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
+#include <dali-toolkit/internal/text/line-helper-functions.h>
+#include <dali-toolkit/internal/text/line-run.h>
+#include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
+#include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
+#include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
+#include <dali-toolkit/internal/text/rendering/view-model.h>
+#include <dali-toolkit/internal/text/strikethrough-glyph-run.h>
+#include <dali-toolkit/internal/text/text-definitions.h>
+#include <dali-toolkit/internal/text/underlined-glyph-run.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+namespace
+{
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
+
+const float HALF(0.5f);
+const float ONE_AND_A_HALF(1.5f);
+
+/**
+ * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
+ *
+ * @param x The value between [0..255]
+ * @param y The value between [0..255]
+ * @return (x*y)/255
+ */
+inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
+{
+  const uint32_t xy = static_cast<const uint32_t>(x) * y;
+  return ((xy << 15) + (xy << 7) + xy) >> 23;
+}
+
+/// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
+// clang-format off
+/**
+ * @brief Prepare decode glyph bitmap data. It must be call END_GLYPH_BITMAP end of same scope.
+ */
+#define BEGIN_GLYPH_BITMAP(data)                                                                                                                \
+{                                                                                                                                               \
+  uint32_t   glyphOffet               = 0u;                                                                                                     \
+  const bool useLocalScanline         = data.glyphBitmap.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;  \
+  uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer; \
+  DALI_ASSERT_ALWAYS(glyphScanline && "Glyph scanline for buffer is nullptr!");
+
+/**
+ * @brief Macro to skip useless line fast.
+ */
+#define SKIP_GLYPH_SCANLINE(skipLine)                                                                  \
+if(useLocalScanline)                                                                                   \
+{                                                                                                      \
+  for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex)                                        \
+  {                                                                                                    \
+    TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
+  }                                                                                                    \
+}                                                                                                      \
+else                                                                                                   \
+{                                                                                                      \
+  glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);           \
+}
+
+/**
+ * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
+ */
+#define BEGIN_GLYPH_SCANLINE_DECODE(data)                                                              \
+{                                                                                                      \
+  if(useLocalScanline)                                                                                 \
+  {                                                                                                    \
+    TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
+  }
+
+/**
+ * @brief Finalize scanline of glyph bitmap data per each lines.
+ */
+#define END_GLYPH_SCANLINE_DECODE(data)                       \
+  if(!useLocalScanline)                                       \
+  {                                                           \
+    glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
+  }                                                           \
+} // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
+
+/**
+ * @brief Finalize decode glyph bitmap data.
+ */
+#define END_GLYPH_BITMAP() \
+  if(useLocalScanline)     \
+  {                        \
+    free(glyphScanline);   \
+  }                        \
+} // For ensure that we call BEGIN_GLYPH_BITMAP before
+
+// clang-format on
+/// Helper macro define end.
+
+/**
+ * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
+ */
+struct GlyphData
+{
+  Devel::PixelBuffer               bitmapBuffer;     ///< The buffer of the whole bitmap. The format is RGBA8888.
+  Vector2*                         position;         ///< The position of the glyph.
+  TextAbstraction::GlyphBufferData glyphBitmap;      ///< The glyph's bitmap.
+  uint32_t                         width;            ///< The bitmap's width.
+  uint32_t                         height;           ///< The bitmap's height.
+  int32_t                          horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
+  int32_t                          verticalOffset;   ///< The vertical offset to be added to the 'y' glyph's position.
+};
+
+/**
+ * @brief Sets the glyph's buffer into the bitmap's buffer.
+ *
+ * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
+ * @param[in] position The position of the glyph.
+ * @param[in] color The color of the glyph.
+ * @param[in] style The style of the text.
+ * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+ */
+void TypesetGlyph(GlyphData& __restrict__ data,
+                  const Vector2* const __restrict__ position,
+                  const Vector4* const __restrict__ color,
+                  const Typesetter::Style style,
+                  const Pixel::Format     pixelFormat)
+{
+  if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
+  {
+    // Nothing to do if the width or height of the buffer is zero.
+    return;
+  }
+
+  // Initial vertical / horizontal offset.
+  const int32_t yOffset = data.verticalOffset + position->y;
+  const int32_t xOffset = data.horizontalOffset + position->x;
+
+  // Whether the given glyph is a color one.
+  const bool     isColorGlyph    = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
+  const uint32_t glyphPixelSize  = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
+  const uint32_t glyphAlphaIndex = (glyphPixelSize > 0u) ? glyphPixelSize - 1u : 0u;
+
+  // Determinate iterator range.
+  const int32_t lineIndexRangeMin = std::max(0, -yOffset);
+  const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
+  const int32_t indexRangeMin     = std::max(0, -xOffset);
+  const int32_t indexRangeMax     = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
+
+  // If current glyph don't need to be rendered, just ignore.
+  if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
+  {
+    return;
+  }
+
+  if(Pixel::RGBA8888 == pixelFormat)
+  {
+    uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
+    // Skip basic line.
+    bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
+
+    // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
+    // Just overwrite transparent color and return.
+    if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
+    {
+      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
+      {
+        // We can use memset here.
+        memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
+        bitmapBuffer += data.width;
+      }
+      return;
+    }
+
+    const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
+
+    // Precalculate input color's packed result.
+    uint32_t packedInputColor                    = 0u;
+    uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
+
+    *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
+    *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
+    *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
+    *(packedInputColorBuffer)      = static_cast<uint8_t>(color->r * 255);
+
+    // Prepare glyph bitmap
+    BEGIN_GLYPH_BITMAP(data);
+
+    // Skip basic line of glyph.
+    SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
+
+    // Traverse the pixels of the glyph line per line.
+    if(isColorGlyph)
+    {
+      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
+      {
+        BEGIN_GLYPH_SCANLINE_DECODE(data);
+
+        for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
+        {
+          const int32_t xOffsetIndex = xOffset + index;
+
+          // Retrieves the color from the color glyph.
+          uint32_t packedColorGlyph                    = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
+          uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
+
+          // Update the alpha channel.
+          const uint8_t colorAlpha       = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
+          *(packedColorGlyphBuffer + 3u) = colorAlpha;
+
+          if(Typesetter::STYLE_SHADOW == style)
+          {
+            // The shadow of color glyph needs to have the shadow color.
+            *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
+            *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
+            *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
+          }
+          else
+          {
+            if(swapChannelsBR)
+            {
+              std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
+            }
+
+            *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
+            *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
+            *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
+
+            if(data.glyphBitmap.isColorBitmap)
+            {
+              *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
+              *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
+              *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
+            }
+          }
+
+          // Set the color into the final pixel buffer.
+          *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
+        }
+
+        bitmapBuffer += data.width;
+
+        END_GLYPH_SCANLINE_DECODE(data);
+      }
+    }
+    else
+    {
+      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
+      {
+        BEGIN_GLYPH_SCANLINE_DECODE(data);
+
+        for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
+        {
+          // Update the alpha channel.
+          const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
+
+          // Copy non-transparent pixels only
+          if(alpha > 0u)
+          {
+            const int32_t xOffsetIndex = xOffset + index;
+
+            // Check alpha of overlapped pixels
+            uint32_t& currentColor             = *(bitmapBuffer + xOffsetIndex);
+            uint8_t*  packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(&currentColor);
+
+            // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
+            // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
+            // semi-transparent gaps between joint glyphs with overlapped pixels, which could
+            // happen, for example, in the RTL text when we copy glyphs from right to left).
+            uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
+            currentAlpha         = std::max(currentAlpha, alpha);
+            if(currentAlpha == 255)
+            {
+              // Fast-cut to avoid float type operation.
+              currentColor = packedInputColor;
+            }
+            else
+            {
+              // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
+              // The format is RGBA8888.
+              uint32_t packedColor                    = 0u;
+              uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
+
+              // Color is pre-muliplied with its alpha.
+              *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
+              *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
+              *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
+              *(packedColorBuffer)      = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
+
+              // Set the color into the final pixel buffer.
+              currentColor = packedColor;
+            }
+          }
+        }
+
+        bitmapBuffer += data.width;
+
+        END_GLYPH_SCANLINE_DECODE(data);
+      }
+    }
+
+    END_GLYPH_BITMAP();
+  }
+  else // Pixel::L8
+  {
+    // Below codes required only if not color glyph.
+    if(!isColorGlyph)
+    {
+      uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
+      // Skip basic line.
+      bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
+
+      // Prepare glyph bitmap
+      BEGIN_GLYPH_BITMAP(data);
+
+      // Skip basic line of glyph.
+      SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
+
+      // Traverse the pixels of the glyph line per line.
+      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
+      {
+        BEGIN_GLYPH_SCANLINE_DECODE(data);
+
+        for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
+        {
+          const int32_t xOffsetIndex = xOffset + index;
+
+          // Update the alpha channel.
+          const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
+
+          // Copy non-transparent pixels only
+          if(alpha > 0u)
+          {
+            // Check alpha of overlapped pixels
+            uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
+
+            // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
+            // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
+            // semi-transparent gaps between joint glyphs with overlapped pixels, which could
+            // happen, for example, in the RTL text when we copy glyphs from right to left).
+            currentAlpha = std::max(currentAlpha, alpha);
+          }
+        }
+
+        bitmapBuffer += data.width;
+
+        END_GLYPH_SCANLINE_DECODE(data);
+      }
+
+      END_GLYPH_BITMAP();
+    }
+  }
+}
+
+/// Draws the background color to the buffer
+void DrawBackgroundColor(
+  Vector4        backgroundColor,
+  const uint32_t bufferWidth,
+  const uint32_t bufferHeight,
+  GlyphData&     glyphData,
+  const float    baseline,
+  const LineRun& line,
+  const float    lineExtentLeft,
+  const float    lineExtentRight)
+{
+  const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
+  const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
+  const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
+  const int32_t xRangeMax = std::min(static_cast<int32_t>(bufferWidth), static_cast<int32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
+
+  // If current glyph don't need to be rendered, just ignore.
+  if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
+  {
+    return;
+  }
+
+  // We can optimize by memset when backgroundColor.a is near zero
+  uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
+
+  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
+
+  // Skip yRangeMin line.
+  bitmapBuffer += yRangeMin * glyphData.width;
+
+  if(backgroundColorAlpha == 0)
+  {
+    for(int32_t y = yRangeMin; y < yRangeMax; y++)
+    {
+      // We can use memset.
+      memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
+      bitmapBuffer += glyphData.width;
+    }
+  }
+  else
+  {
+    uint32_t packedBackgroundColor       = 0u;
+    uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
+
+    // Write the color to the pixel buffer
+    *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
+    *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
+    *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
+    *(packedBackgroundColorBuffer)      = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
+
+    for(int32_t y = yRangeMin; y < yRangeMax; y++)
+    {
+      for(int32_t x = xRangeMin; x < xRangeMax; x++)
+      {
+        // Note : this is same logic as bitmap[y][x] = backgroundColor;
+        *(bitmapBuffer + x) = packedBackgroundColor;
+      }
+      bitmapBuffer += glyphData.width;
+    }
+  }
+}
+
+/// Draws the specified underline color to the buffer
+void DrawUnderline(
+  const uint32_t                  bufferWidth,
+  const uint32_t                  bufferHeight,
+  GlyphData&                      glyphData,
+  const float                     baseline,
+  const float                     currentUnderlinePosition,
+  const float                     maxUnderlineHeight,
+  const float                     lineExtentLeft,
+  const float                     lineExtentRight,
+  const UnderlineStyleProperties& commonUnderlineProperties,
+  const UnderlineStyleProperties& currentUnderlineProperties,
+  const LineRun&                  line)
+{
+  const Vector4&              underlineColor       = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
+  const Text::Underline::Type underlineType        = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
+  const float                 dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
+  const float                 dashedUnderlineGap   = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
+
+  int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
+
+  const uint32_t yRangeMin = underlineYOffset;
+  const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
+  const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
+  const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
+
+  // If current glyph don't need to be rendered, just ignore.
+  if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
+  {
+    return;
+  }
+
+  // We can optimize by memset when underlineColor.a is near zero
+  uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
+
+  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
+
+  // Skip yRangeMin line.
+  bitmapBuffer += yRangeMin * glyphData.width;
+
+  // Note if underlineType is DASHED, we cannot setup color by memset.
+  if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
+  {
+    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
+    {
+      // We can use memset.
+      memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
+      bitmapBuffer += glyphData.width;
+    }
+    if(underlineType == Text::Underline::DOUBLE)
+    {
+      int32_t        secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
+      const uint32_t secondYRangeMin        = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
+      const uint32_t secondYRangeMax        = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
+
+      // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
+      bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
+
+      for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
+      {
+        // We can use memset.
+        memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
+        bitmapBuffer += glyphData.width;
+      }
+    }
+  }
+  else
+  {
+    uint32_t packedUnderlineColor       = 0u;
+    uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
+
+    // Write the color to the pixel buffer
+    *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
+    *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
+    *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
+    *(packedUnderlineColorBuffer)      = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
+
+    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
+    {
+      if(underlineType == Text::Underline::DASHED)
+      {
+        float dashWidth = dashedUnderlineWidth;
+        float dashGap   = 0;
+
+        for(uint32_t x = xRangeMin; x < xRangeMax; x++)
+        {
+          if(Dali::EqualsZero(dashGap) && dashWidth > 0)
+          {
+            // Note : this is same logic as bitmap[y][x] = underlineColor;
+            *(bitmapBuffer + x) = packedUnderlineColor;
+            dashWidth--;
+          }
+          else if(dashGap < dashedUnderlineGap)
+          {
+            dashGap++;
+          }
+          else
+          {
+            //reset
+            dashWidth = dashedUnderlineWidth;
+            dashGap   = 0;
+          }
+        }
+      }
+      else
+      {
+        for(uint32_t x = xRangeMin; x < xRangeMax; x++)
+        {
+          // Note : this is same logic as bitmap[y][x] = underlineColor;
+          *(bitmapBuffer + x) = packedUnderlineColor;
+        }
+      }
+      bitmapBuffer += glyphData.width;
+    }
+    if(underlineType == Text::Underline::DOUBLE)
+    {
+      int32_t        secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
+      const uint32_t secondYRangeMin        = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
+      const uint32_t secondYRangeMax        = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
+
+      // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
+      bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
+
+      for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
+      {
+        for(uint32_t x = xRangeMin; x < xRangeMax; x++)
+        {
+          // Note : this is same logic as bitmap[y][x] = underlineColor;
+          *(bitmapBuffer + x) = packedUnderlineColor;
+        }
+        bitmapBuffer += glyphData.width;
+      }
+    }
+  }
+}
+
+/// Draws the specified strikethrough color to the buffer
+void DrawStrikethrough(const uint32_t                      bufferWidth,
+                       const uint32_t                      bufferHeight,
+                       GlyphData&                          glyphData,
+                       const float                         baseline,
+                       const float                         strikethroughStartingYPosition,
+                       const float                         maxStrikethroughHeight,
+                       const float                         lineExtentLeft,
+                       const float                         lineExtentRight,
+                       const StrikethroughStyleProperties& commonStrikethroughProperties,
+                       const StrikethroughStyleProperties& currentStrikethroughProperties,
+                       const LineRun&                      line)
+{
+  const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
+
+  const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
+  const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
+  const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
+  const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
+
+  // If current glyph don't need to be rendered, just ignore.
+  if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
+  {
+    return;
+  }
+
+  // We can optimize by memset when strikethroughColor.a is near zero
+  uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
+
+  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
+
+  // Skip yRangeMin line.
+  bitmapBuffer += yRangeMin * glyphData.width;
+
+  if(strikethroughColorAlpha == 0)
+  {
+    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
+    {
+      // We can use memset.
+      memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
+      bitmapBuffer += glyphData.width;
+    }
+  }
+  else
+  {
+    uint32_t packedStrikethroughColor       = 0u;
+    uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
+
+    // Write the color to the pixel buffer
+    *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
+    *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
+    *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
+    *(packedStrikethroughColorBuffer)      = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
+
+    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
+    {
+      for(uint32_t x = xRangeMin; x < xRangeMax; x++)
+      {
+        // Note : this is same logic as bitmap[y][x] = strikethroughColor;
+        *(bitmapBuffer + x) = packedStrikethroughColor;
+      }
+      bitmapBuffer += glyphData.width;
+    }
+  }
+}
+
+/// Helper functions to create image buffer
+
+struct InputParameterForEachLine
+{
+  const uint32_t bufferWidth;
+  const uint32_t bufferHeight;
+  const int32_t  horizontalOffset;
+
+  const Vector2& styleOffset; ///< If style is STYLE_OUTLINE, outline offset. If style is STYLE_SHADOW, shadow offset. Otherwise, zero.
+
+  const GlyphIndex fromGlyphIndex;
+  const GlyphIndex toGlyphIndex;
+
+  // Elide text info
+  const GlyphIndex startIndexOfGlyphs;
+  const GlyphIndex endIndexOfGlyphs;
+  const GlyphIndex firstMiddleIndexOfElidedGlyphs;
+  const GlyphIndex secondMiddleIndexOfElidedGlyphs;
+
+  const DevelText::VerticalLineAlignment::Type verticalLineAlignType;
+  const DevelText::EllipsisPosition::Type      ellipsisPosition;
+
+  const GlyphInfo* __restrict__ hyphens;
+  const Length* __restrict__ hyphenIndices;
+  const Length hyphensCount;
+
+  const bool ignoreHorizontalAlignment : 1;
+};
+
+struct InputParameterForEachGlyph
+{
+  const Typesetter::Style style;
+  const Pixel::Format     pixelFormat;
+
+  const float outlineWidth;
+
+  const float modelCharacterSpacing;
+
+  const Vector4& defaultColor; ///< The default color for the text.
+                               ///  Or some color which depends on style value. (e.g. ShadowColor if style is STYLE_SHADOW)
+
+  const Vector<UnderlinedGlyphRun>&       underlineRuns;
+  const Vector<StrikethroughGlyphRun>&    strikethroughRuns;
+  const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns;
+
+  const GlyphInfo* const __restrict__ glyphsBuffer;
+  const Character* __restrict__ textBuffer;
+  const CharacterIndex* __restrict__ glyphToCharacterMapBuffer;
+
+  const Vector2* const __restrict__ positionBuffer;
+
+  const Vector4* const __restrict__ colorsBuffer;
+  const TextAbstraction::ColorIndex* const __restrict__ colorIndexBuffer;
+
+  const UnderlineStyleProperties     modelUnderlineProperties;
+  const StrikethroughStyleProperties modelStrikethroughProperties;
+
+  const bool underlineEnabled : 1;
+  const bool strikethroughEnabled : 1;
+  const bool cutoutEnabled : 1;
+
+  const bool removeFrontInset : 1;
+  const bool removeBackInset : 1;
+
+  const bool useDefaultColor : 1;
+};
+
+struct OutputParameterForEachGlyph
+{
+  UnderlineStyleProperties& currentUnderlineProperties;
+
+  float& maxUnderlineHeight;
+  bool&  thereAreUnderlinedGlyphs;
+
+  StrikethroughStyleProperties& currentStrikethroughProperties;
+
+  float& maxStrikethroughHeight;
+  bool&  thereAreStrikethroughGlyphs;
+
+  float& currentUnderlinePosition;
+
+  float& baseline;
+  float& lineExtentLeft;
+  float& lineExtentRight;
+
+  FontId& lastFontId;
+};
+
+void CreateImageBufferForEachGlyph(TextAbstraction::FontClient fontClient, GlyphData& glyphData, GlyphIndex& glyphIndex, const GlyphIndex elidedGlyphIndex, const GlyphInfo* glyphInfo, const bool addHyphen, const InputParameterForEachGlyph& inputParamsForGlyph, OutputParameterForEachGlyph& outputParamsForGlyph)
+{
+  Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = inputParamsForGlyph.underlineRuns.End();
+  const bool                                underlineGlyph              = inputParamsForGlyph.underlineEnabled || IsGlyphUnderlined(glyphIndex, inputParamsForGlyph.underlineRuns, currentUnderlinedGlyphRunIt);
+  outputParamsForGlyph.currentUnderlineProperties                       = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, inputParamsForGlyph.underlineRuns, currentUnderlinedGlyphRunIt, inputParamsForGlyph.modelUnderlineProperties);
+  float currentUnderlineHeight                                          = outputParamsForGlyph.currentUnderlineProperties.height;
+
+  outputParamsForGlyph.thereAreUnderlinedGlyphs = outputParamsForGlyph.thereAreUnderlinedGlyphs || underlineGlyph;
+
+  Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = inputParamsForGlyph.strikethroughRuns.End();
+  const bool                                   strikethroughGlyph             = inputParamsForGlyph.strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, inputParamsForGlyph.strikethroughRuns, currentStrikethroughGlyphRunIt);
+  outputParamsForGlyph.currentStrikethroughProperties                         = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, inputParamsForGlyph.strikethroughRuns, currentStrikethroughGlyphRunIt, inputParamsForGlyph.modelStrikethroughProperties);
+  float currentStrikethroughHeight                                            = outputParamsForGlyph.currentStrikethroughProperties.height;
+
+  outputParamsForGlyph.thereAreStrikethroughGlyphs = outputParamsForGlyph.thereAreStrikethroughGlyphs || strikethroughGlyph;
+
+  // Are we still using the same fontId as previous
+  if((glyphInfo->fontId != outputParamsForGlyph.lastFontId) && (strikethroughGlyph || underlineGlyph))
+  {
+    // We need to fetch fresh font underline metrics
+    FontMetrics fontMetrics;
+    fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
+
+    //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
+    outputParamsForGlyph.currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
+
+    if(underlineGlyph)
+    {
+      CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, outputParamsForGlyph.maxUnderlineHeight);
+    }
+
+    if(strikethroughGlyph)
+    {
+      CalcualteStrikethroughHeight(currentStrikethroughHeight, outputParamsForGlyph.maxStrikethroughHeight);
+    }
+
+    // Update lastFontId because fontId is changed
+    outputParamsForGlyph.lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
+  }
+
+  // Retrieves the glyph's position.
+  Vector2 position = *(inputParamsForGlyph.positionBuffer + elidedGlyphIndex);
+
+  if(addHyphen)
+  {
+    GlyphInfo   tempInfo          = *(inputParamsForGlyph.glyphsBuffer + elidedGlyphIndex);
+    const float characterSpacing  = GetGlyphCharacterSpacing(glyphIndex, inputParamsForGlyph.characterSpacingGlyphRuns, inputParamsForGlyph.modelCharacterSpacing);
+    const float calculatedAdvance = GetCalculatedAdvance(*(inputParamsForGlyph.textBuffer + (*(inputParamsForGlyph.glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
+    position.x                    = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
+    position.y                    = -glyphInfo->yBearing;
+  }
+
+  if(outputParamsForGlyph.baseline < position.y + glyphInfo->yBearing)
+  {
+    outputParamsForGlyph.baseline = position.y + glyphInfo->yBearing;
+  }
+
+  // Calculate the positions of leftmost and rightmost glyphs in the current line
+  if(inputParamsForGlyph.removeFrontInset)
+  {
+    if(position.x < outputParamsForGlyph.lineExtentLeft)
+    {
+      outputParamsForGlyph.lineExtentLeft = position.x;
+    }
+  }
+  else
+  {
+    const float originPositionLeft = position.x - glyphInfo->xBearing;
+    if(originPositionLeft < outputParamsForGlyph.lineExtentLeft)
+    {
+      outputParamsForGlyph.lineExtentLeft = originPositionLeft;
+    }
+  }
+
+  if(inputParamsForGlyph.removeBackInset)
+  {
+    if(position.x + glyphInfo->width > outputParamsForGlyph.lineExtentRight)
+    {
+      outputParamsForGlyph.lineExtentRight = position.x + glyphInfo->width;
+    }
+  }
+  else
+  {
+    const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
+    if(originPositionRight > outputParamsForGlyph.lineExtentRight)
+    {
+      outputParamsForGlyph.lineExtentRight = originPositionRight;
+    }
+  }
+
+  // Retrieves the glyph's color.
+  const ColorIndex colorIndex = inputParamsForGlyph.useDefaultColor ? 0u : *(inputParamsForGlyph.colorIndexBuffer + glyphIndex);
+
+  Vector4 color;
+  if(inputParamsForGlyph.style == Typesetter::STYLE_SHADOW)
+  {
+    color = inputParamsForGlyph.defaultColor;
+  }
+  else if(inputParamsForGlyph.style == Typesetter::STYLE_OUTLINE)
+  {
+    color = inputParamsForGlyph.defaultColor;
+  }
+  else
+  {
+    color = (inputParamsForGlyph.useDefaultColor || (0u == colorIndex)) ? inputParamsForGlyph.defaultColor : *(inputParamsForGlyph.colorsBuffer + (colorIndex - 1u));
+  }
+
+  if(inputParamsForGlyph.style == Typesetter::STYLE_NONE && inputParamsForGlyph.cutoutEnabled)
+  {
+    // Temporarily adjust the transparency to 1.f
+    color.a = 1.f;
+  }
+
+  // Premultiply alpha
+  color.r *= color.a;
+  color.g *= color.a;
+  color.b *= color.a;
+
+  // Retrieves the glyph's bitmap.
+  glyphData.glyphBitmap.buffer = nullptr;
+  glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
+  glyphData.glyphBitmap.height = glyphInfo->height;
+
+  float outlineWidth = inputParamsForGlyph.outlineWidth;
+
+  if(inputParamsForGlyph.style != Typesetter::STYLE_OUTLINE && inputParamsForGlyph.style != Typesetter::STYLE_SHADOW)
+  {
+    // Don't render outline for other styles
+    outlineWidth = 0.0f;
+  }
+
+  if(inputParamsForGlyph.style != Typesetter::STYLE_UNDERLINE && inputParamsForGlyph.style != Typesetter::STYLE_STRIKETHROUGH)
+  {
+    fontClient.CreateBitmap(glyphInfo->fontId,
+                            glyphInfo->index,
+                            glyphInfo->isItalicRequired,
+                            glyphInfo->isBoldRequired,
+                            glyphData.glyphBitmap,
+                            static_cast<int32_t>(outlineWidth));
+  }
+
+  // Sets the glyph's bitmap into the bitmap of the whole text.
+  if(nullptr != glyphData.glyphBitmap.buffer)
+  {
+    if(inputParamsForGlyph.style == Typesetter::STYLE_OUTLINE)
+    {
+      // Set the position offset for the current glyph
+      glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
+      glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
+    }
+
+    // Set the buffer of the glyph's bitmap into the final bitmap's buffer
+    TypesetGlyph(glyphData,
+                 &position,
+                 &color,
+                 inputParamsForGlyph.style,
+                 inputParamsForGlyph.pixelFormat);
+
+    if(inputParamsForGlyph.style == Typesetter::STYLE_OUTLINE)
+    {
+      // Reset the position offset for the next glyph
+      glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
+      glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
+    }
+
+    // free the glyphBitmap.buffer if it is owner of buffer
+    if(glyphData.glyphBitmap.isBufferOwned)
+    {
+      free(glyphData.glyphBitmap.buffer);
+      glyphData.glyphBitmap.isBufferOwned = false;
+    }
+    glyphData.glyphBitmap.buffer = nullptr;
+  }
+}
+
+void CreateImageBufferForEachLine(TextAbstraction::FontClient fontClient, GlyphData& glyphData, Length& hyphenIndex, const LineRun& line, const bool isFirstLine, const InputParameterForEachLine& inputParamsForLine, const InputParameterForEachGlyph& inputParamsForGlyph)
+{
+  // Sets the horizontal offset of the line.
+  glyphData.horizontalOffset = inputParamsForLine.ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
+  glyphData.horizontalOffset += inputParamsForLine.horizontalOffset;
+
+  // Increases the vertical offset with the line's ascender.
+  glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, inputParamsForLine.verticalLineAlignType));
+
+  if(inputParamsForGlyph.style == Typesetter::STYLE_OUTLINE)
+  {
+    glyphData.horizontalOffset -= inputParamsForGlyph.outlineWidth;
+    glyphData.horizontalOffset += inputParamsForLine.styleOffset.x;
+    if(isFirstLine)
+    {
+      // Only need to add the vertical outline offset for the first line
+      glyphData.verticalOffset -= inputParamsForGlyph.outlineWidth;
+      glyphData.verticalOffset += inputParamsForLine.styleOffset.y;
+    }
+  }
+  else if(inputParamsForGlyph.style == Typesetter::STYLE_SHADOW)
+  {
+    glyphData.horizontalOffset += inputParamsForLine.styleOffset.x - inputParamsForGlyph.outlineWidth; // if outline enabled then shadow should offset from outline
+
+    if(isFirstLine)
+    {
+      // Only need to add the vertical shadow offset for first line
+      glyphData.verticalOffset += inputParamsForLine.styleOffset.y - inputParamsForGlyph.outlineWidth;
+    }
+  }
+
+  bool thereAreUnderlinedGlyphs    = false;
+  bool thereAreStrikethroughGlyphs = false;
+
+  float currentUnderlinePosition   = 0.0f;
+  auto  currentUnderlineProperties = inputParamsForGlyph.modelUnderlineProperties;
+  float maxUnderlineHeight         = currentUnderlineProperties.height;
+
+  auto  currentStrikethroughProperties = inputParamsForGlyph.modelStrikethroughProperties;
+  float maxStrikethroughHeight         = currentStrikethroughProperties.height;
+
+  FontId lastFontId = 0;
+
+  float lineExtentLeft  = inputParamsForLine.bufferWidth;
+  float lineExtentRight = 0.0f;
+  float baseline        = 0.0f;
+  bool  addHyphen       = false;
+
+  // Traverses the glyphs of the line.
+  const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, inputParamsForLine.startIndexOfGlyphs), inputParamsForLine.fromGlyphIndex);
+  GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
+  endGlyphIndex                    = std::min(std::min(endGlyphIndex, inputParamsForLine.endIndexOfGlyphs), inputParamsForLine.toGlyphIndex);
+
+  for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
+  {
+    //To handle START case of ellipsis, the first glyph has been shifted
+    //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
+    GlyphIndex elidedGlyphIndex = glyphIndex - inputParamsForLine.startIndexOfGlyphs;
+
+    //To handle MIDDLE case of ellipsis, the first glyph in the second half of line has been shifted and skip the removed glyph from middle.
+    if(inputParamsForLine.ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
+    {
+      if(glyphIndex > inputParamsForLine.firstMiddleIndexOfElidedGlyphs &&
+         glyphIndex < inputParamsForLine.secondMiddleIndexOfElidedGlyphs)
+      {
+        // Ignore any glyph that removed for MIDDLE ellipsis
+        continue;
+      }
+      if(glyphIndex >= inputParamsForLine.secondMiddleIndexOfElidedGlyphs)
+      {
+        elidedGlyphIndex -= (inputParamsForLine.secondMiddleIndexOfElidedGlyphs - inputParamsForLine.firstMiddleIndexOfElidedGlyphs - 1u);
+      }
+    }
+
+    // Retrieve the glyph's info.
+    const GlyphInfo* glyphInfo;
+
+    if(addHyphen && inputParamsForLine.hyphens)
+    {
+      glyphInfo = inputParamsForLine.hyphens + hyphenIndex;
+      hyphenIndex++;
+    }
+    else
+    {
+      glyphInfo = inputParamsForGlyph.glyphsBuffer + elidedGlyphIndex;
+    }
+
+    if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
+       (glyphInfo->height < Math::MACHINE_EPSILON_1000))
+    {
+      // Nothing to do if the glyph's width or height is zero.
+      continue;
+    }
+
+    // Collect output l-values
+    // clang-format off
+    OutputParameterForEachGlyph outputParamsForGlyph{currentUnderlineProperties,
+
+                                                     maxUnderlineHeight,
+                                                     thereAreUnderlinedGlyphs,
+
+                                                     currentStrikethroughProperties,
+
+                                                     maxStrikethroughHeight,
+                                                     thereAreStrikethroughGlyphs,
+
+                                                     currentUnderlinePosition,
+
+                                                     baseline,
+                                                     lineExtentLeft,
+                                                     lineExtentRight,
+
+                                                     lastFontId};
+    // clang-format on
+
+    CreateImageBufferForEachGlyph(fontClient, glyphData, glyphIndex, elidedGlyphIndex, glyphInfo, addHyphen, inputParamsForGlyph, outputParamsForGlyph);
+
+    if(inputParamsForLine.hyphenIndices)
+    {
+      while((hyphenIndex < inputParamsForLine.hyphensCount) && (glyphIndex > inputParamsForLine.hyphenIndices[hyphenIndex]))
+      {
+        hyphenIndex++;
+      }
+
+      addHyphen = ((hyphenIndex < inputParamsForLine.hyphensCount) && ((glyphIndex + 1) == inputParamsForLine.hyphenIndices[hyphenIndex]));
+      if(addHyphen)
+      {
+        glyphIndex--;
+      }
+    }
+  }
+
+  // Draw the underline from the leftmost glyph to the rightmost glyph
+  if(thereAreUnderlinedGlyphs && inputParamsForGlyph.style == Typesetter::STYLE_UNDERLINE)
+  {
+    DrawUnderline(inputParamsForLine.bufferWidth, inputParamsForLine.bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, inputParamsForGlyph.modelUnderlineProperties, currentUnderlineProperties, line);
+  }
+
+  // Draw the background color from the leftmost glyph to the rightmost glyph
+  if(inputParamsForGlyph.style == Typesetter::STYLE_BACKGROUND)
+  {
+    DrawBackgroundColor(inputParamsForGlyph.defaultColor, inputParamsForLine.bufferWidth, inputParamsForLine.bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
+  }
+
+  // Draw the strikethrough from the leftmost glyph to the rightmost glyph
+  if(thereAreStrikethroughGlyphs && inputParamsForGlyph.style == Typesetter::STYLE_STRIKETHROUGH)
+  {
+    //TODO : The currently implemented strikethrough creates a strikethrough on the line level. We need to create different strikethroughs the case of glyphs with different sizes.
+    const float strikethroughStartingYPosition = (glyphData.verticalOffset + baseline + currentUnderlinePosition) - ((line.ascender) * HALF); // Since Free Type font doesn't contain the strikethrough-position property, strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
+    DrawStrikethrough(inputParamsForLine.bufferWidth, inputParamsForLine.bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, inputParamsForGlyph.modelStrikethroughProperties, currentStrikethroughProperties, line);
+  }
+
+  // Increases the vertical offset with the line's descender & line spacing.
+  glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, inputParamsForLine.verticalLineAlignType));
+}
+
+/// Helper functions to create image buffer end
+
+/**
+ * @brief Create an initialized image buffer filled with transparent color.
+ *
+ * Creates the pixel data used to generate the final image with the given size.
+ *
+ * @param[in] bufferWidth The width of the image buffer.
+ * @param[in] bufferHeight The height of the image buffer.
+ * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+ *
+ * @return An image buffer.
+ */
+inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
+{
+  Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
+
+  if(Pixel::RGBA8888 == pixelFormat)
+  {
+    const uint32_t bufferSizeInt  = bufferWidth * bufferHeight;
+    const size_t   bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
+    memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
+  }
+  else
+  {
+    memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
+  }
+
+  return imageBuffer;
+}
+
+} // namespace
+
+ViewModel* Typesetter::Impl::GetViewModel()
+{
+  return mModel.get();
+}
+
+void Typesetter::Impl::SetFontClient(TextAbstraction::FontClient& fontClient)
+{
+  mFontClient = fontClient;
+}
+
+TextAbstraction::FontClient& Typesetter::Impl::GetFontClient()
+{
+  return mFontClient;
+}
+
+Devel::PixelBuffer Typesetter::Impl::CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
+{
+  return Dali::Toolkit::Text::CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
+}
+
+void Typesetter::Impl::DrawGlyphsBackground(Devel::PixelBuffer& buffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const int32_t horizontalOffset, const int32_t verticalOffset)
+{
+  // Use l-value to make ensure it is not nullptr, so compiler happy.
+  auto& viewModel = *(mModel.get());
+
+  // Retrieve lines, glyphs, positions and colors from the view model.
+  const Length            modelNumberOfLines           = viewModel.GetNumberOfLines();
+  const LineRun* const    modelLinesBuffer             = viewModel.GetLines();
+  const Length            numberOfGlyphs               = viewModel.GetNumberOfGlyphs();
+  const GlyphInfo* const  glyphsBuffer                 = viewModel.GetGlyphs();
+  const Vector2* const    positionBuffer               = viewModel.GetLayout();
+  const Vector4* const    backgroundColorsBuffer       = viewModel.GetBackgroundColors();
+  const ColorIndex* const backgroundColorIndicesBuffer = viewModel.GetBackgroundColorIndices();
+  const bool              removeFrontInset             = viewModel.IsRemoveFrontInset();
+  const bool              removeBackInset              = viewModel.IsRemoveBackInset();
+
+  const DevelText::VerticalLineAlignment::Type verticalLineAlignType = viewModel.GetVerticalLineAlignment();
+
+  // Create and initialize the pixel buffer.
+  GlyphData glyphData;
+  glyphData.verticalOffset   = verticalOffset;
+  glyphData.width            = bufferWidth;
+  glyphData.height           = bufferHeight;
+  glyphData.bitmapBuffer     = buffer;
+  glyphData.horizontalOffset = 0;
+
+  ColorIndex prevBackgroundColorIndex = 0;
+  ColorIndex backgroundColorIndex     = 0;
+
+  // Traverses the lines of the text.
+  for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
+  {
+    const LineRun& line = *(modelLinesBuffer + lineIndex);
+
+    // Sets the horizontal offset of the line.
+    glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
+    glyphData.horizontalOffset += horizontalOffset;
+
+    // Increases the vertical offset with the line's ascender.
+    glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verticalLineAlignType));
+
+    float left     = bufferWidth;
+    float right    = 0.0f;
+    float baseline = 0.0f;
+
+    // Traverses the glyphs of the line.
+    const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
+    for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
+    {
+      // Retrieve the glyph's info.
+      const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
+
+      if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
+         (glyphInfo->height < Math::MACHINE_EPSILON_1000))
+      {
+        // Nothing to do if default background color, the glyph's width or height is zero.
+        continue;
+      }
+
+      backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
+
+      if((backgroundColorIndex != prevBackgroundColorIndex) &&
+         (prevBackgroundColorIndex != 0u))
+      {
+        const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
+        DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
+      }
+
+      if(backgroundColorIndex == 0u)
+      {
+        prevBackgroundColorIndex = backgroundColorIndex;
+        //if background color is the default do nothing
+        continue;
+      }
+
+      // Retrieves the glyph's position.
+      const Vector2* const position = positionBuffer + glyphIndex;
+
+      if(baseline < position->y + glyphInfo->yBearing)
+      {
+        baseline = position->y + glyphInfo->yBearing;
+      }
+
+      // Calculate the positions of leftmost and rightmost glyphs in the current line
+      if(removeFrontInset)
+      {
+        if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+        {
+          left = position->x;
+        }
+      }
+      else
+      {
+        const float originPositionLeft = position->x - glyphInfo->xBearing;
+        if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
+        {
+          left = originPositionLeft;
+        }
+      }
+
+      if(removeBackInset)
+      {
+        if(position->x + glyphInfo->width > right)
+        {
+          right = position->x + glyphInfo->width;
+        }
+      }
+      else
+      {
+        const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
+        if(originPositionRight > right)
+        {
+          right = originPositionRight;
+        }
+      }
+
+      prevBackgroundColorIndex = backgroundColorIndex;
+    }
+
+    //draw last background at line end if not default
+    if(backgroundColorIndex != 0u)
+    {
+      const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
+      DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
+    }
+
+    // Increases the vertical offset with the line's descender.
+    glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verticalLineAlignType));
+  }
+}
+
+Devel::PixelBuffer Typesetter::Impl::CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const GlyphIndex fromGlyphIndex, const GlyphIndex toGlyphIndex)
+{
+  // Use l-value to make ensure it is not nullptr, so compiler happy.
+  auto& viewModel = *(mModel.get());
+
+  // Retrieve lines, glyphs, positions and colors from the view model.
+  const Length modelNumberOfLines                       = viewModel.GetNumberOfLines();
+  const LineRun* const __restrict__ modelLinesBuffer    = viewModel.GetLines();
+  const GlyphInfo* const __restrict__ glyphsBuffer      = viewModel.GetGlyphs();
+  const Vector2* const __restrict__ positionBuffer      = viewModel.GetLayout();
+  const Vector4* const __restrict__ colorsBuffer        = viewModel.GetColors();
+  const ColorIndex* const __restrict__ colorIndexBuffer = viewModel.GetColorIndices();
+  const GlyphInfo* __restrict__ hyphens                 = viewModel.GetHyphens();
+  const Length* __restrict__ hyphenIndices              = viewModel.GetHyphenIndices();
+  const Length hyphensCount                             = viewModel.GetHyphensCount();
+
+  // Create and initialize the pixel buffer.
+  GlyphData glyphData;
+  glyphData.verticalOffset   = verticalOffset;
+  glyphData.width            = bufferWidth;
+  glyphData.height           = bufferHeight;
+  glyphData.bitmapBuffer     = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
+  glyphData.horizontalOffset = 0;
+
+  Length hyphenIndex = 0;
+
+  const Character* __restrict__ textBuffer                       = viewModel.GetTextBuffer();
+  const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = viewModel.GetGlyphsToCharacters();
+  const CharacterIndex* __restrict__ glyphToCharacterMapBuffer   = glyphToCharacterMap.Begin();
+
+  // Get the underline runs.
+  const Length               numberOfUnderlineRuns = viewModel.GetNumberOfUnderlineRuns();
+  Vector<UnderlinedGlyphRun> underlineRuns;
+  underlineRuns.Resize(numberOfUnderlineRuns);
+  viewModel.GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
+
+  // Get the strikethrough runs.
+  const Length                  numberOfStrikethroughRuns = viewModel.GetNumberOfStrikethroughRuns();
+  Vector<StrikethroughGlyphRun> strikethroughRuns;
+  strikethroughRuns.Resize(numberOfStrikethroughRuns);
+  viewModel.GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
+
+  // Get the character-spacing runs.
+  const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = viewModel.GetCharacterSpacingGlyphRuns();
+
+  // clang-format off
+  // Aggregate input parameter for each line from mModel
+  const InputParameterForEachLine inputParamsForLine{bufferWidth,
+                                                     bufferHeight,
+                                                     horizontalOffset,
+
+                                                     (style == Typesetter::STYLE_OUTLINE) ? viewModel.GetOutlineOffset() :
+                                                     (style == Typesetter::STYLE_SHADOW)  ? viewModel.GetShadowOffset()  :
+                                                     Vector2::ZERO,
+
+                                                     fromGlyphIndex,
+                                                     toGlyphIndex,
+
+                                                     // Elided text info. Indices according to elided text and Ellipsis position.
+                                                     viewModel.GetStartIndexOfElidedGlyphs(),
+                                                     viewModel.GetEndIndexOfElidedGlyphs(),
+                                                     viewModel.GetFirstMiddleIndexOfElidedGlyphs(),
+                                                     viewModel.GetSecondMiddleIndexOfElidedGlyphs(),
+
+                                                     viewModel.GetVerticalLineAlignment(),
+                                                     viewModel.GetEllipsisPosition(),
+
+                                                     hyphens,
+                                                     hyphenIndices,
+                                                     hyphensCount,
+
+                                                     ignoreHorizontalAlignment};
+
+  // Aggregate underline-style-properties from mModel
+  const UnderlineStyleProperties modelUnderlineProperties{viewModel.GetUnderlineType(),
+                                                          viewModel.GetUnderlineColor(),
+                                                          viewModel.GetUnderlineHeight(),
+                                                          viewModel.GetDashedUnderlineGap(),
+                                                          viewModel.GetDashedUnderlineWidth(),
+                                                          true,
+                                                          true,
+                                                          true,
+                                                          true,
+                                                          true};
+
+  // Aggregate strikethrough-style-properties from mModel
+  const StrikethroughStyleProperties modelStrikethroughProperties{viewModel.GetStrikethroughColor(),
+                                                                  viewModel.GetStrikethroughHeight(),
+                                                                  true,
+                                                                  true};
+
+
+  // Aggregate input parameter for each glyph from mModel
+  const InputParameterForEachGlyph inputParamsForGlyph{style,
+                                                       pixelFormat,
+
+                                                       // Retrieves the glyph's outline width
+                                                       static_cast<float>(viewModel.GetOutlineWidth()),
+
+                                                       viewModel.GetCharacterSpacing(),
+
+                                                       (style == Typesetter::STYLE_OUTLINE)    ? viewModel.GetOutlineColor()    :
+                                                       (style == Typesetter::STYLE_SHADOW)     ? viewModel.GetShadowColor()     :
+                                                       (style == Typesetter::STYLE_BACKGROUND) ? viewModel.GetBackgroundColor() :
+                                                       viewModel.GetDefaultColor(),
+
+                                                       underlineRuns,
+                                                       strikethroughRuns,
+                                                       characterSpacingGlyphRuns,
+
+                                                       glyphsBuffer,
+                                                       textBuffer,
+                                                       glyphToCharacterMapBuffer,
+
+                                                       positionBuffer,
+
+                                                       colorsBuffer,
+                                                       colorIndexBuffer,
+
+                                                       modelUnderlineProperties,
+                                                       modelStrikethroughProperties,
+
+                                                       viewModel.IsUnderlineEnabled(),
+                                                       viewModel.IsStrikethroughEnabled(),
+                                                       viewModel.IsCutoutEnabled(),
+
+                                                       viewModel.IsRemoveFrontInset(),
+                                                       viewModel.IsRemoveBackInset(),
+
+                                                       // Whether to use the default color.
+                                                       (nullptr == colorsBuffer)};
+  // clang-format on
+
+  // Traverses the lines of the text.
+  for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
+  {
+    const LineRun& line = *(modelLinesBuffer + lineIndex);
+    CreateImageBufferForEachLine(mFontClient, glyphData, hyphenIndex, line, (lineIndex == 0u), inputParamsForLine, inputParamsForGlyph);
+  }
+
+  return glyphData.bitmapBuffer;
+}
+
+Typesetter::Impl::Impl(const ModelInterface* const model)
+: mModel(std::make_unique<ViewModel>(model))
+{
+  // Default font client set.
+  mFontClient = TextAbstraction::FontClient::Get();
+}
+
+Typesetter::Impl::~Impl() = default;
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/rendering/text-typesetter-impl.h b/dali-toolkit/internal/text/rendering/text-typesetter-impl.h
new file mode 100644 (file)
index 0000000..d0d7a3e
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef DALI_TOOLKIT_TEXT_TYPESETTER_IMPL_H
+#define DALI_TOOLKIT_TEXT_TYPESETTER_IMPL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/images/pixel.h>
+#include <dali/public-api/object/ref-object.h>
+#include <memory> ///< for std::unique_ptr
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/rendering/text-typesetter.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+class ModelInterface;
+class ViewModel;
+
+/**
+ * @brief This class is seperated logics for TypeSetter.
+ * It will reduce the complexicy of typesetter logic.
+ */
+struct Typesetter::Impl
+{
+public:
+  /**
+   * @brief Create an initialized image buffer filled with transparent color.
+   *
+   * Creates the pixel data used to generate the final image with the given size.
+   *
+   * @param[in] bufferWidth The width of the image buffer.
+   * @param[in] bufferHeight The height of the image buffer.
+   * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+   *
+   * @return An image buffer.
+   */
+  static Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat);
+
+public: // Constructor & Destructor
+  /**
+   * @brief Creates a Typesetter impl instance.
+   */
+  Impl(const ModelInterface* const model);
+
+  ~Impl();
+
+public:
+  /**
+   * @brief Retrieves the pointer to the view model.
+   *
+   * @return A pointer to the view model.
+   */
+  ViewModel* GetViewModel();
+
+  /**
+   * @brief Set the font client.
+   *
+   * Set the font client used in the update/render process of the text model.
+   *
+   * @param[in] fontClient The font client used by the Typesetter.
+   */
+  void SetFontClient(TextAbstraction::FontClient& fontClient);
+
+  /**
+   * @brief Get the font client.
+   *
+   * @return The font client used by the Typesetter.
+   */
+  TextAbstraction::FontClient& GetFontClient();
+
+public: // Image buffer creation
+  void DrawGlyphsBackground(Devel::PixelBuffer& buffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const int32_t horizontalOffset, const int32_t verticalOffset);
+
+  /**
+   * @brief Create & draw the image buffer for the given range of the glyphs in the given style.
+   *
+   * Does the following operations:
+   * - Retrieves the data buffers from the text model.
+   * - Creates the pixel data used to generate the final image with the given size.
+   * - Traverse the visible glyphs, retrieve their bitmaps and compose the final pixel data.
+   *
+   * @param[in] bufferWidth The width of the image buffer.
+   * @param[in] bufferHeight The height of the image buffer.
+   * @param[in] style The style of the text.
+   * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment, not ignored by default.
+   * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+   * @param[in] horizontalOffset The horizontal offset to be added to the glyph's position.
+   * @param[in] verticalOffset The vertical offset to be added to the glyph's position.
+   * @param[in] fromGlyphIndex The index of the first glyph within the text to be drawn
+   * @param[in] toGlyphIndex The index of the last glyph within the text to be drawn
+   *
+   * @return An image buffer with the text.
+   */
+  Devel::PixelBuffer CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const TextAbstraction::GlyphIndex fromGlyphIndex, const TextAbstraction::GlyphIndex toGlyphIndex);
+
+private:
+  std::unique_ptr<ViewModel>  mModel;
+  TextAbstraction::FontClient mFontClient;
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_TYPESETTER_IMPL_H
index ec6dce2..52747a3 100644 (file)
 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
 
 // EXTERNAL INCLUDES
-#include <cmath>
 #include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
 #include <dali/public-api/common/constants.h>
 #include <dali/public-api/math/math-utils.h>
 #include <memory.h>
+#include <cmath>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
@@ -34,6 +34,7 @@
 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
 #include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
 #include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
+#include <dali-toolkit/internal/text/rendering/text-typesetter-impl.h>
 #include <dali-toolkit/internal/text/rendering/view-model.h>
 
 namespace Dali
@@ -46,9 +47,6 @@ namespace
 {
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
 
-const float HALF(0.5f);
-const float ONE_AND_A_HALF(1.5f);
-
 /**
  * @brief Fast multiply & divide by 255. It wiil be useful when we applying alpha value in color
  *
@@ -76,746 +74,7 @@ inline uint8_t MultiplyAndSummationAndNormalizeColor(const uint8_t x1, const uin
   const uint32_t xy1 = static_cast<const uint32_t>(x1) * y1;
   const uint32_t xy2 = static_cast<const uint32_t>(x2) * y2;
   const uint32_t res = std::min(65025u, xy1 + xy2); // 65025 is 255 * 255.
-  return ((res + ((res + 257) >> 8)) >> 8); // fast divide by 255.
-}
-
-/// Helper macro define for glyph typesetter. It will reduce some duplicated code line.
-// clang-format off
-/**
- * @brief Prepare decode glyph bitmap data. It must be call END_GLYPH_BITMAP end of same scope.
- */
-#define BEGIN_GLYPH_BITMAP(data)                                                                                                                \
-{                                                                                                                                               \
-  uint32_t   glyphOffet               = 0u;                                                                                                     \
-  const bool useLocalScanline         = data.glyphBitmap.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;  \
-  uint8_t* __restrict__ glyphScanline = useLocalScanline ? (uint8_t*)malloc(data.glyphBitmap.width * glyphPixelSize) : data.glyphBitmap.buffer; \
-  DALI_ASSERT_ALWAYS(glyphScanline && "Glyph scanline for buffer is null!");
-
-/**
- * @brief Macro to skip useless line fast.
- */
-#define SKIP_GLYPH_SCANLINE(skipLine)                                                                  \
-if(useLocalScanline)                                                                                   \
-{                                                                                                      \
-  for(int32_t lineIndex = 0; lineIndex < skipLine; ++lineIndex)                                        \
-  {                                                                                                    \
-    TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
-  }                                                                                                    \
-}                                                                                                      \
-else                                                                                                   \
-{                                                                                                      \
-  glyphScanline += skipLine * static_cast<int32_t>(data.glyphBitmap.width * glyphPixelSize);           \
-}
-
-/**
- * @brief Prepare scanline of glyph bitmap data per each lines. It must be call END_GLYPH_SCANLINE_DECODE end of same scope.
- */
-#define BEGIN_GLYPH_SCANLINE_DECODE(data)                                                              \
-{                                                                                                      \
-  if(useLocalScanline)                                                                                 \
-  {                                                                                                    \
-    TextAbstraction::GlyphBufferData::DecompressScanline(data.glyphBitmap, glyphScanline, glyphOffet); \
-  }
-
-/**
- * @brief Finalize scanline of glyph bitmap data per each lines.
- */
-#define END_GLYPH_SCANLINE_DECODE(data)                       \
-  if(!useLocalScanline)                                       \
-  {                                                           \
-    glyphScanline += data.glyphBitmap.width * glyphPixelSize; \
-  }                                                           \
-} // For ensure that we call BEGIN_GLYPH_SCANLINE_DECODE before
-
-/**
- * @brief Finalize decode glyph bitmap data.
- */
-#define END_GLYPH_BITMAP() \
-  if(useLocalScanline)     \
-  {                        \
-    free(glyphScanline);   \
-  }                        \
-} // For ensure that we call BEGIN_GLYPH_BITMAP before
-
-// clang-format on
-/// Helper macro define end.
-
-/**
- * @brief Data struct used to set the buffer of the glyph's bitmap into the final bitmap's buffer.
- */
-struct GlyphData
-{
-  Devel::PixelBuffer               bitmapBuffer;     ///< The buffer of the whole bitmap. The format is RGBA8888.
-  Vector2*                         position;         ///< The position of the glyph.
-  TextAbstraction::GlyphBufferData glyphBitmap;      ///< The glyph's bitmap.
-  uint32_t                         width;            ///< The bitmap's width.
-  uint32_t                         height;           ///< The bitmap's height.
-  int32_t                          horizontalOffset; ///< The horizontal offset to be added to the 'x' glyph's position.
-  int32_t                          verticalOffset;   ///< The vertical offset to be added to the 'y' glyph's position.
-};
-
-/**
- * @brief Sets the glyph's buffer into the bitmap's buffer.
- *
- * @param[in, out] data Struct which contains the glyph's data and the bitmap's data.
- * @param[in] position The position of the glyph.
- * @param[in] color The color of the glyph.
- * @param[in] style The style of the text.
- * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
- */
-void TypesetGlyph(GlyphData& __restrict__ data,
-                  const Vector2* const __restrict__ position,
-                  const Vector4* const __restrict__ color,
-                  const Typesetter::Style style,
-                  const Pixel::Format     pixelFormat)
-{
-  if((0u == data.glyphBitmap.width) || (0u == data.glyphBitmap.height))
-  {
-    // Nothing to do if the width or height of the buffer is zero.
-    return;
-  }
-
-  // Initial vertical / horizontal offset.
-  const int32_t yOffset = data.verticalOffset + position->y;
-  const int32_t xOffset = data.horizontalOffset + position->x;
-
-  // Whether the given glyph is a color one.
-  const bool     isColorGlyph    = data.glyphBitmap.isColorEmoji || data.glyphBitmap.isColorBitmap;
-  const uint32_t glyphPixelSize  = Pixel::GetBytesPerPixel(data.glyphBitmap.format);
-  const uint32_t glyphAlphaIndex = (glyphPixelSize > 0u) ? glyphPixelSize - 1u : 0u;
-
-  // Determinate iterator range.
-  const int32_t lineIndexRangeMin = std::max(0, -yOffset);
-  const int32_t lineIndexRangeMax = std::min(static_cast<int32_t>(data.glyphBitmap.height), static_cast<int32_t>(data.height) - yOffset);
-  const int32_t indexRangeMin     = std::max(0, -xOffset);
-  const int32_t indexRangeMax     = std::min(static_cast<int32_t>(data.glyphBitmap.width), static_cast<int32_t>(data.width) - xOffset);
-
-  // If current glyph don't need to be rendered, just ignore.
-  if(lineIndexRangeMax <= lineIndexRangeMin || indexRangeMax <= indexRangeMin)
-  {
-    return;
-  }
-
-  if(Pixel::RGBA8888 == pixelFormat)
-  {
-    uint32_t* __restrict__ bitmapBuffer = reinterpret_cast<uint32_t*>(data.bitmapBuffer.GetBuffer());
-    // Skip basic line.
-    bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
-
-    // Fast-cut if style is MASK or OUTLINE. Outline not shown for color glyph.
-    // Just overwrite transparent color and return.
-    if(isColorGlyph && (Typesetter::STYLE_MASK == style || Typesetter::STYLE_OUTLINE == style))
-    {
-      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
-      {
-        // We can use memset here.
-        memset(bitmapBuffer + xOffset + indexRangeMin, 0, (indexRangeMax - indexRangeMin) * sizeof(uint32_t));
-        bitmapBuffer += data.width;
-      }
-      return;
-    }
-
-    const bool swapChannelsBR = Pixel::BGRA8888 == data.glyphBitmap.format;
-
-    // Precalculate input color's packed result.
-    uint32_t packedInputColor                    = 0u;
-    uint8_t* __restrict__ packedInputColorBuffer = reinterpret_cast<uint8_t*>(&packedInputColor);
-
-    *(packedInputColorBuffer + 3u) = static_cast<uint8_t>(color->a * 255);
-    *(packedInputColorBuffer + 2u) = static_cast<uint8_t>(color->b * 255);
-    *(packedInputColorBuffer + 1u) = static_cast<uint8_t>(color->g * 255);
-    *(packedInputColorBuffer)      = static_cast<uint8_t>(color->r * 255);
-
-    // Prepare glyph bitmap
-    BEGIN_GLYPH_BITMAP(data);
-
-    // Skip basic line of glyph.
-    SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
-
-    // Traverse the pixels of the glyph line per line.
-    if(isColorGlyph)
-    {
-      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
-      {
-        BEGIN_GLYPH_SCANLINE_DECODE(data);
-
-        for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
-        {
-          const int32_t xOffsetIndex = xOffset + index;
-
-          // Retrieves the color from the color glyph.
-          uint32_t packedColorGlyph                    = *(reinterpret_cast<const uint32_t*>(glyphScanline + (index << 2)));
-          uint8_t* __restrict__ packedColorGlyphBuffer = reinterpret_cast<uint8_t*>(&packedColorGlyph);
-
-          // Update the alpha channel.
-          const uint8_t colorAlpha       = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), *(packedColorGlyphBuffer + 3u));
-          *(packedColorGlyphBuffer + 3u) = colorAlpha;
-
-          if(Typesetter::STYLE_SHADOW == style)
-          {
-            // The shadow of color glyph needs to have the shadow color.
-            *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), colorAlpha);
-            *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), colorAlpha);
-            *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedInputColorBuffer, colorAlpha);
-          }
-          else
-          {
-            if(swapChannelsBR)
-            {
-              std::swap(*packedColorGlyphBuffer, *(packedColorGlyphBuffer + 2u)); // Swap B and R.
-            }
-
-            *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 2u), colorAlpha);
-            *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedColorGlyphBuffer + 1u), colorAlpha);
-            *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedColorGlyphBuffer, colorAlpha);
-
-            if(data.glyphBitmap.isColorBitmap)
-            {
-              *(packedColorGlyphBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), *(packedColorGlyphBuffer + 2u));
-              *(packedColorGlyphBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), *(packedColorGlyphBuffer + 1u));
-              *packedColorGlyphBuffer        = MultiplyAndNormalizeColor(*packedInputColorBuffer, *packedColorGlyphBuffer);
-            }
-          }
-
-          // Set the color into the final pixel buffer.
-          *(bitmapBuffer + xOffsetIndex) = packedColorGlyph;
-        }
-
-        bitmapBuffer += data.width;
-
-        END_GLYPH_SCANLINE_DECODE(data);
-      }
-    }
-    else
-    {
-      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
-      {
-        BEGIN_GLYPH_SCANLINE_DECODE(data);
-
-        for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
-        {
-          // Update the alpha channel.
-          const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
-
-          // Copy non-transparent pixels only
-          if(alpha > 0u)
-          {
-            const int32_t xOffsetIndex = xOffset + index;
-
-            // Check alpha of overlapped pixels
-            uint32_t& currentColor             = *(bitmapBuffer + xOffsetIndex);
-            uint8_t*  packedCurrentColorBuffer = reinterpret_cast<uint8_t*>(&currentColor);
-
-            // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
-            // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
-            // semi-transparent gaps between joint glyphs with overlapped pixels, which could
-            // happen, for example, in the RTL text when we copy glyphs from right to left).
-            uint8_t currentAlpha = *(packedCurrentColorBuffer + 3u);
-            currentAlpha         = std::max(currentAlpha, alpha);
-            if(currentAlpha == 255)
-            {
-              // Fast-cut to avoid float type operation.
-              currentColor = packedInputColor;
-            }
-            else
-            {
-              // Pack the given color into a 32bit buffer. The alpha channel will be updated later for each pixel.
-              // The format is RGBA8888.
-              uint32_t packedColor                    = 0u;
-              uint8_t* __restrict__ packedColorBuffer = reinterpret_cast<uint8_t*>(&packedColor);
-
-              // Color is pre-muliplied with its alpha.
-              *(packedColorBuffer + 3u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 3u), currentAlpha);
-              *(packedColorBuffer + 2u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 2u), currentAlpha);
-              *(packedColorBuffer + 1u) = MultiplyAndNormalizeColor(*(packedInputColorBuffer + 1u), currentAlpha);
-              *(packedColorBuffer)      = MultiplyAndNormalizeColor(*packedInputColorBuffer, currentAlpha);
-
-              // Set the color into the final pixel buffer.
-              currentColor = packedColor;
-            }
-          }
-        }
-
-        bitmapBuffer += data.width;
-
-        END_GLYPH_SCANLINE_DECODE(data);
-      }
-    }
-
-    END_GLYPH_BITMAP();
-  }
-  else // Pixel::L8
-  {
-    // Below codes required only if not color glyph.
-    if(!isColorGlyph)
-    {
-      uint8_t* __restrict__ bitmapBuffer = data.bitmapBuffer.GetBuffer();
-      // Skip basic line.
-      bitmapBuffer += (lineIndexRangeMin + yOffset) * static_cast<int32_t>(data.width);
-
-      // Prepare glyph bitmap
-      BEGIN_GLYPH_BITMAP(data);
-
-      // Skip basic line of glyph.
-      SKIP_GLYPH_SCANLINE(lineIndexRangeMin);
-
-      // Traverse the pixels of the glyph line per line.
-      for(int32_t lineIndex = lineIndexRangeMin; lineIndex < lineIndexRangeMax; ++lineIndex)
-      {
-        BEGIN_GLYPH_SCANLINE_DECODE(data);
-
-        for(int32_t index = indexRangeMin; index < indexRangeMax; ++index)
-        {
-          const int32_t xOffsetIndex = xOffset + index;
-
-          // Update the alpha channel.
-          const uint8_t alpha = *(glyphScanline + index * glyphPixelSize + glyphAlphaIndex);
-
-          // Copy non-transparent pixels only
-          if(alpha > 0u)
-          {
-            // Check alpha of overlapped pixels
-            uint8_t& currentAlpha = *(bitmapBuffer + xOffsetIndex);
-
-            // For any pixel overlapped with the pixel in previous glyphs, make sure we don't
-            // overwrite a previous bigger alpha with a smaller alpha (in order to avoid
-            // semi-transparent gaps between joint glyphs with overlapped pixels, which could
-            // happen, for example, in the RTL text when we copy glyphs from right to left).
-            currentAlpha = std::max(currentAlpha, alpha);
-          }
-        }
-
-        bitmapBuffer += data.width;
-
-        END_GLYPH_SCANLINE_DECODE(data);
-      }
-
-      END_GLYPH_BITMAP();
-    }
-  }
-}
-
-/// Draws the specified underline color to the buffer
-void DrawUnderline(
-  const uint32_t                  bufferWidth,
-  const uint32_t                  bufferHeight,
-  GlyphData&                      glyphData,
-  const float                     baseline,
-  const float                     currentUnderlinePosition,
-  const float                     maxUnderlineHeight,
-  const float                     lineExtentLeft,
-  const float                     lineExtentRight,
-  const UnderlineStyleProperties& commonUnderlineProperties,
-  const UnderlineStyleProperties& currentUnderlineProperties,
-  const LineRun&                  line)
-{
-  const Vector4&              underlineColor       = currentUnderlineProperties.colorDefined ? currentUnderlineProperties.color : commonUnderlineProperties.color;
-  const Text::Underline::Type underlineType        = currentUnderlineProperties.typeDefined ? currentUnderlineProperties.type : commonUnderlineProperties.type;
-  const float                 dashedUnderlineWidth = currentUnderlineProperties.dashWidthDefined ? currentUnderlineProperties.dashWidth : commonUnderlineProperties.dashWidth;
-  const float                 dashedUnderlineGap   = currentUnderlineProperties.dashGapDefined ? currentUnderlineProperties.dashGap : commonUnderlineProperties.dashGap;
-
-  int32_t underlineYOffset = glyphData.verticalOffset + baseline + currentUnderlinePosition;
-
-  const uint32_t yRangeMin = underlineYOffset;
-  const uint32_t yRangeMax = std::min(bufferHeight, underlineYOffset + static_cast<uint32_t>(maxUnderlineHeight));
-  const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
-  const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
-
-  // If current glyph don't need to be rendered, just ignore.
-  if((underlineType != Text::Underline::DOUBLE && yRangeMax <= yRangeMin) || xRangeMax <= xRangeMin)
-  {
-    return;
-  }
-
-  // We can optimize by memset when underlineColor.a is near zero
-  uint8_t underlineColorAlpha = static_cast<uint8_t>(underlineColor.a * 255.f);
-
-  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
-
-  // Skip yRangeMin line.
-  bitmapBuffer += yRangeMin * glyphData.width;
-
-  // Note if underlineType is DASHED, we cannot setup color by memset.
-  if(underlineType != Text::Underline::DASHED && underlineColorAlpha == 0)
-  {
-    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
-    {
-      // We can use memset.
-      memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
-      bitmapBuffer += glyphData.width;
-    }
-    if(underlineType == Text::Underline::DOUBLE)
-    {
-      int32_t        secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
-      const uint32_t secondYRangeMin        = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
-      const uint32_t secondYRangeMax        = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
-
-      // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
-      bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
-
-      for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
-      {
-        // We can use memset.
-        memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
-        bitmapBuffer += glyphData.width;
-      }
-    }
-  }
-  else
-  {
-    uint32_t packedUnderlineColor       = 0u;
-    uint8_t* packedUnderlineColorBuffer = reinterpret_cast<uint8_t*>(&packedUnderlineColor);
-
-    // Write the color to the pixel buffer
-    *(packedUnderlineColorBuffer + 3u) = underlineColorAlpha;
-    *(packedUnderlineColorBuffer + 2u) = static_cast<uint8_t>(underlineColor.b * underlineColorAlpha);
-    *(packedUnderlineColorBuffer + 1u) = static_cast<uint8_t>(underlineColor.g * underlineColorAlpha);
-    *(packedUnderlineColorBuffer)      = static_cast<uint8_t>(underlineColor.r * underlineColorAlpha);
-
-    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
-    {
-      if(underlineType == Text::Underline::DASHED)
-      {
-        float dashWidth = dashedUnderlineWidth;
-        float dashGap   = 0;
-
-        for(uint32_t x = xRangeMin; x < xRangeMax; x++)
-        {
-          if(Dali::EqualsZero(dashGap) && dashWidth > 0)
-          {
-            // Note : this is same logic as bitmap[y][x] = underlineColor;
-            *(bitmapBuffer + x) = packedUnderlineColor;
-            dashWidth--;
-          }
-          else if(dashGap < dashedUnderlineGap)
-          {
-            dashGap++;
-          }
-          else
-          {
-            //reset
-            dashWidth = dashedUnderlineWidth;
-            dashGap   = 0;
-          }
-        }
-      }
-      else
-      {
-        for(uint32_t x = xRangeMin; x < xRangeMax; x++)
-        {
-          // Note : this is same logic as bitmap[y][x] = underlineColor;
-          *(bitmapBuffer + x) = packedUnderlineColor;
-        }
-      }
-      bitmapBuffer += glyphData.width;
-    }
-    if(underlineType == Text::Underline::DOUBLE)
-    {
-      int32_t        secondUnderlineYOffset = underlineYOffset - ONE_AND_A_HALF * maxUnderlineHeight;
-      const uint32_t secondYRangeMin        = static_cast<uint32_t>(std::max(0, secondUnderlineYOffset));
-      const uint32_t secondYRangeMax        = static_cast<uint32_t>(std::max(0, std::min(static_cast<int32_t>(bufferHeight), secondUnderlineYOffset + static_cast<int32_t>(maxUnderlineHeight))));
-
-      // Rewind bitmapBuffer pointer, and skip secondYRangeMin line.
-      bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer()) + yRangeMin * glyphData.width;
-
-      for(uint32_t y = secondYRangeMin; y < secondYRangeMax; y++)
-      {
-        for(uint32_t x = xRangeMin; x < xRangeMax; x++)
-        {
-          // Note : this is same logic as bitmap[y][x] = underlineColor;
-          *(bitmapBuffer + x) = packedUnderlineColor;
-        }
-        bitmapBuffer += glyphData.width;
-      }
-    }
-  }
-}
-
-/// Draws the background color to the buffer
-void DrawBackgroundColor(
-  Vector4        backgroundColor,
-  const uint32_t bufferWidth,
-  const uint32_t bufferHeight,
-  GlyphData&     glyphData,
-  const float    baseline,
-  const LineRun& line,
-  const float    lineExtentLeft,
-  const float    lineExtentRight)
-{
-  const int32_t yRangeMin = std::max(0, static_cast<int32_t>(glyphData.verticalOffset + baseline - line.ascender));
-  const int32_t yRangeMax = std::min(static_cast<int32_t>(bufferHeight), static_cast<int32_t>(glyphData.verticalOffset + baseline - line.descender));
-  const int32_t xRangeMin = std::max(0, static_cast<int32_t>(glyphData.horizontalOffset + lineExtentLeft));
-  const int32_t xRangeMax = std::min(static_cast<int32_t>(bufferWidth), static_cast<int32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
-
-  // If current glyph don't need to be rendered, just ignore.
-  if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
-  {
-    return;
-  }
-
-  // We can optimize by memset when backgroundColor.a is near zero
-  uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
-
-  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
-
-  // Skip yRangeMin line.
-  bitmapBuffer += yRangeMin * glyphData.width;
-
-  if(backgroundColorAlpha == 0)
-  {
-    for(int32_t y = yRangeMin; y < yRangeMax; y++)
-    {
-      // We can use memset.
-      memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
-      bitmapBuffer += glyphData.width;
-    }
-  }
-  else
-  {
-    uint32_t packedBackgroundColor       = 0u;
-    uint8_t* packedBackgroundColorBuffer = reinterpret_cast<uint8_t*>(&packedBackgroundColor);
-
-    // Write the color to the pixel buffer
-    *(packedBackgroundColorBuffer + 3u) = backgroundColorAlpha;
-    *(packedBackgroundColorBuffer + 2u) = static_cast<uint8_t>(backgroundColor.b * backgroundColorAlpha);
-    *(packedBackgroundColorBuffer + 1u) = static_cast<uint8_t>(backgroundColor.g * backgroundColorAlpha);
-    *(packedBackgroundColorBuffer)      = static_cast<uint8_t>(backgroundColor.r * backgroundColorAlpha);
-
-    for(int32_t y = yRangeMin; y < yRangeMax; y++)
-    {
-      for(int32_t x = xRangeMin; x < xRangeMax; x++)
-      {
-        // Note : this is same logic as bitmap[y][x] = backgroundColor;
-        *(bitmapBuffer + x) = packedBackgroundColor;
-      }
-      bitmapBuffer += glyphData.width;
-    }
-  }
-}
-
-Devel::PixelBuffer DrawGlyphsBackground(const ViewModel* model, Devel::PixelBuffer& buffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const int32_t horizontalOffset, const int32_t verticalOffset)
-{
-  // Retrieve lines, glyphs, positions and colors from the view model.
-  const Length            modelNumberOfLines           = model->GetNumberOfLines();
-  const LineRun* const    modelLinesBuffer             = model->GetLines();
-  const Length            numberOfGlyphs               = model->GetNumberOfGlyphs();
-  const GlyphInfo* const  glyphsBuffer                 = model->GetGlyphs();
-  const Vector2* const    positionBuffer               = model->GetLayout();
-  const Vector4* const    backgroundColorsBuffer       = model->GetBackgroundColors();
-  const ColorIndex* const backgroundColorIndicesBuffer = model->GetBackgroundColorIndices();
-  const bool              removeFrontInset             = model->IsRemoveFrontInset();
-  const bool              removeBackInset              = model->IsRemoveBackInset();
-
-  const DevelText::VerticalLineAlignment::Type verLineAlign = model->GetVerticalLineAlignment();
-
-  // Create and initialize the pixel buffer.
-  GlyphData glyphData;
-  glyphData.verticalOffset   = verticalOffset;
-  glyphData.width            = bufferWidth;
-  glyphData.height           = bufferHeight;
-  glyphData.bitmapBuffer     = buffer;
-  glyphData.horizontalOffset = 0;
-
-  ColorIndex prevBackgroundColorIndex = 0;
-  ColorIndex backgroundColorIndex     = 0;
-
-  // Traverses the lines of the text.
-  for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
-  {
-    const LineRun& line = *(modelLinesBuffer + lineIndex);
-
-    // Sets the horizontal offset of the line.
-    glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
-    glyphData.horizontalOffset += horizontalOffset;
-
-    // Increases the vertical offset with the line's ascender.
-    glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
-
-    float left     = bufferWidth;
-    float right    = 0.0f;
-    float baseline = 0.0f;
-
-    // Traverses the glyphs of the line.
-    const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs);
-    for(GlyphIndex glyphIndex = line.glyphRun.glyphIndex; glyphIndex < endGlyphIndex; ++glyphIndex)
-    {
-      // Retrieve the glyph's info.
-      const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex;
-
-      if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
-         (glyphInfo->height < Math::MACHINE_EPSILON_1000))
-      {
-        // Nothing to do if default background color, the glyph's width or height is zero.
-        continue;
-      }
-
-      backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + glyphIndex);
-
-      if((backgroundColorIndex != prevBackgroundColorIndex) &&
-         (prevBackgroundColorIndex != 0u))
-      {
-        const Vector4& backgroundColor = *(backgroundColorsBuffer + prevBackgroundColorIndex - 1u);
-        DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
-      }
-
-      if(backgroundColorIndex == 0u)
-      {
-        prevBackgroundColorIndex = backgroundColorIndex;
-        //if background color is the default do nothing
-        continue;
-      }
-
-      // Retrieves the glyph's position.
-      const Vector2* const position = positionBuffer + glyphIndex;
-
-      if(baseline < position->y + glyphInfo->yBearing)
-      {
-        baseline = position->y + glyphInfo->yBearing;
-      }
-
-      // Calculate the positions of leftmost and rightmost glyphs in the current line
-      if(removeFrontInset)
-      {
-        if((position->x < left) || (backgroundColorIndex != prevBackgroundColorIndex))
-        {
-          left = position->x;
-        }
-      }
-      else
-      {
-        const float originPositionLeft = position->x - glyphInfo->xBearing;
-        if((originPositionLeft < left) || (backgroundColorIndex != prevBackgroundColorIndex))
-        {
-          left = originPositionLeft;
-        }
-      }
-
-      if(removeBackInset)
-      {
-        if(position->x + glyphInfo->width > right)
-        {
-          right = position->x - position->x + glyphInfo->width;
-        }
-      }
-      else
-      {
-        const float originPositionRight = position->x - glyphInfo->xBearing + glyphInfo->advance;
-        if(originPositionRight > right)
-        {
-          right = originPositionRight;
-        }
-      }
-
-      prevBackgroundColorIndex = backgroundColorIndex;
-    }
-
-    //draw last background at line end if not default
-    if(backgroundColorIndex != 0u)
-    {
-      const Vector4& backgroundColor = *(backgroundColorsBuffer + backgroundColorIndex - 1u);
-      DrawBackgroundColor(backgroundColor, bufferWidth, bufferHeight, glyphData, baseline, line, left, right);
-    }
-
-    // Increases the vertical offset with the line's descender.
-    glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
-  }
-
-  return glyphData.bitmapBuffer;
-}
-
-/// Draws the specified strikethrough color to the buffer
-void DrawStrikethrough(const uint32_t                      bufferWidth,
-                       const uint32_t                      bufferHeight,
-                       GlyphData&                          glyphData,
-                       const float                         baseline,
-                       const float                         strikethroughStartingYPosition,
-                       const float                         maxStrikethroughHeight,
-                       const float                         lineExtentLeft,
-                       const float                         lineExtentRight,
-                       const StrikethroughStyleProperties& commonStrikethroughProperties,
-                       const StrikethroughStyleProperties& currentStrikethroughProperties,
-                       const LineRun&                      line)
-{
-  const Vector4& strikethroughColor = currentStrikethroughProperties.colorDefined ? currentStrikethroughProperties.color : commonStrikethroughProperties.color;
-
-  const uint32_t yRangeMin = static_cast<uint32_t>(strikethroughStartingYPosition);
-  const uint32_t yRangeMax = std::min(bufferHeight, static_cast<uint32_t>(strikethroughStartingYPosition + maxStrikethroughHeight));
-  const uint32_t xRangeMin = static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentLeft);
-  const uint32_t xRangeMax = std::min(bufferWidth, static_cast<uint32_t>(glyphData.horizontalOffset + lineExtentRight + 1)); // Due to include last point, we add 1 here
-
-  // If current glyph don't need to be rendered, just ignore.
-  if(yRangeMax <= yRangeMin || xRangeMax <= xRangeMin)
-  {
-    return;
-  }
-
-  // We can optimize by memset when strikethroughColor.a is near zero
-  uint8_t strikethroughColorAlpha = static_cast<uint8_t>(strikethroughColor.a * 255.f);
-
-  uint32_t* bitmapBuffer = reinterpret_cast<uint32_t*>(glyphData.bitmapBuffer.GetBuffer());
-
-  // Skip yRangeMin line.
-  bitmapBuffer += yRangeMin * glyphData.width;
-
-  if(strikethroughColorAlpha == 0)
-  {
-    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
-    {
-      // We can use memset.
-      memset(bitmapBuffer + xRangeMin, 0, (xRangeMax - xRangeMin) * sizeof(uint32_t));
-      bitmapBuffer += glyphData.width;
-    }
-  }
-  else
-  {
-    uint32_t packedStrikethroughColor       = 0u;
-    uint8_t* packedStrikethroughColorBuffer = reinterpret_cast<uint8_t*>(&packedStrikethroughColor);
-
-    // Write the color to the pixel buffer
-    *(packedStrikethroughColorBuffer + 3u) = strikethroughColorAlpha;
-    *(packedStrikethroughColorBuffer + 2u) = static_cast<uint8_t>(strikethroughColor.b * strikethroughColorAlpha);
-    *(packedStrikethroughColorBuffer + 1u) = static_cast<uint8_t>(strikethroughColor.g * strikethroughColorAlpha);
-    *(packedStrikethroughColorBuffer)      = static_cast<uint8_t>(strikethroughColor.r * strikethroughColorAlpha);
-
-    for(uint32_t y = yRangeMin; y < yRangeMax; y++)
-    {
-      for(uint32_t x = xRangeMin; x < xRangeMax; x++)
-      {
-        // Note : this is same logic as bitmap[y][x] = strikethroughColor;
-        *(bitmapBuffer + x) = packedStrikethroughColor;
-      }
-      bitmapBuffer += glyphData.width;
-    }
-  }
-}
-
-/**
- * @brief Create an initialized image buffer filled with transparent color.
- *
- * Creates the pixel data used to generate the final image with the given size.
- *
- * @param[in] bufferWidth The width of the image buffer.
- * @param[in] bufferHeight The height of the image buffer.
- * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
- *
- * @return An image buffer.
- */
-inline Devel::PixelBuffer CreateTransparentImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Pixel::Format pixelFormat)
-{
-  Devel::PixelBuffer imageBuffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, pixelFormat);
-
-  if(Pixel::RGBA8888 == pixelFormat)
-  {
-    const uint32_t bufferSizeInt  = bufferWidth * bufferHeight;
-    const size_t   bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
-    memset(imageBuffer.GetBuffer(), 0, bufferSizeChar);
-  }
-  else
-  {
-    memset(imageBuffer.GetBuffer(), 0, static_cast<std::size_t>(bufferWidth * bufferHeight));
-  }
-
-  return imageBuffer;
+  return ((res + ((res + 257) >> 8)) >> 8);         // fast divide by 255.
 }
 
 /**
@@ -845,13 +104,13 @@ void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::
   uint32_t* topBuffer    = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
   uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
 
-  if(topBuffer == NULL && bottomBuffer == NULL)
+  if(topBuffer == nullptr && bottomBuffer == nullptr)
   {
     // Nothing to do if both buffers are empty.
     return;
   }
 
-  if(topBuffer == NULL)
+  if(topBuffer == nullptr)
   {
     // Nothing to do if topBuffer is empty.
     // If we need to store the result into top, change topPixelBuffer as bottomPixelBuffer.
@@ -862,7 +121,7 @@ void CombineImageBuffer(Devel::PixelBuffer& __restrict__ topPixelBuffer, Devel::
     return;
   }
 
-  if(bottomBuffer == NULL)
+  if(bottomBuffer == nullptr)
   {
     // Nothing to do if bottomBuffer is empty.
     // If we need to store the result into bottom, change bottomPixelBuffer as topPixelBuffer.
@@ -934,13 +193,18 @@ TypesetterPtr Typesetter::New(const ModelInterface* const model)
 
 ViewModel* Typesetter::GetViewModel()
 {
-  return mModel;
+  return mImpl->GetViewModel();
+}
+
+void Typesetter::SetFontClient(TextAbstraction::FontClient& fontClient)
+{
+  mImpl->SetFontClient(fontClient);
 }
 
 PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirection::Type textDirection, RenderBehaviour behaviour, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat)
 {
-  Devel::PixelBuffer result = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
-  PixelData pixelData = Devel::PixelBuffer::Convert(result);
+  Devel::PixelBuffer result    = RenderWithPixelBuffer(size, textDirection, behaviour, ignoreHorizontalAlignment, pixelFormat);
+  PixelData          pixelData = Devel::PixelBuffer::Convert(result);
 
   return pixelData;
 }
@@ -960,16 +224,19 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_RENDERING_TYPESETTER");
   // @todo. This initial implementation for a TextLabel has only one visible page.
 
+  // Use l-value to make ensure it is not nullptr, so compiler happy.
+  auto& viewModel = *(mImpl->GetViewModel());
+
   // Elides the text if needed.
-  mModel->ElideGlyphs();
+  viewModel.ElideGlyphs(mImpl->GetFontClient());
 
   // Retrieves the layout size.
-  const Size& layoutSize = mModel->GetLayoutSize();
-  const int32_t outlineWidth = static_cast<int32_t>(mModel->GetOutlineWidth());
+  const Size&   layoutSize   = viewModel.GetLayoutSize();
+  const int32_t outlineWidth = static_cast<int32_t>(viewModel.GetOutlineWidth());
 
   // Set the offset for the horizontal alignment according to the text direction and outline width.
   int32_t penX = 0;
-  switch(mModel->GetHorizontalAlignment())
+  switch(viewModel.GetHorizontalAlignment())
   {
     case HorizontalAlignment::BEGIN:
     {
@@ -990,7 +257,7 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
 
   // Set the offset for the vertical alignment.
   int32_t penY = 0u;
-  switch(mModel->GetVerticalAlignment())
+  switch(viewModel.GetVerticalAlignment())
   {
     case VerticalAlignment::TOP:
     {
@@ -1011,6 +278,14 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
     }
   }
 
+  const bool isCutoutEnabled = viewModel.IsCutoutEnabled();
+  if(isCutoutEnabled)
+  {
+    Vector2 offset = viewModel.GetOffsetWithCutout();
+    penX           = offset.x;
+    penY           = offset.y;
+  }
+
   // Generate the image buffers of the text for each different style first,
   // then combine all of them together as one final image buffer. We try to
   // do all of these in CPU only, so that once the final texture is generated,
@@ -1023,15 +298,15 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
   const size_t   bufferSizeChar = sizeof(uint32_t) * static_cast<std::size_t>(bufferSizeInt);
 
   //Elided text in ellipsis at START could start on index greater than 0
-  auto startIndexOfGlyphs = mModel->GetStartIndexOfElidedGlyphs();
-  auto endIndexOfGlyphs   = mModel->GetEndIndexOfElidedGlyphs();
+  auto startIndexOfGlyphs = viewModel.GetStartIndexOfElidedGlyphs();
+  auto endIndexOfGlyphs   = viewModel.GetEndIndexOfElidedGlyphs();
 
   Devel::PixelBuffer imageBuffer;
 
   if(RENDER_MASK == behaviour)
   {
     // Generate the image buffer as an alpha mask for color glyphs.
-    imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+    imageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_MASK, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
   }
   else if(RENDER_NO_TEXT == behaviour || RENDER_OVERLAY_STYLE == behaviour)
   {
@@ -1042,20 +317,20 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
   else
   {
     // Generate the image buffer for the text with no style.
-    imageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+    imageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_NONE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
   }
 
   if((RENDER_NO_STYLES != behaviour) && (RENDER_MASK != behaviour))
   {
     // Generate the outline if enabled
-    const uint16_t outlineWidth = mModel->GetOutlineWidth();
-    const float    outlineAlpha = mModel->GetOutlineColor().a;
+    const uint16_t outlineWidth = viewModel.GetOutlineWidth();
+    const float    outlineAlpha = viewModel.GetOutlineColor().a;
     if(outlineWidth != 0u && fabsf(outlineAlpha) > Math::MACHINE_EPSILON_1 && RENDER_OVERLAY_STYLE != behaviour)
     {
       // Create the image buffer for outline
-      Devel::PixelBuffer outlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+      Devel::PixelBuffer outlineImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_OUTLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
 
-      const float& blurRadius = mModel->GetOutlineBlurRadius();
+      const float& blurRadius = viewModel.GetOutlineBlurRadius();
 
       if(blurRadius > Math::MACHINE_EPSILON_1)
       {
@@ -1069,15 +344,15 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
     // @todo. Support shadow for partial text later on.
 
     // Generate the shadow if enabled
-    const Vector2& shadowOffset = mModel->GetShadowOffset();
-    const float    shadowAlpha  = mModel->GetShadowColor().a;
+    const Vector2& shadowOffset = viewModel.GetShadowOffset();
+    const float    shadowAlpha  = viewModel.GetShadowColor().a;
     if(RENDER_OVERLAY_STYLE != behaviour && fabsf(shadowAlpha) > Math::MACHINE_EPSILON_1 && (fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1))
     {
       // Create the image buffer for shadow
-      Devel::PixelBuffer shadowImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+      Devel::PixelBuffer shadowImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_SHADOW, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
 
       // Check whether it will be a soft shadow
-      const float& blurRadius = mModel->GetShadowBlurRadius();
+      const float& blurRadius = viewModel.GetShadowBlurRadius();
 
       if(blurRadius > Math::MACHINE_EPSILON_1)
       {
@@ -1089,24 +364,24 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
     }
 
     // Generate the background if enabled
-    const bool backgroundEnabled   = mModel->IsBackgroundEnabled();
-    const bool backgroundMarkupSet = mModel->IsMarkupBackgroundColorSet();
+    const bool backgroundEnabled   = viewModel.IsBackgroundEnabled();
+    const bool backgroundMarkupSet = viewModel.IsMarkupBackgroundColorSet();
     if((backgroundEnabled || backgroundMarkupSet) && RENDER_OVERLAY_STYLE != behaviour)
     {
       Devel::PixelBuffer backgroundImageBuffer;
 
       if(backgroundEnabled)
       {
-        backgroundImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+        backgroundImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_BACKGROUND, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
       }
       else
       {
-        backgroundImageBuffer = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
+        backgroundImageBuffer = Impl::CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
       }
 
       if(backgroundMarkupSet)
       {
-        DrawGlyphsBackground(mModel, backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
+        mImpl->DrawGlyphsBackground(backgroundImageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, penX, penY);
       }
 
       // Combine the two buffers
@@ -1114,12 +389,12 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
     }
 
     // Generate the background_with_mask if enabled
-    const bool backgroundWithCutoutEnabled   = mModel->IsBackgroundWithCutoutEnabled();
+    const bool backgroundWithCutoutEnabled = viewModel.IsBackgroundWithCutoutEnabled();
     if((backgroundWithCutoutEnabled) && RENDER_OVERLAY_STYLE != behaviour)
     {
       Devel::PixelBuffer backgroundImageBuffer;
 
-      backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, mModel->GetBackgroundColorWithCutout());
+      backgroundImageBuffer = CreateFullBackgroundBuffer(bufferWidth, bufferHeight, viewModel.GetBackgroundColorWithCutout());
 
       // Combine the two buffers
       CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight, true);
@@ -1127,33 +402,33 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
 
     if(RENDER_OVERLAY_STYLE == behaviour)
     {
-      if(mModel->IsUnderlineEnabled())
+      if(viewModel.IsUnderlineEnabled())
       {
         // Create the image buffer for underline
-        Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
+        Devel::PixelBuffer underlineImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, penX, penY, startIndexOfGlyphs, endIndexOfGlyphs);
 
         // Combine the two buffers
         CombineImageBuffer(imageBuffer, underlineImageBuffer, bufferWidth, bufferHeight, true);
       }
 
-      if(mModel->IsStrikethroughEnabled())
+      if(viewModel.IsStrikethroughEnabled())
       {
         // Create the image buffer for strikethrough
-        Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
+        Devel::PixelBuffer strikethroughImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, penX, penY, 0u, endIndexOfGlyphs);
 
         // Combine the two buffers
         CombineImageBuffer(imageBuffer, strikethroughImageBuffer, bufferWidth, bufferHeight, true);
       }
 
       // Markup-Processor for overlay styles
-      if(mModel->IsMarkupProcessorEnabled() || mModel->IsSpannedTextPlaced())
+      if(viewModel.IsMarkupProcessorEnabled() || viewModel.IsSpannedTextPlaced())
       {
-        if(mModel->IsMarkupUnderlineSet())
+        if(viewModel.IsMarkupUnderlineSet())
         {
           imageBuffer = ApplyUnderlineMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
         }
 
-        if(mModel->IsMarkupStrikethroughSet())
+        if(viewModel.IsMarkupStrikethroughSet())
         {
           imageBuffer = ApplyStrikethroughMarkupImageBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
         }
@@ -1166,8 +441,8 @@ Devel::PixelBuffer Typesetter::RenderWithPixelBuffer(const Vector2& size, Toolki
 
 Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Vector4& backgroundColor)
 {
-  const uint32_t bufferSizeInt = bufferWidth * bufferHeight;
-  uint8_t backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
+  const uint32_t bufferSizeInt        = bufferWidth * bufferHeight;
+  uint8_t        backgroundColorAlpha = static_cast<uint8_t>(backgroundColor.a * 255.f);
 
   Devel::PixelBuffer buffer = Devel::PixelBuffer::New(bufferWidth, bufferHeight, Pixel::RGBA8888);
 
@@ -1187,413 +462,17 @@ Devel::PixelBuffer Typesetter::CreateFullBackgroundBuffer(const uint32_t bufferW
   return buffer;
 }
 
-Devel::PixelBuffer Typesetter::CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const GlyphIndex fromGlyphIndex, const GlyphIndex toGlyphIndex)
-{
-  // Retrieve lines, glyphs, positions and colors from the view model.
-  const Length modelNumberOfLines                       = mModel->GetNumberOfLines();
-  const LineRun* const __restrict__ modelLinesBuffer    = mModel->GetLines();
-  const GlyphInfo* const __restrict__ glyphsBuffer      = mModel->GetGlyphs();
-  const Vector2* const __restrict__ positionBuffer      = mModel->GetLayout();
-  const Vector4* const __restrict__ colorsBuffer        = mModel->GetColors();
-  const ColorIndex* const __restrict__ colorIndexBuffer = mModel->GetColorIndices();
-  const GlyphInfo* __restrict__ hyphens                 = mModel->GetHyphens();
-  const Length* __restrict__ hyphenIndices              = mModel->GetHyphenIndices();
-  const Length hyphensCount                             = mModel->GetHyphensCount();
-  const bool removeFrontInset                           = mModel->IsRemoveFrontInset();
-  const bool removeBackInset                            = mModel->IsRemoveBackInset();
-  const bool cutoutEnabled                              = mModel->IsCutoutEnabled();
-
-  // Elided text info. Indices according to elided text and Ellipsis position.
-  const auto startIndexOfGlyphs              = mModel->GetStartIndexOfElidedGlyphs();
-  const auto endIndexOfGlyphs                = mModel->GetEndIndexOfElidedGlyphs();
-  const auto firstMiddleIndexOfElidedGlyphs  = mModel->GetFirstMiddleIndexOfElidedGlyphs();
-  const auto secondMiddleIndexOfElidedGlyphs = mModel->GetSecondMiddleIndexOfElidedGlyphs();
-  const auto ellipsisPosition                = mModel->GetEllipsisPosition();
-
-  // Whether to use the default color.
-  const bool     useDefaultColor = (NULL == colorsBuffer);
-  const Vector4& defaultColor    = mModel->GetDefaultColor();
-
-  // Create and initialize the pixel buffer.
-  GlyphData glyphData;
-  glyphData.verticalOffset   = verticalOffset;
-  glyphData.width            = bufferWidth;
-  glyphData.height           = bufferHeight;
-  glyphData.bitmapBuffer     = CreateTransparentImageBuffer(bufferWidth, bufferHeight, pixelFormat);
-  glyphData.horizontalOffset = 0;
-
-  // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
-  TextAbstraction::FontClient fontClient  = TextAbstraction::FontClient::Get();
-  Length                      hyphenIndex = 0;
-
-  const Character* __restrict__ textBuffer                       = mModel->GetTextBuffer();
-  float calculatedAdvance                                        = 0.f;
-  const Vector<CharacterIndex>& __restrict__ glyphToCharacterMap = mModel->GetGlyphsToCharacters();
-  const CharacterIndex* __restrict__ glyphToCharacterMapBuffer   = glyphToCharacterMap.Begin();
-
-  const DevelText::VerticalLineAlignment::Type verLineAlign = mModel->GetVerticalLineAlignment();
-
-  // Traverses the lines of the text.
-  for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex)
-  {
-    const LineRun& line = *(modelLinesBuffer + lineIndex);
-
-    // Sets the horizontal offset of the line.
-    glyphData.horizontalOffset = ignoreHorizontalAlignment ? 0 : static_cast<int32_t>(line.alignmentOffset);
-    glyphData.horizontalOffset += horizontalOffset;
-
-    // Increases the vertical offset with the line's ascender.
-    glyphData.verticalOffset += static_cast<int32_t>(line.ascender + GetPreOffsetVerticalLineAlignment(line, verLineAlign));
-
-    // Retrieves the glyph's outline width
-    float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
-
-    if(style == Typesetter::STYLE_OUTLINE)
-    {
-      const Vector2& outlineOffset = mModel->GetOutlineOffset();
-
-      glyphData.horizontalOffset -= outlineWidth;
-      glyphData.horizontalOffset += outlineOffset.x;
-      if(lineIndex == 0u)
-      {
-        // Only need to add the vertical outline offset for the first line
-        glyphData.verticalOffset -= outlineWidth;
-        glyphData.verticalOffset += outlineOffset.y;
-      }
-    }
-    else if(style == Typesetter::STYLE_SHADOW)
-    {
-      const Vector2& shadowOffset = mModel->GetShadowOffset();
-      glyphData.horizontalOffset += shadowOffset.x - outlineWidth; // if outline enabled then shadow should offset from outline
-
-      if(lineIndex == 0u)
-      {
-        // Only need to add the vertical shadow offset for first line
-        glyphData.verticalOffset += shadowOffset.y - outlineWidth;
-      }
-    }
-
-    const bool  underlineEnabled      = mModel->IsUnderlineEnabled();
-    const bool  strikethroughEnabled  = mModel->IsStrikethroughEnabled();
-    const float modelCharacterSpacing = mModel->GetCharacterSpacing();
-
-    // Get the character-spacing runs.
-    const Vector<CharacterSpacingGlyphRun>& __restrict__ characterSpacingGlyphRuns = mModel->GetCharacterSpacingGlyphRuns();
-
-    // Aggregate underline-style-properties from mModel
-    const UnderlineStyleProperties modelUnderlineProperties{mModel->GetUnderlineType(),
-                                                            mModel->GetUnderlineColor(),
-                                                            mModel->GetUnderlineHeight(),
-                                                            mModel->GetDashedUnderlineGap(),
-                                                            mModel->GetDashedUnderlineWidth(),
-                                                            true,
-                                                            true,
-                                                            true,
-                                                            true,
-                                                            true};
-
-    // Aggregate strikethrough-style-properties from mModel
-    const StrikethroughStyleProperties modelStrikethroughProperties{mModel->GetStrikethroughColor(),
-                                                                    mModel->GetStrikethroughHeight(),
-                                                                    true,
-                                                                    true};
-
-    // Get the underline runs.
-    const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
-    Vector<UnderlinedGlyphRun> underlineRuns;
-    underlineRuns.Resize(numberOfUnderlineRuns);
-    mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
-
-    // Get the strikethrough runs.
-    const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
-    Vector<StrikethroughGlyphRun> strikethroughRuns;
-    strikethroughRuns.Resize(numberOfStrikethroughRuns);
-    mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
-
-    bool thereAreUnderlinedGlyphs    = false;
-    bool thereAreStrikethroughGlyphs = false;
-
-    float currentUnderlinePosition   = 0.0f;
-    float currentUnderlineHeight     = modelUnderlineProperties.height;
-    float maxUnderlineHeight         = currentUnderlineHeight;
-    auto  currentUnderlineProperties = modelUnderlineProperties;
-
-    float currentStrikethroughHeight     = modelStrikethroughProperties.height;
-    float maxStrikethroughHeight         = currentStrikethroughHeight;
-    auto  currentStrikethroughProperties = modelStrikethroughProperties;
-    float strikethroughStartingYPosition = 0.0f;
-
-    FontId lastFontId = 0;
-
-    float lineExtentLeft  = bufferWidth;
-    float lineExtentRight = 0.0f;
-    float baseline        = 0.0f;
-    bool  addHyphen       = false;
-
-    // Traverses the glyphs of the line.
-    const GlyphIndex startGlyphIndex = std::max(std::max(line.glyphRun.glyphIndex, startIndexOfGlyphs), fromGlyphIndex);
-    GlyphIndex       endGlyphIndex   = (line.isSplitToTwoHalves ? line.glyphRunSecondHalf.glyphIndex + line.glyphRunSecondHalf.numberOfGlyphs : line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs) - 1u;
-    endGlyphIndex                    = std::min(std::min(endGlyphIndex, endIndexOfGlyphs), toGlyphIndex);
-
-    for(GlyphIndex glyphIndex = startGlyphIndex; glyphIndex <= endGlyphIndex; ++glyphIndex)
-    {
-      //To handle START case of ellipsis, the first glyph has been shifted
-      //glyphIndex represent indices in whole glyphs but elidedGlyphIndex represents indices in elided Glyphs
-      GlyphIndex elidedGlyphIndex = glyphIndex - startIndexOfGlyphs;
-
-      //To handle MIDDLE case of ellipsis, the first glyph in the second half of line has been shifted and skip the removed glyph from middle.
-      if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
-      {
-        if(glyphIndex > firstMiddleIndexOfElidedGlyphs &&
-           glyphIndex < secondMiddleIndexOfElidedGlyphs)
-        {
-          // Ignore any glyph that removed for MIDDLE ellipsis
-          continue;
-        }
-        if(glyphIndex >= secondMiddleIndexOfElidedGlyphs)
-        {
-          elidedGlyphIndex -= (secondMiddleIndexOfElidedGlyphs - firstMiddleIndexOfElidedGlyphs - 1u);
-        }
-      }
-
-      // Retrieve the glyph's info.
-      const GlyphInfo* glyphInfo;
-
-      if(addHyphen && hyphens)
-      {
-        glyphInfo = hyphens + hyphenIndex;
-        hyphenIndex++;
-      }
-      else
-      {
-        glyphInfo = glyphsBuffer + elidedGlyphIndex;
-      }
-
-      if((glyphInfo->width < Math::MACHINE_EPSILON_1000) ||
-         (glyphInfo->height < Math::MACHINE_EPSILON_1000))
-      {
-        // Nothing to do if the glyph's width or height is zero.
-        continue;
-      }
-
-      Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
-      const bool                                underlineGlyph              = underlineEnabled || IsGlyphUnderlined(glyphIndex, underlineRuns, currentUnderlinedGlyphRunIt);
-      currentUnderlineProperties                                            = GetCurrentUnderlineProperties(glyphIndex, underlineGlyph, underlineRuns, currentUnderlinedGlyphRunIt, modelUnderlineProperties);
-      currentUnderlineHeight                                                = currentUnderlineProperties.height;
-      thereAreUnderlinedGlyphs                                              = thereAreUnderlinedGlyphs || underlineGlyph;
-
-      Vector<StrikethroughGlyphRun>::ConstIterator currentStrikethroughGlyphRunIt = strikethroughRuns.End();
-      const bool                                   strikethroughGlyph             = strikethroughEnabled || IsGlyphStrikethrough(glyphIndex, strikethroughRuns, currentStrikethroughGlyphRunIt);
-      currentStrikethroughProperties                                              = GetCurrentStrikethroughProperties(glyphIndex, strikethroughGlyph, strikethroughRuns, currentStrikethroughGlyphRunIt, modelStrikethroughProperties);
-      currentStrikethroughHeight                                                  = currentStrikethroughProperties.height;
-      thereAreStrikethroughGlyphs                                                 = thereAreStrikethroughGlyphs || strikethroughGlyph;
-
-      // Are we still using the same fontId as previous
-      if((glyphInfo->fontId != lastFontId) && (strikethroughGlyph || underlineGlyph))
-      {
-        // We need to fetch fresh font underline metrics
-        FontMetrics fontMetrics;
-        fontClient.GetFontMetrics(glyphInfo->fontId, fontMetrics);
-
-        //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
-        currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(fontMetrics);
-
-        if(underlineGlyph)
-        {
-          CalcualteUnderlineHeight(fontMetrics, currentUnderlineHeight, maxUnderlineHeight);
-        }
-
-        if(strikethroughGlyph)
-        {
-          CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
-        }
-
-        // Update lastFontId because fontId is changed
-        lastFontId = glyphInfo->fontId; // Prevents searching for existing blocksizes when string of the same fontId.
-      }
-
-      // Retrieves the glyph's position.
-      Vector2 position = *(positionBuffer + elidedGlyphIndex);
-
-      if(addHyphen)
-      {
-        GlyphInfo   tempInfo         = *(glyphsBuffer + elidedGlyphIndex);
-        const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
-        calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + elidedGlyphIndex))), characterSpacing, tempInfo.advance);
-        position.x                   = position.x + calculatedAdvance - tempInfo.xBearing + glyphInfo->xBearing;
-        position.y                   = -glyphInfo->yBearing;
-      }
-
-      if(baseline < position.y + glyphInfo->yBearing)
-      {
-        baseline = position.y + glyphInfo->yBearing;
-      }
-
-      // Calculate the positions of leftmost and rightmost glyphs in the current line
-      if(removeFrontInset)
-      {
-        if(position.x < lineExtentLeft)
-        {
-          lineExtentLeft = position.x;
-        }
-      }
-      else
-      {
-        const float originPositionLeft = position.x - glyphInfo->xBearing;
-        if(originPositionLeft < lineExtentLeft)
-        {
-          lineExtentLeft = originPositionLeft;
-        }
-      }
-
-      if(removeBackInset)
-      {
-        if(position.x + glyphInfo->width > lineExtentRight)
-        {
-          lineExtentRight = position.x + glyphInfo->width;
-        }
-      }
-      else
-      {
-        const float originPositionRight = position.x - glyphInfo->xBearing + glyphInfo->advance;
-        if(originPositionRight > lineExtentRight)
-        {
-          lineExtentRight = originPositionRight;
-        }
-      }
-
-      // Retrieves the glyph's color.
-      const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndexBuffer + glyphIndex);
-
-      Vector4 color;
-      if(style == Typesetter::STYLE_SHADOW)
-      {
-        color = mModel->GetShadowColor();
-      }
-      else if(style == Typesetter::STYLE_OUTLINE)
-      {
-        color = mModel->GetOutlineColor();
-      }
-      else
-      {
-        color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + (colorIndex - 1u));
-      }
-
-      if(style == Typesetter::STYLE_NONE && cutoutEnabled)
-      {
-        // Temporarily adjust the transparency to 1.f
-        color.a = 1.f;
-      }
-
-      // Premultiply alpha
-      color.r *= color.a;
-      color.g *= color.a;
-      color.b *= color.a;
-
-      // Retrieves the glyph's bitmap.
-      glyphData.glyphBitmap.buffer = NULL;
-      glyphData.glyphBitmap.width  = glyphInfo->width; // Desired width and height.
-      glyphData.glyphBitmap.height = glyphInfo->height;
-
-      if(style != Typesetter::STYLE_OUTLINE && style != Typesetter::STYLE_SHADOW)
-      {
-        // Don't render outline for other styles
-        outlineWidth = 0.0f;
-      }
-
-      if(style != Typesetter::STYLE_UNDERLINE && style != Typesetter::STYLE_STRIKETHROUGH)
-      {
-        fontClient.CreateBitmap(glyphInfo->fontId,
-                                glyphInfo->index,
-                                glyphInfo->isItalicRequired,
-                                glyphInfo->isBoldRequired,
-                                glyphData.glyphBitmap,
-                                static_cast<int32_t>(outlineWidth));
-      }
-
-      // Sets the glyph's bitmap into the bitmap of the whole text.
-      if(NULL != glyphData.glyphBitmap.buffer)
-      {
-        if(style == Typesetter::STYLE_OUTLINE)
-        {
-          // Set the position offset for the current glyph
-          glyphData.horizontalOffset -= glyphData.glyphBitmap.outlineOffsetX;
-          glyphData.verticalOffset -= glyphData.glyphBitmap.outlineOffsetY;
-        }
-
-        // Set the buffer of the glyph's bitmap into the final bitmap's buffer
-        TypesetGlyph(glyphData,
-                     &position,
-                     &color,
-                     style,
-                     pixelFormat);
-
-        if(style == Typesetter::STYLE_OUTLINE)
-        {
-          // Reset the position offset for the next glyph
-          glyphData.horizontalOffset += glyphData.glyphBitmap.outlineOffsetX;
-          glyphData.verticalOffset += glyphData.glyphBitmap.outlineOffsetY;
-        }
-
-        // free the glyphBitmap.buffer if it is owner of buffer
-        if(glyphData.glyphBitmap.isBufferOwned)
-        {
-          free(glyphData.glyphBitmap.buffer);
-          glyphData.glyphBitmap.isBufferOwned = false;
-        }
-        glyphData.glyphBitmap.buffer = NULL;
-      }
-
-      if(hyphenIndices)
-      {
-        while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex]))
-        {
-          hyphenIndex++;
-        }
-
-        addHyphen = ((hyphenIndex < hyphensCount) && ((glyphIndex + 1) == hyphenIndices[hyphenIndex]));
-        if(addHyphen)
-        {
-          glyphIndex--;
-        }
-      }
-    }
-
-    // Draw the underline from the leftmost glyph to the rightmost glyph
-    if(thereAreUnderlinedGlyphs && style == Typesetter::STYLE_UNDERLINE)
-    {
-      DrawUnderline(bufferWidth, bufferHeight, glyphData, baseline, currentUnderlinePosition, maxUnderlineHeight, lineExtentLeft, lineExtentRight, modelUnderlineProperties, currentUnderlineProperties, line);
-    }
-
-    // Draw the background color from the leftmost glyph to the rightmost glyph
-    if(style == Typesetter::STYLE_BACKGROUND)
-    {
-      DrawBackgroundColor(mModel->GetBackgroundColor(), bufferWidth, bufferHeight, glyphData, baseline, line, lineExtentLeft, lineExtentRight);
-    }
-
-    // Draw the strikethrough from the leftmost glyph to the rightmost glyph
-    if(thereAreStrikethroughGlyphs && style == Typesetter::STYLE_STRIKETHROUGH)
-    {
-      //TODO : The currently implemented strikethrough creates a strikethrough on the line level. We need to create different strikethroughs the case of glyphs with different sizes.
-      strikethroughStartingYPosition = (glyphData.verticalOffset + baseline + currentUnderlinePosition) - ((line.ascender) * HALF); // Since Free Type font doesn't contain the strikethrough-position property, strikethrough position will be calculated by moving the underline position upwards by half the value of the line height.
-      DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
-    }
-
-    // Increases the vertical offset with the line's descender & line spacing.
-    glyphData.verticalOffset += static_cast<int32_t>(-line.descender + GetPostOffsetVerticalLineAlignment(line, verLineAlign));
-  }
-
-  return glyphData.bitmapBuffer;
-}
-
 Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset)
 {
+  // Use l-value to make ensure it is not nullptr, so compiler happy.
+  auto& viewModel = *(mImpl->GetViewModel());
+
   // Underline-tags (this is for Markup case)
   // Get the underline runs.
-  const Length               numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
+  const Length               numberOfUnderlineRuns = viewModel.GetNumberOfUnderlineRuns();
   Vector<UnderlinedGlyphRun> underlineRuns;
   underlineRuns.Resize(numberOfUnderlineRuns);
-  mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
+  viewModel.GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
 
   // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
   Vector<UnderlinedGlyphRun>::ConstIterator itGlyphRun    = underlineRuns.Begin();
@@ -1607,7 +486,7 @@ Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffe
     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
 
     // Create the image buffer for underline
-    Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
+    Devel::PixelBuffer underlineImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
     // Combine the two buffers
     // Result pixel buffer will be stored into topPixelBuffer.
     CombineImageBuffer(underlineImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
@@ -1620,12 +499,15 @@ Devel::PixelBuffer Typesetter::ApplyUnderlineMarkupImageBuffer(Devel::PixelBuffe
 
 Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelBuffer topPixelBuffer, const uint32_t bufferWidth, const uint32_t bufferHeight, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset)
 {
+  // Use l-value to make ensure it is not nullptr, so compiler happy.
+  auto& viewModel = *(mImpl->GetViewModel());
+
   // strikethrough-tags (this is for Markup case)
   // Get the strikethrough runs.
-  const Length                  numberOfStrikethroughRuns = mModel->GetNumberOfStrikethroughRuns();
+  const Length                  numberOfStrikethroughRuns = viewModel.GetNumberOfStrikethroughRuns();
   Vector<StrikethroughGlyphRun> strikethroughRuns;
   strikethroughRuns.Resize(numberOfStrikethroughRuns);
-  mModel->GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
+  viewModel.GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
 
   // Iterate on the consecutive strikethrough glyph run and connect them into one chunk of strikethrough characters.
   Vector<StrikethroughGlyphRun>::ConstIterator itGlyphRun    = strikethroughRuns.Begin();
@@ -1639,7 +521,7 @@ Devel::PixelBuffer Typesetter::ApplyStrikethroughMarkupImageBuffer(Devel::PixelB
     endGlyphIndex   = startGlyphIndex + itGlyphRun->glyphRun.numberOfGlyphs - 1;
 
     // Create the image buffer for strikethrough
-    Devel::PixelBuffer strikethroughImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
+    Devel::PixelBuffer strikethroughImageBuffer = mImpl->CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_STRIKETHROUGH, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
     // Combine the two buffers
     // Result pixel buffer will be stored into topPixelBuffer.
     CombineImageBuffer(strikethroughImageBuffer, topPixelBuffer, bufferWidth, bufferHeight, false);
@@ -1657,7 +539,7 @@ void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixel
   uint32_t* topBuffer    = reinterpret_cast<uint32_t*>(topPixelBuffer.GetBuffer());
   uint32_t* bottomBuffer = reinterpret_cast<uint32_t*>(bottomPixelBuffer.GetBuffer());
 
-  if(topBuffer == NULL || bottomBuffer == NULL)
+  if(topBuffer == nullptr || bottomBuffer == nullptr)
   {
     // Nothing to do if one of both buffers are empty.
     return;
@@ -1667,15 +549,15 @@ void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixel
 
   for(uint32_t pixelIndex = 0; pixelIndex < bufferSizeInt; ++pixelIndex)
   {
-    uint32_t topBufferColor = *(topBuffer);
-    uint32_t bottomBufferColor = *(bottomBuffer);
-    uint8_t* __restrict__ topBufferColorBuffer = reinterpret_cast<uint8_t*>(&topBufferColor);
+    uint32_t topBufferColor                       = *(topBuffer);
+    uint32_t bottomBufferColor                    = *(bottomBuffer);
+    uint8_t* __restrict__ topBufferColorBuffer    = reinterpret_cast<uint8_t*>(&topBufferColor);
     uint8_t* __restrict__ bottomBufferColorBuffer = reinterpret_cast<uint8_t*>(&bottomBufferColor);
 
     // Return the transparency of the text to original.
     uint8_t originAlphaInt = originAlpha * 255;
 
-    uint8_t topAlpha = topBufferColorBuffer[3];
+    uint8_t topAlpha    = topBufferColorBuffer[3];
     uint8_t bottomAlpha = 255 - topAlpha;
 
     // Manual blending.
@@ -1693,14 +575,11 @@ void Typesetter::SetMaskForImageBuffer(Devel::PixelBuffer& __restrict__ topPixel
 }
 
 Typesetter::Typesetter(const ModelInterface* const model)
-: mModel(new ViewModel(model))
+: mImpl{std::make_unique<Impl>(model)}
 {
 }
 
-Typesetter::~Typesetter()
-{
-  delete mModel;
-}
+Typesetter::~Typesetter() = default;
 
 } // namespace Text
 
index 7d268c9..bdbd598 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_TYPESETTER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 // EXTERNAL INCLUDES
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
 #include <dali/public-api/common/intrusive-ptr.h>
 #include <dali/public-api/images/pixel-data.h>
 #include <dali/public-api/images/pixel.h>
 #include <dali/public-api/object/ref-object.h>
+#include <memory> ///< for std::unique_ptr
 
 namespace Dali
 {
@@ -92,6 +94,15 @@ public:
   ViewModel* GetViewModel();
 
   /**
+   * @brief Set the font client.
+   *
+   * Set the font client used in the update/render process of the text model.
+   *
+   * @param[in] fontClient The font client used by the Typesetter.
+   */
+  void SetFontClient(TextAbstraction::FontClient& fontClient);
+
+  /**
    * @brief Renders the text.
    *
    * Does the following operations:
@@ -180,28 +191,6 @@ private:
   Typesetter& operator=(const Typesetter& handle);
 
   /**
-   * @brief Create & draw the image buffer for the given range of the glyphs in the given style.
-   *
-   * Does the following operations:
-   * - Retrieves the data buffers from the text model.
-   * - Creates the pixel data used to generate the final image with the given size.
-   * - Traverse the visible glyphs, retrieve their bitmaps and compose the final pixel data.
-   *
-   * @param[in] bufferWidth The width of the image buffer.
-   * @param[in] bufferHeight The height of the image buffer.
-   * @param[in] style The style of the text.
-   * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment, not ignored by default.
-   * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
-   * @param[in] horizontalOffset The horizontal offset to be added to the glyph's position.
-   * @param[in] verticalOffset The vertical offset to be added to the glyph's position.
-   * @param[in] fromGlyphIndex The index of the first glyph within the text to be drawn
-   * @param[in] toGlyphIndex The index of the last glyph within the text to be drawn
-   *
-   * @return An image buffer with the text.
-   */
-  Devel::PixelBuffer CreateImageBuffer(const uint32_t bufferWidth, const uint32_t bufferHeight, const Typesetter::Style style, const bool ignoreHorizontalAlignment, const Pixel::Format pixelFormat, const int32_t horizontalOffset, const int32_t verticalOffset, const TextAbstraction::GlyphIndex fromGlyphIndex, const TextAbstraction::GlyphIndex toGlyphIndex);
-
-  /**
    * @brief Apply markup underline tags.
    *
    * The properties on TextLabel override the behavior of Markup.
@@ -252,7 +241,8 @@ protected:
   virtual ~Typesetter();
 
 private:
-  ViewModel* mModel;
+  struct Impl;
+  std::unique_ptr<Impl> mImpl;
 };
 
 } // namespace Text
@@ -261,4 +251,4 @@ private:
 
 } // namespace Dali
 
-#endif // DALI_TOOLKIT_TEXT_TYPESETTER_H
\ No newline at end of file
+#endif // DALI_TOOLKIT_TEXT_TYPESETTER_H
index 7e3a4e3..b94c9e6 100644 (file)
@@ -19,7 +19,6 @@
 #include <dali-toolkit/internal/text/rendering/view-model.h>
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/text-abstraction/font-client.h>
 #include <memory.h>
 
 // INTERNAL INCLUDES
@@ -355,7 +354,7 @@ const Vector<CharacterIndex>& ViewModel::GetGlyphsToCharacters() const
   return mModel->GetGlyphsToCharacters();
 }
 
-void ViewModel::ElideGlyphs()
+void ViewModel::ElideGlyphs(TextAbstraction::FontClient& fontClient)
 {
   mIsTextElided             = false;
   mStartIndexOfElidedGlyphs = mFirstMiddleIndexOfElidedGlyphs = mSecondMiddleIndexOfElidedGlyphs = 0;
@@ -414,8 +413,7 @@ void ViewModel::ElideGlyphs()
         if(0u != numberOfActualLaidOutGlyphs)
         {
           // There are elided glyphs.
-          mIsTextElided                          = true;
-          TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+          mIsTextElided = true;
 
           // Retrieve the whole glyphs and their positions.
           const GlyphInfo* const glyphs    = mModel->GetGlyphs();
@@ -794,6 +792,11 @@ const Vector4& ViewModel::GetBackgroundColorWithCutout() const
   return mModel->GetBackgroundColorWithCutout();
 }
 
+const Vector2& ViewModel::GetOffsetWithCutout() const
+{
+  return mModel->GetOffsetWithCutout();
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index dc7363d..45b75f5 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/public-api/common/dali-vector.h>
 
 // INTERNAL INCLUDES
@@ -325,8 +326,10 @@ public:
    * between the first and last visible lines to add the ellipsis glyph.
    *
    * It stores as well a copy of the positions for each visible glyph.
+   *
+   * @param[in] fontClient FontClient to use in this function to obtain information about the ellipsis glyph.
    */
-  void ElideGlyphs();
+  void ElideGlyphs(TextAbstraction::FontClient& fontClient);
 
   /**
    * @copydoc ModelInterface::GetStrikethroughHeight()
@@ -413,6 +416,11 @@ public:
    */
   const Vector4& GetBackgroundColorWithCutout() const override;
 
+  /**
+   * @copydoc ModelInterface::GetOffsetWithCutout()
+   */
+  const Vector2& GetOffsetWithCutout() const override;
+
 private:
   const ModelInterface* const mModel;                           ///< Pointer to the text's model.
   Vector<GlyphInfo>           mElidedGlyphs;                    ///< Stores the glyphs of the elided text.
index f52710c..0a841c6 100644 (file)
@@ -19,7 +19,6 @@
 #include <dali-toolkit/internal/text/segmentation.h>
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/text-abstraction/segmentation.h>
 #ifdef DEBUG_ENABLED
 #include <dali/integration-api/debug.h>
 #include <string>
@@ -44,10 +43,11 @@ namespace Toolkit
 {
 namespace Text
 {
-void SetLineBreakInfo(const Vector<Character>& text,
-                      CharacterIndex           startIndex,
-                      Length                   numberOfCharacters,
-                      Vector<LineBreakInfo>&   lineBreakInfo)
+void SetLineBreakInfo(TextAbstraction::Segmentation& segmentation,
+                      const Vector<Character>&       text,
+                      CharacterIndex                 startIndex,
+                      Length                         numberOfCharacters,
+                      Vector<LineBreakInfo>&         lineBreakInfo)
 {
   const Length totalNumberOfCharacters = text.Count();
 
@@ -77,9 +77,9 @@ void SetLineBreakInfo(const Vector<Character>& text,
   }
 
   // Retrieve the line break info.
-  TextAbstraction::Segmentation::Get().GetLineBreakPositions(text.Begin() + startIndex,
-                                                             numberOfCharacters,
-                                                             lineBreakInfoBuffer);
+  segmentation.GetLineBreakPositions(text.Begin() + startIndex,
+                                     numberOfCharacters,
+                                     lineBreakInfoBuffer);
 
   // If the line break info is updated, it needs to be inserted in the model.
   if(updateCurrentBuffer)
@@ -109,10 +109,11 @@ void SetLineBreakInfo(const Vector<Character>& text,
 #endif
 }
 
-void SetWordBreakInfo(const Vector<Character>& text,
-                      CharacterIndex           startIndex,
-                      Length                   numberOfCharacters,
-                      Vector<WordBreakInfo>&   wordBreakInfo)
+void SetWordBreakInfo(TextAbstraction::Segmentation& segmentation,
+                      const Vector<Character>&       text,
+                      CharacterIndex                 startIndex,
+                      Length                         numberOfCharacters,
+                      Vector<WordBreakInfo>&         wordBreakInfo)
 {
   const Length totalNumberOfCharacters = text.Count();
 
@@ -142,9 +143,9 @@ void SetWordBreakInfo(const Vector<Character>& text,
   }
 
   // Retrieve the word break info.
-  TextAbstraction::Segmentation::Get().GetWordBreakPositions(text.Begin() + startIndex,
-                                                             numberOfCharacters,
-                                                             wordBreakInfoBuffer);
+  segmentation.GetWordBreakPositions(text.Begin() + startIndex,
+                                     numberOfCharacters,
+                                     wordBreakInfoBuffer);
 
   // If the word break info is updated, it needs to be inserted in the model.
   if(updateCurrentBuffer)
index 305329f..347db93 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/segmentation.h>
 #include <dali/public-api/common/dali-vector.h>
 
 // INTERNAL INCLUDES
@@ -41,15 +42,17 @@ class LogicalModel;
  *  - 1 is a LINE_ALLOW_BREAK. Is possible to break the text into a new line.
  *  - 2 is a LINE_NO_BREAK.    Text can't be broken into a new line.
  *
+ * @param[in] segmentation Segmentation to use in this function.
  * @param[in] text Vector of UTF-32 characters.
  * @param[in] startIndex The character from where the break info is set.
  * @param[in] numberOfCharacters The number of characters.
  * @param[out] lineBreakInfo The line break info
  */
-void SetLineBreakInfo(const Vector<Character>& text,
-                      CharacterIndex           startIndex,
-                      Length                   numberOfCharacters,
-                      Vector<LineBreakInfo>&   lineBreakInfo);
+void SetLineBreakInfo(TextAbstraction::Segmentation& segmentation,
+                      const Vector<Character>&       text,
+                      CharacterIndex                 startIndex,
+                      Length                         numberOfCharacters,
+                      Vector<LineBreakInfo>&         lineBreakInfo);
 
 /**
  * Sets word break info.
@@ -57,15 +60,17 @@ void SetLineBreakInfo(const Vector<Character>& text,
  * - 0 is a WORD_BREAK.    Text can be broken into a new word.
  * - 1 is a WORD_NO_BREAK. Text can't be broken into a new word.
  *
+ * @param[in] segmentation Segmentation to use in this function.
  * @param[in] text Vector of UTF-32 characters.
  * @param[in] startIndex The character from where the break info is set.
  * @param[in] numberOfCharacters The number of characters.
  * @param[out] wordBreakInfo The word break info.
  */
-void SetWordBreakInfo(const Vector<Character>& text,
-                      CharacterIndex           startIndex,
-                      Length                   numberOfCharacters,
-                      Vector<WordBreakInfo>&   wordBreakInfo);
+void SetWordBreakInfo(TextAbstraction::Segmentation& segmentation,
+                      const Vector<Character>&       text,
+                      CharacterIndex                 startIndex,
+                      Length                         numberOfCharacters,
+                      Vector<WordBreakInfo>&         wordBreakInfo);
 
 } // namespace Text
 
index 5fbabd4..9a4d768 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@
 // EXTERNAL INCLUDES
 #include <chrono>
 #include <dali/devel-api/text-abstraction/font-client.h>
-#include <dali/devel-api/text-abstraction/shaping.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/trace.h>
 
@@ -51,7 +50,9 @@ uint32_t GetMilliSeconds()
 }
 #endif
 
-void ShapeText(const Vector<Character>&     text,
+void ShapeText(TextAbstraction::Shaping&    shaping,
+               TextAbstraction::FontClient& fontClient,
+               const Vector<Character>&     text,
                const Vector<LineBreakInfo>& lineBreakInfo,
                const Vector<ScriptRun>&     scripts,
                const Vector<FontRun>&       fonts,
@@ -96,8 +97,6 @@ void ShapeText(const Vector<Character>&     text,
   // Each chunk must contain characters with the same font id and script set.
   // A chunk of consecutive characters must not contain a LINE_MUST_BREAK, if there is one a new chunk has to be created.
 
-  TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
-
   // To shape the text a font and an script is needed.
 
   // Get the font run containing the startCharacterIndex character.
@@ -209,7 +208,8 @@ void ShapeText(const Vector<Character>&     text,
 #endif
 
     // Shape the text for the current chunk.
-    const Length numberOfGlyphs = shaping.Shape(textBuffer + previousIndex,
+    const Length numberOfGlyphs = shaping.Shape(fontClient,
+                                                textBuffer + previousIndex,
                                                 (currentIndex - previousIndex), // The number of characters to shape.
                                                 currentFontId,
                                                 currentScript);
index 549031b..671bec9 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_SHAPER_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/shaping.h>
 #include <dali/public-api/common/dali-vector.h>
 
 // INTERNAL INCLUDES
@@ -37,6 +39,8 @@ class VisualModel;
 /**
  * Shapes the whole text.
  *
+ * @param[in] shaping Shaping to use in this function.
+ * @param[in] fontClient FontClient to use in this function.
  * @param[in] text Vector of UTF-32 characters.
  * @param[in] lineBreakInfo The line break info.
  * @param[in] scripts Vector containing the script runs for the whole text.
@@ -49,7 +53,9 @@ class VisualModel;
  * @param[out] charactersPerGlyph Vector containing the number of characters per glyph.
  * @param[out] newParagraphGlyphs Vector containing the indices to the new paragraph glyphs.
  */
-void ShapeText(const Vector<Character>&     text,
+void ShapeText(TextAbstraction::Shaping&    shaping,
+               TextAbstraction::FontClient& fontClient,
+               const Vector<Character>&     text,
                const Vector<LineBreakInfo>& lineBreakInfo,
                const Vector<ScriptRun>&     scripts,
                const Vector<FontRun>&       fonts,
index ba9f6ca..5083b0f 100644 (file)
@@ -543,6 +543,13 @@ public:
    * @return The color of the background with cutout.
    */
   virtual const Vector4& GetBackgroundColorWithCutout() const = 0;
+
+  /**
+   * @brief Retrieves the left and top offset with cutout.
+   *
+   * @return The offset with cutout.
+   */
+  virtual const Vector2& GetOffsetWithCutout() const = 0;
 };
 
 } // namespace Text
index bbe126b..ecb0b35 100644 (file)
@@ -381,6 +381,11 @@ const Vector4& Model::GetBackgroundColorWithCutout() const
   return mVisualModel->GetBackgroundColorWithCutout();
 }
 
+const Vector2& Model::GetOffsetWithCutout() const
+{
+  return mVisualModel->GetOffsetWithCutout();
+}
+
 Model::Model()
 : mLogicalModel(),
   mVisualModel(),
@@ -393,8 +398,8 @@ Model::Model()
   mAlignmentOffset(0.0f),
   mElideEnabled(false),
   mIgnoreSpacesAfterText(true),
-  mRemoveFrontInset(true),
-  mRemoveBackInset(true),
+  mRemoveFrontInset(false),
+  mRemoveBackInset(false),
   mMatchLayoutDirection(DevelText::MatchLayoutDirection::INHERIT),
   mEllipsisPosition(DevelText::EllipsisPosition::END)
 {
index 5661035..c94362d 100644 (file)
@@ -399,6 +399,11 @@ public:
    */
   const Vector4& GetBackgroundColorWithCutout() const override;
 
+  /**
+   * @copydoc ModelInterface::GetOffsetWithCutout()
+   */
+  const Vector2& GetOffsetWithCutout() const override;
+
 private: // Private contructors & copy operator.
   /**
    * @brief Private constructor.
index 08e08c8..bd86739 100644 (file)
@@ -150,8 +150,9 @@ void TextScroller::StopScrolling()
     {
       case TextLabel::AutoScrollStopMode::IMMEDIATE:
       {
-        mIsStop = true;
+        mIsStop = false;
         mScrollAnimation.Stop();
+        mScrollerInterface.ScrollingFinished();
         break;
       }
       case TextLabel::AutoScrollStopMode::FINISH_LOOP:
@@ -281,13 +282,9 @@ void TextScroller::AutoScrollAnimationFinished(Dali::Animation& animation)
 {
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::AutoScrollAnimationFinished\n");
   mIsStop = false;
-  mScrollerInterface.ScrollingFinished();
-
-  // Revert to the original shader and texture after scrolling
-  mRenderer.SetShader(mShader);
-  if(mTextureSet)
+  if(mStopMode == TextLabel::AutoScrollStopMode::FINISH_LOOP)
   {
-    mRenderer.SetTextures(mTextureSet);
+    mScrollerInterface.ScrollingFinished();
   }
 }
 
index 3b678dd..64b91ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
 #include <dali-toolkit/internal/text/text-view.h>
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/public-api/math/vector2.h>
 #include <memory.h>
 
@@ -383,8 +382,6 @@ View::View()
     : mImpl(NULL)
 {
   mImpl = new View::Impl();
-
-  mImpl->mFontClient = TextAbstraction::FontClient::Get();
 }
 
 View::~View()
@@ -392,6 +389,11 @@ View::~View()
   delete mImpl;
 }
 
+void View::SetFontClient(TextAbstraction::FontClient& fontClient)
+{
+  mImpl->mFontClient = fontClient;
+}
+
 void View::SetVisualModel(VisualModelPtr visualModel)
 {
   mImpl->mVisualModel = visualModel;
index d141f4b..b375668 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_VIEW_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@
  *
  */
 
+// EXTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
+
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/logical-model-impl.h>
@@ -47,6 +50,13 @@ public:
   virtual ~View();
 
   /**
+   * @brief Set the font client.
+   *
+   * @param[in] fontClient The font client used by the View.
+   */
+  void SetFontClient(TextAbstraction::FontClient& fontClient);
+
+  /**
    * @brief Set the visual model.
    *
    * @param[in] visualModel The visual model used by the View.
index 3fdd6b8..2f3ea3e 100644 (file)
@@ -695,6 +695,16 @@ const Vector4& VisualModel::GetBackgroundColorWithCutout() const
   return mBackgroundColorWithCutout;
 }
 
+void VisualModel::SetOffsetWithCutout(const Vector2& offset)
+{
+  mOffsetWithCutout = offset;
+}
+
+const Vector2& VisualModel::GetOffsetWithCutout() const
+{
+  return mOffsetWithCutout;
+}
+
 VisualModel::~VisualModel()
 {
 }
index 7ef55b5..e391113 100644 (file)
@@ -704,6 +704,20 @@ public:
    */
   const Vector4& GetBackgroundColorWithCutout() const;
 
+  /**
+   * @brief Sets the left and top offset with cutout.
+   *
+   * @param[in] offset The offset to set.
+   */
+  void SetOffsetWithCutout(const Vector2& offset);
+
+  /**
+   * @brief Retrieves the left and top offset with cutout.
+   *
+   * @return The offset.
+   */
+  const Vector2& GetOffsetWithCutout() const;
+
 protected:
   /**
    * @brief A reference counted object may only be deleted by calling Unreference().
@@ -750,11 +764,12 @@ public:
   float                            mDashedUnderlineWidth;       ///< The width of the dashes of the dashed underline.
   float                            mDashedUnderlineGap;         ///< The gap between the dashes of the dashed underline.
   float                            mShadowBlurRadius;           ///< Blur radius of shadow, 0 indicates no blur.
-  float                            mOutlineBlurRadius;      ///< Blur radius of outline, 0 indicates no blur.
+  float                            mOutlineBlurRadius;          ///< Blur radius of outline, 0 indicates no blur.
   uint16_t                         mOutlineWidth;               ///< Width of outline.
   Vector<StrikethroughGlyphRun>    mStrikethroughRuns;          ///< Runs of glyphs that have strikethrough.
   Vector<CharacterSpacingGlyphRun> mCharacterSpacingRuns;       ///< Runs of glyphs that have character-spacing.
   Vector4                          mBackgroundColorWithCutout;  ///< Background color with cutout.
+  Vector2                          mOffsetWithCutout;           ///< Left and top offset when cutout.
 
 private:
   Size mNaturalSize;    ///< Size of the text with no line wrapping.
index 972b919..84a13cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ void TextureAsyncLoadingHelper::Load(const TextureManager::TextureId
   LoadingTaskPtr loadingTask;
   if(DALI_UNLIKELY(url.IsBufferResource()))
   {
-    loadingTask = new LoadingTask(++mLoadTaskId, mTextureManager.GetEncodedImageBuffer(url.GetUrl()), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad, MakeCallback(this, &TextureAsyncLoadingHelper::AsyncLoadComplete));
+    loadingTask = new LoadingTask(++mLoadTaskId, mTextureManager.GetEncodedImageBuffer(url), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad, MakeCallback(this, &TextureAsyncLoadingHelper::AsyncLoadComplete));
   }
   else
   {
index 2dfd16f..19c19bf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -504,6 +504,7 @@ TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
   const Dali::SamplingMode::Type       samplingMode,
   const TextureCacheManager::TextureId maskTextureId,
   const bool                           cropToMask,
+  const bool                           orientationCorrection,
   const uint32_t                       frameIndex)
 {
   std::vector<std::uint8_t> hashTarget;
@@ -528,6 +529,15 @@ TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
     *hashTargetPtr = (fittingMode << 3u) | (samplingMode);
   }
 
+  // Append whether we will not correction orientation. We don't do additional job when it is true, the general cases.
+  if(!orientationCorrection)
+  {
+    auto textureIdIndex = hashTarget.size();
+    hashTarget.resize(hashTarget.size() + 1u);
+    std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
+    *hashTargetPtr++            = 'F';
+  }
+
   if(maskTextureId != INVALID_TEXTURE_ID)
   {
     auto textureIdIndex = hashTarget.size();
@@ -574,6 +584,7 @@ TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
   const TextureCacheManager::StorageType    storageType,
   const TextureCacheManager::TextureId      maskTextureId,
   const bool                                cropToMask,
+  const bool                                orientationCorrection,
   const TextureCacheManager::MultiplyOnLoad preMultiplyOnLoad,
   const bool                                isAnimatedImage,
   const uint32_t                            frameIndex)
@@ -597,6 +608,7 @@ TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
            (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
            (storageType == textureInfo.storageType) &&
            (frameIndex == textureInfo.frameIndex) &&
+           (orientationCorrection == textureInfo.orientationCorrection) &&
            ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
             (fittingMode == textureInfo.fittingMode &&
              samplingMode == textureInfo.samplingMode)))
index 03075f4..10bf73d 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXTURE_CACHE_MANAGER_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -218,6 +218,7 @@ public:
     const Dali::SamplingMode::Type       samplingMode,
     const TextureCacheManager::TextureId maskTextureId,
     const bool                           cropToMask,
+    const bool                           orientationCorrection,
     const uint32_t                       frameIndex);
 
   /**
@@ -245,6 +246,7 @@ public:
     const TextureCacheManager::StorageType    storageType,
     const TextureCacheManager::TextureId      maskTextureId,
     const bool                                cropToMask,
+    const bool                                orientationCorrection,
     const TextureCacheManager::MultiplyOnLoad preMultiplyOnLoad,
     const bool                                isAnimatedImage,
     const uint32_t                            frameIndex);
index f3b5028..2b55f6a 100644 (file)
@@ -29,7 +29,7 @@
 // INTERNAL HEADERS
 #include <dali-toolkit/internal/texture-manager/texture-async-loading-helper.h>
 #include <dali-toolkit/internal/texture-manager/texture-cache-manager.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
 #include <dali-toolkit/internal/visuals/rendering-addon.h>
 
 namespace
@@ -116,7 +116,7 @@ TextureManager::~TextureManager()
 {
   if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
   {
-    Adaptor::Get().UnregisterProcessor(*this, true);
+    Adaptor::Get().UnregisterProcessorOnce(*this, true);
     mRemoveProcessorRegistered = false;
   }
 }
@@ -528,10 +528,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
 
   if(storageType != TextureManager::StorageType::RETURN_PIXEL_BUFFER)
   {
-    textureHash = mTextureCacheManager.GenerateHash(url, desiredSize, fittingMode, samplingMode, maskTextureId, cropToMask, frameIndex);
+    textureHash = mTextureCacheManager.GenerateHash(url, desiredSize, fittingMode, samplingMode, maskTextureId, cropToMask, orientationCorrection, frameIndex);
 
     // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
-    cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url, desiredSize, fittingMode, samplingMode, storageType, maskTextureId, cropToMask, preMultiplyOnLoad, (animatedImageLoading) ? true : false, frameIndex);
+    cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url, desiredSize, fittingMode, samplingMode, storageType, maskTextureId, cropToMask, orientationCorrection, preMultiplyOnLoad, (animatedImageLoading) ? true : false, frameIndex);
   }
 
   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
@@ -549,7 +549,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
 
     // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info.
     preMultiplyOnLoad = mTextureCacheManager[cacheIndex].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d, maskTextureId=%d, prevTextureId=%d, frameindex=%d, premultiplied=%d, refCount=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0, static_cast<int>(mTextureCacheManager[cacheIndex].referenceCount));
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s size=%hux%hu observer=%p ) Using cached texture id@%d, textureId=%d, maskTextureId=%d, prevTextureId=%d, frameindex=%d, premultiplied=%d, refCount=%d\n", url.GetUrl().c_str(), desiredSize.GetWidth(), desiredSize.GetHeight(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0, static_cast<int>(mTextureCacheManager[cacheIndex].referenceCount));
   }
 
   if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
@@ -560,7 +560,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
 
     // Cache new texutre, and get cacheIndex.
     cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex, loadYuvPlanes));
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d, maskTextureId=%d, frameindex=%d premultiply=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, frameIndex, preMultiply);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s size=%hux%hu observer=%p ) New texture, cacheIndex:%d, textureId=%d, maskTextureId=%d, frameindex=%d premultiply=%d\n", url.GetUrl().c_str(), desiredSize.GetWidth(), desiredSize.GetHeight(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, frameIndex, preMultiply);
   }
 
   // The below code path is common whether we are using the cache or not.
@@ -592,7 +592,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
      TextureManager::LoadState::CANCELLED != textureInfo.loadState &&
      TextureManager::LoadState::MASK_CANCELLED != textureInfo.loadState)
   {
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d, maskTextureId=%d, prevTextureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s size=%hux%hu observer=%p ) ForcedReload cacheIndex:%d, textureId=%d, maskTextureId=%d, prevTextureId=%d\n", url.GetUrl().c_str(), desiredSize.GetWidth(), desiredSize.GetHeight(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId);
     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
   }
 
@@ -751,7 +751,7 @@ void TextureManager::RequestRemove(const TextureManager::TextureId textureId, Te
       if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
       {
         mRemoveProcessorRegistered = true;
-        Adaptor::Get().RegisterProcessor(*this, true);
+        Adaptor::Get().RegisterProcessorOnce(*this, true);
       }
     }
   }
@@ -821,13 +821,8 @@ void TextureManager::Process(bool postProcessor)
 {
   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Process()\n");
 
+  mRemoveProcessorRegistered = false;
   ProcessRemoveQueue();
-
-  if(Adaptor::IsAvailable())
-  {
-    Adaptor::Get().UnregisterProcessor(*this, true);
-    mRemoveProcessorRegistered = false;
-  }
 }
 
 void TextureManager::LoadImageSynchronously(
@@ -929,7 +924,7 @@ void TextureManager::QueueLoadTexture(const TextureManager::TextureInfo& texture
 
 void TextureManager::LoadTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer)
 {
-  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F");
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): size:%hux%hu url:%s sync:%s\n", textureInfo.desiredSize.GetWidth(), textureInfo.desiredSize.GetHeight(), textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F");
   textureInfo.loadState = TextureManager::LoadState::LOADING;
   if(!textureInfo.loadSynchronously)
   {
@@ -993,7 +988,7 @@ void TextureManager::ProcessLoadQueue()
 void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo,
                                     TextureUploadObserver*       observer)
 {
-  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): url:%s observer:%p\n", textureInfo.url.GetUrl().c_str(), observer);
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ObserveTexture(): size:%hux%hu url:%s observer:%p\n", textureInfo.desiredSize.GetWidth(), textureInfo.desiredSize.GetHeight(), textureInfo.url.GetUrl().c_str(), observer);
 
   if(observer)
   {
@@ -1012,7 +1007,7 @@ void TextureManager::AsyncLoadComplete(const TextureManager::TextureId textureId
   {
     TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
 
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "  textureId:%d Url:%s CacheIndex:%d LoadState: %s\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState));
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "  textureId:%d size:%hux%hu Url:%s CacheIndex:%d LoadState: %s\n", textureInfo.textureId, textureInfo.desiredSize.GetWidth(), textureInfo.desiredSize.GetHeight(), textureInfo.url.GetUrl().c_str(), cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState));
     if(textureInfo.loadState != TextureManager::LoadState::CANCELLED && textureInfo.loadState != TextureManager::LoadState::MASK_CANCELLED)
     {
       // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified)
@@ -1256,7 +1251,7 @@ void TextureManager::ApplyMask(TextureManager::TextureInfo& textureInfo, const T
     Devel::PixelBuffer pixelBuffer     = textureInfo.pixelBuffer;
     textureInfo.pixelBuffer.Reset();
 
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F");
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): size:%hux%hu url:%s sync:%s\n", textureInfo.desiredSize.GetWidth(), textureInfo.desiredSize.GetHeight(), textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F");
 
     textureInfo.loadState  = TextureManager::LoadState::MASK_APPLYING;
     auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
@@ -1334,7 +1329,7 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
     // invalidating the reference to the textureInfo struct.
     // Texture load requests for the same URL are deferred until the end of this
     // method.
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() observer:%p textureId:%d url:%s loadState:%s\n", observer, textureId, info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState));
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() observer:%p textureId:%d size:%hux%hu url:%s loadState:%s\n", observer, textureId, info->desiredSize.GetWidth(), info->desiredSize.GetHeight(), info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState));
     // It is possible for the observer to be deleted.
     // Disconnect and remove the observer first.
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
index f460893..979839c 100644 (file)
@@ -261,20 +261,36 @@ public:
   /**
    * @copydoc TextureCacheManager::RemoveExternalTexture
    */
-  inline TextureSet RemoveExternalTexture(const std::string& url)
+  inline TextureSet RemoveExternalTexture(const VisualUrl& url)
   {
     return mTextureCacheManager.RemoveExternalTexture(url);
   }
 
   /**
+   * @copydoc TextureCacheManager::RemoveExternalTexture
+   */
+  inline TextureSet RemoveExternalTextureByUrl(const std::string& url)
+  {
+    return RemoveExternalTexture(url);
+  }
+
+  /**
    * @copydoc TextureCacheManager::RemoveEncodedImageBuffer
    */
-  inline EncodedImageBuffer RemoveEncodedImageBuffer(const std::string& url)
+  inline EncodedImageBuffer RemoveEncodedImageBuffer(const VisualUrl& url)
   {
     return mTextureCacheManager.RemoveEncodedImageBuffer(url);
   }
 
   /**
+   * @copydoc TextureCacheManager::RemoveEncodedImageBuffer
+   */
+  inline EncodedImageBuffer RemoveEncodedImageBufferByUrl(const std::string& url)
+  {
+    return RemoveEncodedImageBuffer(url);
+  }
+
+  /**
    * @copydoc TextureCacheManager::UseExternalResource
    */
   inline void UseExternalResource(const VisualUrl& url)
@@ -285,7 +301,7 @@ public:
   /**
    * @copydoc TextureCacheManager::GetEncodedImageBuffer
    */
-  inline EncodedImageBuffer GetEncodedImageBuffer(const std::string& url)
+  inline EncodedImageBuffer GetEncodedImageBuffer(const VisualUrl& url)
   {
     return mTextureCacheManager.GetEncodedImageBuffer(url);
   }
index d1b16af..5abe85e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -98,8 +98,8 @@ uint32_t TransitionSet::GetTransitionCount() const
 
 void TransitionSet::Play()
 {
-  Adaptor::Get().RegisterProcessor(*this, true);
-  Adaptor::Get().RegisterProcessor(*this, false);
+  Adaptor::Get().RegisterProcessorOnce(*this, true);
+  Adaptor::Get().RegisterProcessorOnce(*this, false);
   TransitionLifecycleController::GetInstance().AddTransitions(Dali::Toolkit::TransitionSet(this));
 }
 
@@ -140,7 +140,7 @@ void TransitionSet::TransitionStart()
         if(minimumDelays[index].first == transition->GetTarget())
         {
           minimumDelays[index].second = std::min(minimumDelays[index].second, transition->GetTimePeriod().delaySeconds);
-          found = true;
+          found                       = true;
           break;
         }
       }
@@ -221,7 +221,6 @@ void TransitionSet::Process(bool postProcessor)
   {
     TransitionStart();
   }
-  Adaptor::Get().UnregisterProcessor(*this, postProcessor);
 }
 
 } // namespace Internal
index 0835552..905c9d9 100644 (file)
@@ -133,7 +133,7 @@ AnimatedGradientVisualPtr AnimatedGradientVisual::New(VisualFactoryCache& factor
 }
 
 AnimatedGradientVisual::AnimatedGradientVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, static_cast<Toolkit::Visual::Type>(Toolkit::DevelVisual::ANIMATED_GRADIENT))
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, static_cast<Toolkit::Visual::Type>(Toolkit::DevelVisual::ANIMATED_GRADIENT))
 {
   SetupDefaultValue();
 }
index d8de5da..4ccaa66 100644 (file)
@@ -32,8 +32,8 @@
 #include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
 #include <dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h>
 #include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
@@ -49,7 +49,7 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(5); // ltr, wrap, pixel area, crop to mask, mask texture ratio
+const int CUSTOM_PROPERTY_COUNT(6); // ltr, wrap, pixel area, crop to mask, mask texture ratio, pre-multiplied alph
 
 // fitting modes
 DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE)
@@ -106,11 +106,23 @@ static constexpr Vector4  FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 static constexpr auto     LOOP_FOREVER = -1;
 static constexpr auto     FIRST_LOOP   = 0u;
 
+constexpr float MINIMUM_FRAME_SPEED_FACTOR(0.01f);
+constexpr float MAXIMUM_FRAME_SPEED_FACTOR(100.0f);
+
 constexpr uint32_t TEXTURE_COUNT_FOR_GPU_ALPHA_MASK = 2u;
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
 #endif
+
+/**
+ * @brief Safety method to calculate interval with speed factor.
+ */
+template<typename T>
+inline uint32_t CalculateInterval(const T interval, const float frameSpeedFactor)
+{
+  return DALI_LIKELY(Dali::Equals(frameSpeedFactor, 1.0f)) ? static_cast<uint32_t>(interval) : static_cast<uint32_t>(static_cast<float>(interval) / (frameSpeedFactor));
+}
 } // namespace
 
 /**
@@ -190,6 +202,22 @@ void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl)
 {
   mImageUrl             = imageUrl;
   mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
+
+  // If we fail to load the animated image, we will try to load as a normal image.
+  if(!mAnimatedImageLoading)
+  {
+    mImageUrls = new ImageCache::UrlList();
+    mImageUrls->reserve(SINGLE_IMAGE_COUNT);
+
+    for(unsigned int i = 0; i < SINGLE_IMAGE_COUNT; ++i)
+    {
+      ImageCache::UrlStore urlStore;
+      urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
+      urlStore.mUrl       = imageUrl;
+      mImageUrls->push_back(urlStore);
+    }
+    mFrameCount = SINGLE_IMAGE_COUNT;
+  }
 }
 
 void AnimatedImageVisual::CreateImageCache()
@@ -219,18 +247,20 @@ void AnimatedImageVisual::CreateImageCache()
     }
   }
 
-  if(!mImageCache)
+  if(DALI_UNLIKELY(!mImageCache))
   {
     DALI_LOG_ERROR("mImageCache is null\n");
   }
 }
 
 AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, ImageDimensions desiredSize)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::ANIMATED_IMAGE),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::ANIMATED_IMAGE),
   mFrameDelayTimer(),
   mPlacementActor(),
   mImageVisualShaderFactory(shaderFactory),
   mPixelArea(FULL_TEXTURE_RECT),
+  mPixelAreaIndex(Property::INVALID_INDEX),
+  mPreMultipliedAlphaIndex(Property::INVALID_INDEX),
   mImageUrl(),
   mAnimatedImageLoading(),
   mFrameIndexForJumpTo(0),
@@ -246,12 +276,13 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image
   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
   mMaskingData(),
   mDesiredSize(desiredSize),
+  mFrameSpeedFactor(1.0f),
   mFrameCount(0),
   mImageSize(),
   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
   mWrapModeU(WrapMode::DEFAULT),
   mWrapModeV(WrapMode::DEFAULT),
-  mFittingMode(FittingMode::SCALE_TO_FILL),
+  mFittingMode(FittingMode::VISUAL_FITTING),
   mSamplingMode(SamplingMode::BOX_THEN_LINEAR),
   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
   mStartFirstFrame(false),
@@ -266,7 +297,10 @@ AnimatedImageVisual::~AnimatedImageVisual()
   // If this is animated image, clear cache. Else if this is single frame image, this is affected be release policy.
   if(mFrameCount > SINGLE_IMAGE_COUNT || mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER)
   {
-    mImageCache->ClearCache();
+    if(DALI_LIKELY(mImageCache))
+    {
+      mImageCache->ClearCache();
+    }
   }
   delete mImageCache;
   delete mImageUrls;
@@ -276,6 +310,26 @@ void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
 {
   if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
   {
+    if(mImpl->mRenderer)
+    {
+      auto textureSet = mImpl->mRenderer.GetTextures();
+      if(textureSet && textureSet.GetTextureCount())
+      {
+        auto texture = textureSet.GetTexture(0);
+        if(texture)
+        {
+          Dali::Vector2 textureSize;
+          textureSize.x = texture.GetWidth();
+          textureSize.y = texture.GetHeight();
+          if(textureSize != Vector2::ZERO)
+          {
+            naturalSize = textureSize;
+            return;
+          }
+        }
+      }
+    }
+
     naturalSize.x = mDesiredSize.GetWidth();
     naturalSize.y = mDesiredSize.GetHeight();
     return;
@@ -297,7 +351,7 @@ void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
       }
     }
 
-    if(mImageUrl.IsValid())
+    if(mImageUrl.IsValid() && mAnimatedImageLoading)
     {
       mImageSize = mAnimatedImageLoading.GetImageSize();
     }
@@ -335,7 +389,17 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
     map.Insert(Toolkit::ImageVisual::Property::URL, value);
   }
 
-  map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
+  if(mImpl->mRenderer && mPixelAreaIndex != Property::INVALID_INDEX)
+  {
+    // Update values from Renderer
+    Vector4 pixelArea = mImpl->mRenderer.GetProperty<Vector4>(mPixelAreaIndex);
+    map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, pixelArea);
+  }
+  else
+  {
+    map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
+  }
+
   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
 
@@ -379,17 +443,31 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
+  map.Insert(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, mFrameSpeedFactor);
 }
 
 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
 {
   map.Clear();
   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
-  if(mImageUrl.IsValid())
+  map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
+  map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
+}
+
+void AnimatedImageVisual::EnablePreMultipliedAlpha(bool preMultiplied)
+{
+  if(mImpl->mRenderer)
   {
-    map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
-    map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
+    if(mPreMultipliedAlphaIndex != Property::INVALID_INDEX || !preMultiplied)
+    {
+      // RegisterUniqueProperty call SetProperty internally.
+      // Register PREMULTIPLIED_ALPHA only if it become false.
+      // Default PREMULTIPLIED_ALPHA value is 1.0f, at image-visual-shader-factory.cpp
+      mPreMultipliedAlphaIndex = mImpl->mRenderer.RegisterUniqueProperty(mPreMultipliedAlphaIndex, PREMULTIPLIED_ALPHA, preMultiplied ? 1.0f : 0.0f);
+    }
   }
+
+  Visual::Base::EnablePreMultipliedAlpha(preMultiplied);
 }
 
 void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
@@ -542,6 +620,10 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second);
       }
+      else if(keyValue.first == FRAME_SPEED_FACTOR)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, keyValue.second);
+      }
     }
   }
   // Load image immediately if LOAD_POLICY requires it
@@ -628,9 +710,9 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       if(value.Get(frameDelay))
       {
         mFrameDelay = frameDelay;
-        if(mImageCache)
+        if(DALI_LIKELY(mImageCache))
         {
-          mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
+          mImageCache->SetInterval(CalculateInterval(mFrameDelay, mFrameSpeedFactor));
         }
       }
       break;
@@ -774,6 +856,22 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       }
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR:
+    {
+      float frameSpeedFactor = 1.0f;
+      if(value.Get(frameSpeedFactor))
+      {
+        // TODO : Could we remove this limitation?
+        Dali::ClampInPlace(frameSpeedFactor, MINIMUM_FRAME_SPEED_FACTOR, MAXIMUM_FRAME_SPEED_FACTOR);
+
+        if(!Dali::Equals(mFrameSpeedFactor, frameSpeedFactor))
+        {
+          mFrameSpeedFactor = frameSpeedFactor;
+        }
+      }
+      break;
+    }
   }
 }
 
@@ -784,13 +882,6 @@ void AnimatedImageVisual::DoSetOnScene(Actor& actor)
   PrepareTextureSet();
 
   actor.InheritedVisibilityChangedSignal().Connect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged);
-
-  Window window = DevelWindow::Get(actor);
-  if(window)
-  {
-    mPlacementWindow = window;
-    DevelWindow::VisibilityChangedSignal(window).Connect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
-  }
 }
 
 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
@@ -806,7 +897,10 @@ void AnimatedImageVisual::DoSetOffScene(Actor& actor)
   actor.RemoveRenderer(mImpl->mRenderer);
   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
   {
-    mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+    if(DALI_LIKELY(mImageCache))
+    {
+      mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+    }
     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
 
     TextureSet textureSet = TextureSet::New();
@@ -819,13 +913,6 @@ void AnimatedImageVisual::DoSetOffScene(Actor& actor)
   mCurrentLoopIndex  = FIRST_LOOP;
 
   actor.InheritedVisibilityChangedSignal().Disconnect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged);
-
-  Window window = mPlacementWindow.GetHandle();
-  if(window)
-  {
-    DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
-    mPlacementWindow.Reset();
-  }
 }
 
 void AnimatedImageVisual::OnSetTransform()
@@ -852,7 +939,7 @@ Shader AnimatedImageVisual::GenerateShader() const
   Shader shader;
   shader = mImageVisualShaderFactory.GetShader(
     mFactoryCache,
-    ImageVisualShaderFeatureBuilder()
+    ImageVisualShaderFeature::FeatureBuilder()
       .ApplyDefaultTextureWrapMode(defaultWrapMode)
       .EnableRoundedCorner(IsRoundedCornerRequired())
       .EnableBorderline(IsBorderlineRequired())
@@ -884,7 +971,7 @@ void AnimatedImageVisual::OnInitialize()
 
   if(mPixelArea != FULL_TEXTURE_RECT)
   {
-    mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
+    mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
   }
 
   if(mMaskingData)
@@ -892,7 +979,7 @@ void AnimatedImageVisual::OnInitialize()
     mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
   }
 
-  // Enable PreMultipliedAlpha if it need premultiplied
+  // Enable PreMultipliedAlpha if it need.
   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
@@ -921,7 +1008,7 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t first
   {
     if(mFrameCount > SINGLE_IMAGE_COUNT)
     {
-      mFrameDelayTimer = Timer::New(firstInterval);
+      mFrameDelayTimer = Timer::New(CalculateInterval(firstInterval, mFrameSpeedFactor));
       mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
       mFrameDelayTimer.Start();
     }
@@ -936,10 +1023,15 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t first
 void AnimatedImageVisual::PrepareTextureSet()
 {
   TextureSet textureSet;
-  if(mImageCache)
+  if(DALI_LIKELY(mImageCache))
   {
     textureSet = mImageCache->FirstFrame();
   }
+  else
+  {
+    // preMultiplied should be false because broken image don't premultiply alpha on load
+    FrameReady(TextureSet(), 0, false);
+  }
 
   // Check whether synchronous loading is true or false for the first frame.
   if(textureSet)
@@ -990,7 +1082,10 @@ void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, b
 
   if(mStartFirstFrame)
   {
-    mFrameCount = mImageCache->GetTotalFrameCount();
+    if(DALI_LIKELY(mImageCache))
+    {
+      mFrameCount = mImageCache->GetTotalFrameCount();
+    }
     StartFirstFrame(textureSet, interval);
   }
   else
@@ -999,7 +1094,7 @@ void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, b
     {
       if(mFrameDelayTimer && interval > 0u)
       {
-        mFrameDelayTimer.SetInterval(interval);
+        mFrameDelayTimer.SetInterval(CalculateInterval(interval, mFrameSpeedFactor));
       }
       mImpl->mRenderer.SetTextures(textureSet);
       CheckMaskTexture();
@@ -1012,7 +1107,7 @@ bool AnimatedImageVisual::DisplayNextFrame()
   TextureSet textureSet;
   bool       continueTimer = false;
 
-  if(mImageCache)
+  if(DALI_LIKELY(mImageCache))
   {
     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
 
@@ -1073,7 +1168,7 @@ bool AnimatedImageVisual::DisplayNextFrame()
         mImpl->mRenderer.SetTextures(textureSet);
         CheckMaskTexture();
       }
-      mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
+      mFrameDelayTimer.SetInterval(CalculateInterval(mImageCache->GetFrameInterval(frameIndex), mFrameSpeedFactor));
     }
 
     mCurrentFrameIndex = frameIndex;
@@ -1094,8 +1189,13 @@ TextureSet AnimatedImageVisual::SetLoadingFailed()
   {
     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
   }
-  mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
-  TextureSet textureSet = mImpl->mRenderer.GetTextures();
+
+  TextureSet textureSet;
+  if(DALI_LIKELY(mImpl->mRenderer))
+  {
+    mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+    textureSet = mImpl->mRenderer.GetTextures();
+  }
 
   if(mFrameDelayTimer)
   {
@@ -1144,16 +1244,6 @@ void AnimatedImageVisual::OnControlInheritedVisibilityChanged(Actor actor, bool
   }
 }
 
-void AnimatedImageVisual::OnWindowVisibilityChanged(Window window, bool visible)
-{
-  if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
-  {
-    mActionStatus = DevelAnimatedImageVisual::Action::STOP;
-    DisplayNextFrame();
-    DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnWindowVisibilityChanged: invisibile. Pause animation [%p]\n", this);
-  }
-}
-
 } // namespace Internal
 
 } // namespace Toolkit
index d8c2f8e..63e87ae 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_ANIMATED_IMAGE_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,9 +20,9 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/actors/actor-devel.h>
-#include <dali/public-api/adaptor-framework/window.h>
 #include <dali/devel-api/adaptor-framework/animated-image-loading.h>
 #include <dali/public-api/adaptor-framework/timer.h>
+#include <dali/public-api/adaptor-framework/window.h>
 #include <dali/public-api/common/dali-vector.h>
 #include <dali/public-api/common/intrusive-ptr.h>
 #include <dali/public-api/math/vector4.h>
@@ -134,6 +134,11 @@ public: // from Visual
   void DoCreateInstancePropertyMap(Property::Map& map) const override;
 
   /**
+   * @copydoc Visual::Base::EnablePreMultipliedAlpha
+   */
+  void EnablePreMultipliedAlpha(bool preMultiplied) override;
+
+  /**
    * @copydoc Visual::Base::OnDoAction
    */
   void OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) override;
@@ -263,11 +268,6 @@ private:
    */
   void OnControlInheritedVisibilityChanged(Actor actor, bool visible);
 
-  /**
-   * @brief Callback when the visibility of the window is changed.
-   */
-  void OnWindowVisibilityChanged(Window window, bool visible);
-
   // Undefined
   AnimatedImageVisual(const AnimatedImageVisual& animatedImageVisual);
 
@@ -276,12 +276,15 @@ private:
 
 private:
   Timer                     mFrameDelayTimer;
-  WeakHandle<Window>        mPlacementWindow;
   WeakHandle<Actor>         mPlacementActor;
   ImageVisualShaderFactory& mImageVisualShaderFactory;
 
+  // Variables for Image renderer
+  Vector4         mPixelArea;
+  Property::Index mPixelAreaIndex;
+  Property::Index mPreMultipliedAlphaIndex; ///< Index of premultipliedAlpha uniform.
+
   // Variables for Animated Image player
-  Vector4                    mPixelArea;
   VisualUrl                  mImageUrl;
   Dali::AnimatedImageLoading mAnimatedImageLoading; // Only needed for animated image
   uint32_t                   mFrameIndexForJumpTo;  // Frame index into textureRects
@@ -301,6 +304,7 @@ private:
   Dali::Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;
   TextureManager::MaskingDataPointer              mMaskingData;
   Dali::ImageDimensions                           mDesiredSize;
+  float                                           mFrameSpeedFactor;
 
   // Shared variables
   uint32_t        mFrameCount; // Number of frames
index e751f90..96f0f3e 100644 (file)
@@ -18,7 +18,7 @@
 #include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
 
 // INTERNAL HEADERS
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h> // For ImageAtlasManagerPtr
 
 // EXTERNAL HEADERS
 #include <dali/integration-api/adaptor-framework/adaptor.h>
@@ -67,14 +67,22 @@ TextureSet FixedImageCache::Frame(uint32_t frameIndex)
     return textureSet;
   }
 
-  while(mReadyFlags.size() < mImageUrls.size() &&
-        (frameIndex > mCurrentFrameIndex || mReadyFlags.empty()))
+  mCurrentFrameIndex = frameIndex;
+
+  bool batchRequested = false;
+
+  // Make ensure that current frameIndex load requested.
+  while(mReadyFlags.size() <= frameIndex)
   {
-    ++mCurrentFrameIndex;
+    batchRequested = true;
     LoadBatch();
   }
 
-  mCurrentFrameIndex = frameIndex;
+  // Request batch only 1 times for this function.
+  if(!batchRequested && mReadyFlags.size() < mImageUrls.size())
+  {
+    LoadBatch();
+  }
 
   if(IsFrameReady(mCurrentFrameIndex) && mLoadState != TextureManager::LoadState::LOAD_FAILED)
   {
@@ -108,7 +116,7 @@ int32_t FixedImageCache::GetTotalFrameCount() const
 
 bool FixedImageCache::IsFrameReady(uint32_t frameIndex) const
 {
-  return (mReadyFlags.size() > 0 && mReadyFlags[frameIndex] == true);
+  return ((mReadyFlags.size() > 0) && (mReadyFlags[frameIndex] == true));
 }
 
 void FixedImageCache::LoadBatch()
@@ -139,8 +147,10 @@ void FixedImageCache::LoadBatch()
     auto preMultiplyOnLoading = mPreMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
                                                    : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
-    mTextureManager.LoadTexture(url, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mImageUrls[frameIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoading);
-    mRequestingLoad = false;
+    TextureManager::TextureId loadTextureId = TextureManager::INVALID_TEXTURE_ID;
+    mTextureManager.LoadTexture(url, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, loadTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoading);
+    mImageUrls[frameIndex].mTextureId = loadTextureId;
+    mRequestingLoad                   = false;
   }
 }
 
@@ -180,8 +190,8 @@ void FixedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureI
 {
   if(loadSuccess)
   {
-    mLoadState               = TextureManager::LoadState::LOAD_FINISHED;
-    bool isCurrentFrameReady = IsFrameReady(mCurrentFrameIndex);
+    mLoadState                = TextureManager::LoadState::LOAD_FINISHED;
+    bool wasCurrentFrameReady = IsFrameReady(mCurrentFrameIndex);
     if(!mRequestingLoad)
     {
       for(std::size_t i = 0; i < mImageUrls.size(); ++i)
@@ -195,9 +205,14 @@ void FixedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureI
     }
     else
     {
-      mReadyFlags.back() = true;
+      DALI_ASSERT_ALWAYS(mReadyFlags.size() > 0u && "Some FixedImageCache::LoadBatch() called mismatched!");
+      size_t i = mReadyFlags.size() - 1u;
+
+      // texture id might not setup yet. Update it now.
+      mImageUrls[i].mTextureId = textureInformation.textureId;
+      mReadyFlags[i]           = true;
     }
-    MakeReady(isCurrentFrameReady, mCurrentFrameIndex, textureInformation.preMultiplied);
+    MakeReady(wasCurrentFrameReady, mCurrentFrameIndex, textureInformation.preMultiplied);
   }
   else
   {
index 3ece4cb..0cffcd0 100644 (file)
@@ -19,7 +19,7 @@
 
 // INTERNAL HEADERS
 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h> // For ImageAtlasManagerPtr
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 
index d818417..c83161c 100644 (file)
@@ -18,7 +18,7 @@
 #include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
 
 // INTERNAL HEADERS
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h> // For ImageAtlasManagerPtr
 
 // EXTERNAL HEADERS
 #include <dali/integration-api/adaptor-framework/adaptor.h>
index 9fc2fda..959694b 100644 (file)
@@ -32,8 +32,8 @@
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
@@ -65,6 +65,9 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(LOOPING_MODE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::LoopingMode, AUTO_REVERSE)
 DALI_ENUM_TO_STRING_TABLE_END(LOOPING_MODE)
 
+constexpr float MINIMUM_FRAME_SPEED_FACTOR(0.01f);
+constexpr float MAXIMUM_FRAME_SPEED_FACTOR(100.0f);
+
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
 #endif
@@ -87,7 +90,7 @@ AnimatedVectorImageVisualPtr AnimatedVectorImageVisual::New(VisualFactoryCache&
 }
 
 AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, ImageDimensions size)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, static_cast<Toolkit::Visual::Type>(Toolkit::DevelVisual::ANIMATED_VECTOR_IMAGE)),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, static_cast<Toolkit::Visual::Type>(Toolkit::DevelVisual::ANIMATED_VECTOR_IMAGE)),
   mImageUrl(imageUrl),
   mAnimationData(),
   mVectorAnimationTask(new VectorAnimationTask(factoryCache)),
@@ -98,6 +101,7 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual(VisualFactoryCache& factory
   mPlacementActor(),
   mPlayState(DevelImageVisual::PlayState::STOPPED),
   mEventCallback(nullptr),
+  mFrameSpeedFactor(1.0f),
   mLastSentPlayStateId(0u),
   mLoadFailed(false),
   mRendererAdded(false),
@@ -120,7 +124,7 @@ AnimatedVectorImageVisual::~AnimatedVectorImageVisual()
     if(mImageUrl.IsBufferResource())
     {
       TextureManager& textureManager = mFactoryCache.GetTextureManager();
-      textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
+      textureManager.RemoveEncodedImageBuffer(mImageUrl);
     }
 
     if(mEventCallback)
@@ -214,6 +218,7 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
   map.Insert(Toolkit::DevelImageVisual::Property::ENABLE_FRAME_CACHE, mEnableFrameCache);
   map.Insert(Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION, mNotifyAfterRasterization);
+  map.Insert(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, mFrameSpeedFactor);
 }
 
 void AnimatedVectorImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -282,6 +287,10 @@ void AnimatedVectorImageVisual::DoSetProperties(const Property::Map& propertyMap
       {
         DoSetProperty(Toolkit::DevelImageVisual::Property::NOTIFY_AFTER_RASTERIZATION, keyValue.second);
       }
+      else if(keyValue.first == FRAME_SPEED_FACTOR)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR, keyValue.second);
+      }
     }
   }
 
@@ -417,6 +426,25 @@ void AnimatedVectorImageVisual::DoSetProperty(Property::Index index, const Prope
       }
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::FRAME_SPEED_FACTOR:
+    {
+      float frameSpeedFactor = 1.0f;
+      if(value.Get(frameSpeedFactor))
+      {
+        // TODO : Could we remove this limitation?
+        Dali::ClampInPlace(frameSpeedFactor, MINIMUM_FRAME_SPEED_FACTOR, MAXIMUM_FRAME_SPEED_FACTOR);
+
+        if(!Dali::Equals(mFrameSpeedFactor, frameSpeedFactor))
+        {
+          mFrameSpeedFactor = frameSpeedFactor;
+
+          mAnimationData.frameSpeedFactor = mFrameSpeedFactor;
+          mAnimationData.resendFlag |= VectorAnimationTask::RESEND_FRAME_SPEED_FACTOR;
+        }
+      }
+      break;
+    }
   }
 }
 
@@ -433,9 +461,9 @@ void AnimatedVectorImageVisual::OnInitialize(void)
     // EncodedImageBuffer.
     // Reference count will be decreased at destructor of the visual.
     TextureManager& textureManager = mFactoryCache.GetTextureManager();
-    textureManager.UseExternalResource(mImageUrl.GetUrl());
+    textureManager.UseExternalResource(mImageUrl);
 
-    encodedImageBuffer = textureManager.GetEncodedImageBuffer(mImageUrl.GetUrl());
+    encodedImageBuffer = textureManager.GetEncodedImageBuffer(mImageUrl);
   }
 
   mVectorAnimationTask->KeepRasterizedBuffer(mEnableFrameCache);
@@ -483,13 +511,6 @@ void AnimatedVectorImageVisual::DoSetOnScene(Actor& actor)
 
     actor.InheritedVisibilityChangedSignal().Connect(this, &AnimatedVectorImageVisual::OnControlInheritedVisibilityChanged);
 
-    Window window = DevelWindow::Get(actor);
-    if(window)
-    {
-      mPlacementWindow = window;
-      DevelWindow::VisibilityChangedSignal(window).Connect(this, &AnimatedVectorImageVisual::OnWindowVisibilityChanged);
-    }
-
     if(mImpl->mEventObserver)
     {
       // The visual needs it's size set before it can be rasterized hence request relayout once on stage
@@ -520,13 +541,6 @@ void AnimatedVectorImageVisual::DoSetOffScene(Actor& actor)
 
   actor.InheritedVisibilityChangedSignal().Disconnect(this, &AnimatedVectorImageVisual::OnControlInheritedVisibilityChanged);
 
-  Window window = mPlacementWindow.GetHandle();
-  if(window)
-  {
-    DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &AnimatedVectorImageVisual::OnWindowVisibilityChanged);
-    mPlacementWindow.Reset();
-  }
-
   mPlacementActor.Reset();
 
   // Reset the visual size to zero so that when adding the actor back to stage the rasterization is forced
@@ -628,7 +642,7 @@ void AnimatedVectorImageVisual::OnDoAction(const Property::Index actionId, const
   TriggerVectorRasterization();
 }
 
-void AnimatedVectorImageVisual::OnDoActionExtension(const Property::Index actionId, Dali::Any attributes)
+void AnimatedVectorImageVisual::OnDoActionExtension(const Property::Index actionId, const Dali::Any& attributes)
 {
   switch(actionId)
   {
@@ -868,17 +882,6 @@ void AnimatedVectorImageVisual::OnControlInheritedVisibilityChanged(Actor actor,
   }
 }
 
-void AnimatedVectorImageVisual::OnWindowVisibilityChanged(Window window, bool visible)
-{
-  if(!visible)
-  {
-    StopAnimation();
-    TriggerVectorRasterization();
-
-    DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnWindowVisibilityChanged: invisibile. Pause animation [%p]\n", this);
-  }
-}
-
 void AnimatedVectorImageVisual::OnProcessEvents()
 {
   SendAnimationData();
@@ -901,7 +904,7 @@ Shader AnimatedVectorImageVisual::GenerateShader() const
   {
     shader = mImageVisualShaderFactory.GetShader(
       mFactoryCache,
-      ImageVisualShaderFeatureBuilder()
+      ImageVisualShaderFeature::FeatureBuilder()
         .EnableRoundedCorner(IsRoundedCornerRequired())
         .EnableBorderline(IsBorderlineRequired())
         .SetTextureForFragmentShaderCheck(mUseNativeImage ? mImpl->mRenderer.GetTextures().GetTexture(0) : Dali::Texture()));
index fe3396a..183ea2c 100644 (file)
@@ -158,7 +158,7 @@ protected:
   /**
    * @copydoc Visual::Base::OnDoActionExtension
    */
-  void OnDoActionExtension(const Property::Index actionId, Dali::Any attributes) override;
+  void OnDoActionExtension(const Property::Index actionId, const Dali::Any& attributes) override;
 
 private:
   /**
@@ -217,11 +217,6 @@ private:
   void OnControlInheritedVisibilityChanged(Actor actor, bool visible);
 
   /**
-   * @brief Callback when the visibility of the window is changed.
-   */
-  void OnWindowVisibilityChanged(Window window, bool visible);
-
-  /**
    * @brief Callback when the event is processed.
    */
   void OnProcessEvents();
@@ -233,7 +228,6 @@ private:
   AnimatedVectorImageVisual& operator=(const AnimatedVectorImageVisual& visual) = delete;
 
 private:
-  WeakHandle<Window>                 mPlacementWindow;
   VisualUrl                          mImageUrl;
   VectorAnimationTask::AnimationData mAnimationData;
   VectorAnimationTaskPtr             mVectorAnimationTask;
@@ -246,6 +240,7 @@ private:
   WeakHandle<Actor>                  mPlacementActor;
   DevelImageVisual::PlayState::Type  mPlayState;
   CallbackBase*                      mEventCallback; // Not owned
+  float                              mFrameSpeedFactor;
 
   uint32_t mLastSentPlayStateId;
 
index 2d1fce9..55253bd 100644 (file)
@@ -45,18 +45,14 @@ DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false)
 VectorAnimationManager::VectorAnimationManager()
 : mEventCallbacks(),
   mVectorAnimationThread(nullptr),
-  mProcessorRegistered(false)
+  mProcessorRegistered(false),
+  mDestroyed(false)
 {
 }
 
 VectorAnimationManager::~VectorAnimationManager()
 {
-  mEventCallbacks.Clear();
-
-  if(mProcessorRegistered && Adaptor::IsAvailable())
-  {
-    Adaptor::Get().UnregisterProcessor(*this, true);
-  }
+  Finalize();
 }
 
 VectorAnimationThread& VectorAnimationManager::GetVectorAnimationThread()
@@ -71,12 +67,19 @@ VectorAnimationThread& VectorAnimationManager::GetVectorAnimationThread()
 
 void VectorAnimationManager::RegisterEventCallback(CallbackBase* callback)
 {
-  mEventCallbacks.PushBack(callback); ///< Take ownership of callback.
+  if(DALI_LIKELY(!mDestroyed))
+  {
+    mEventCallbacks.PushBack(callback); ///< Take ownership of callback.
 
-  if(!mProcessorRegistered)
+    if(!mProcessorRegistered && DALI_LIKELY(Adaptor::IsAvailable()))
+    {
+      Adaptor::Get().RegisterProcessorOnce(*this, true); // Use post processor to trigger after layoutting
+      mProcessorRegistered = true;
+    }
+  }
+  else
   {
-    Adaptor::Get().RegisterProcessor(*this, true); // Use post processor to trigger after layoutting
-    mProcessorRegistered = true;
+    delete callback; // No longer needed.
   }
 }
 
@@ -86,52 +89,67 @@ void VectorAnimationManager::UnregisterEventCallback(CallbackBase* callback)
   if(iter != mEventCallbacks.End())
   {
     mEventCallbacks.Erase(iter);
+  }
+}
 
-    if(mEventCallbacks.Count() == 0u)
+void VectorAnimationManager::Finalize()
+{
+  if(DALI_LIKELY(!mDestroyed))
+  {
+    DALI_LOG_DEBUG_INFO("Finalizing Vector Animation Manager.\n");
+    mDestroyed = true;
+
+    mEventCallbacks.Clear();
+
+    if(mProcessorRegistered && Adaptor::IsAvailable())
     {
-      if(Adaptor::IsAvailable())
-      {
-        Adaptor::Get().UnregisterProcessor(*this, true);
-        mProcessorRegistered = false;
-      }
+      Adaptor::Get().UnregisterProcessorOnce(*this, true);
+      mProcessorRegistered = false;
+    }
+
+    if(mVectorAnimationThread)
+    {
+      mVectorAnimationThread->Finalize();
     }
   }
 }
 
 void VectorAnimationManager::Process(bool postProcessor)
 {
-#ifdef TRACE_ENABLED
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  if(DALI_LIKELY(!mDestroyed))
   {
-    if(mEventCallbacks.Count() > 0u)
+#ifdef TRACE_ENABLED
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
     {
-      std::ostringstream oss;
-      oss << "[" << mEventCallbacks.Count() << "]";
-      DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_VECTOR_ANIMATION_MANAGER_PROCESS", oss.str().c_str());
+      if(mEventCallbacks.Count() > 0u)
+      {
+        std::ostringstream oss;
+        oss << "[" << mEventCallbacks.Count() << "]";
+        DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_VECTOR_ANIMATION_MANAGER_PROCESS", oss.str().c_str());
+      }
     }
-  }
 #endif
 
-  for(auto&& iter : mEventCallbacks)
-  {
-    CallbackBase::Execute(*iter);
-  }
+    mProcessorRegistered = false;
+
+    for(auto&& iter : mEventCallbacks)
+    {
+      CallbackBase::Execute(*iter);
+    }
 
 #ifdef TRACE_ENABLED
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
-    if(mEventCallbacks.Count() > 0u)
+    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
     {
-      std::ostringstream oss;
-      oss << "[" << mEventCallbacks.Count() << "]";
-      DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_VECTOR_ANIMATION_MANAGER_PROCESS", oss.str().c_str());
+      if(mEventCallbacks.Count() > 0u)
+      {
+        std::ostringstream oss;
+        oss << "[" << mEventCallbacks.Count() << "]";
+        DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_VECTOR_ANIMATION_MANAGER_PROCESS", oss.str().c_str());
+      }
     }
-  }
 #endif
-  mEventCallbacks.Clear();
-
-  Adaptor::Get().UnregisterProcessor(*this, true);
-  mProcessorRegistered = false;
+    mEventCallbacks.Clear();
+  }
 }
 
 } // namespace Internal
index 53dbb06..ec7d26e 100644 (file)
@@ -72,6 +72,11 @@ public:
    */
   void UnregisterEventCallback(CallbackBase* callback);
 
+  /**
+   * @brief Finalize the manager. This will stop the animation thread and clear all resources.
+   */
+  void Finalize();
+
 protected: // Implementation of Processor
   /**
    * @copydoc Dali::Integration::Processor::Process()
@@ -98,6 +103,7 @@ private:
 
   std::unique_ptr<VectorAnimationThread> mVectorAnimationThread;
   bool                                   mProcessorRegistered : 1;
+  bool                                   mDestroyed : 1;
 };
 
 } // namespace Internal
index ac448c0..8727a15 100644 (file)
@@ -28,7 +28,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h>
 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
 
 #ifdef TRACE_ENABLED
 #include <chrono>
@@ -64,6 +64,11 @@ uint64_t GetNanoseconds()
 }
 #endif
 
+int64_t CalculateFrameDurationMicroSeconds(const float frameRate, const float frameSpeedFactor)
+{
+  return static_cast<int64_t>(MICROSECONDS_PER_SECOND / static_cast<double>(frameRate * frameSpeedFactor));
+}
+
 } // unnamed namespace
 
 VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
@@ -84,6 +89,7 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mNextFrameStartTime(),
   mFrameDurationMicroSeconds(MICROSECONDS_PER_SECOND / 60.0f),
   mFrameRate(60.0f),
+  mFrameSpeedFactor(1.0f),
   mCurrentFrame(0),
   mTotalFrame(0),
   mStartFrame(0),
@@ -172,15 +178,12 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
 #ifdef TRACE_ENABLED
   uint64_t mStartTimeNanoSceonds = 0;
   uint64_t mEndTimeNanoSceonds   = 0;
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+#endif
+
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_LOADING_TASK", [&](std::ostringstream& oss) {
     mStartTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << "[u:" << mImageUrl.GetEllipsedUrl() << "]";
-    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("BEGIN: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 
   if(mEncodedImageBuffer)
   {
@@ -220,19 +223,14 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
         mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get(), 0u);
       }
     }
-#ifdef TRACE_ENABLED
-    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-    {
+
+    DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_LOADING_TASK", [&](std::ostringstream& oss) {
       mEndTimeNanoSceonds = GetNanoseconds();
-      std::ostringstream oss;
       oss << std::fixed << std::setprecision(3);
       oss << "[";
       oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
       oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
-      // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-      DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
-    }
-#endif
+    });
     return false;
   }
 
@@ -240,8 +238,9 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
 
   mEndFrame = mTotalFrame - 1;
 
-  mFrameRate                 = mVectorRenderer.GetFrameRate();
-  mFrameDurationMicroSeconds = MICROSECONDS_PER_SECOND / mFrameRate;
+  mFrameRate = mVectorRenderer.GetFrameRate();
+
+  mFrameDurationMicroSeconds = CalculateFrameDurationMicroSeconds(mFrameRate, mFrameSpeedFactor);
 
   mLoadRequest = false;
   {
@@ -254,19 +253,13 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mImageUrl.GetUrl().c_str(), mTotalFrame, mFrameRate, this);
 
-#ifdef TRACE_ENABLED
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_LOTTIE_LOADING_TASK", [&](std::ostringstream& oss) {
     mEndTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << std::fixed << std::setprecision(3);
     oss << "[";
     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
-    // DALI_TRACE_END(gTraceFilter, "DALI_LOTTIE_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("END: DALI_LOTTIE_LOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 
   return true;
 }
@@ -739,7 +732,6 @@ bool VectorAnimationTask::Rasterize()
   // Forcely trigger render once if need.
   if(mNotifyAfterRasterization || mNeedForceRenderOnceTrigger)
   {
-    Mutex::ScopedLock lock(mMutex);
     mVectorAnimationThread.RequestForceRenderOnce();
     mNeedForceRenderOnceTrigger = false;
   }
@@ -755,7 +747,12 @@ bool VectorAnimationTask::Rasterize()
     oss << "[";
     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
     oss << "s:" << mWidth << "x" << mHeight << " ";
-    oss << "f:" << mCurrentFrame << " ";
+    oss << "f:" << mCurrentFrame;
+    if(mDroppedFrames > 0)
+    {
+      oss << "(+" << mDroppedFrames << ")";
+    }
+    oss << " ";
     oss << "l:" << mCurrentLoop << " ";
     oss << "p:" << mPlayState << " ";
     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
@@ -797,31 +794,36 @@ uint32_t VectorAnimationTask::GetStoppedFrame(uint32_t startFrame, uint32_t endF
   return frame;
 }
 
+/// Event & VectorAnimationThread called after Rasterize() finished.
 VectorAnimationTask::TimePoint VectorAnimationTask::CalculateNextFrameTime(bool renderNow)
 {
   // std::chrono::time_point template has second parameter duration which defaults to the std::chrono::steady_clock supported
   // duration. In some C++11 implementations it is a milliseconds duration, so it fails to compile unless mNextFrameStartTime
   // is casted to use the default duration.
-  mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
-  auto current        = std::chrono::steady_clock::now();
-  mDroppedFrames      = 0;
+  auto current = std::chrono::steady_clock::now();
 
   if(renderNow)
   {
     mNextFrameStartTime = current;
+    mDroppedFrames      = 0;
   }
-  else if(mNextFrameStartTime < current)
+  else
   {
-    uint32_t droppedFrames = 0;
+    uint32_t   droppedFrames        = 0;
+    const auto durationMicroSeconds = std::chrono::microseconds(mFrameDurationMicroSeconds);
 
-    while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds)) && droppedFrames < mTotalFrame)
+    mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + durationMicroSeconds);
+    if(mNextFrameStartTime < current)
     {
-      droppedFrames++;
-      mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + std::chrono::microseconds(mFrameDurationMicroSeconds));
-    }
+      while(current > std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + durationMicroSeconds) && droppedFrames < mTotalFrame)
+      {
+        droppedFrames++;
+        mNextFrameStartTime = std::chrono::time_point_cast<TimePoint::duration>(mNextFrameStartTime + durationMicroSeconds);
+      }
 
-    mNextFrameStartTime = current;
-    mDroppedFrames      = droppedFrames;
+      mNextFrameStartTime = current;
+    }
+    mDroppedFrames = droppedFrames;
   }
 
   return mNextFrameStartTime;
@@ -883,6 +885,14 @@ void VectorAnimationTask::ApplyAnimationData()
       mNotifyAfterRasterization = animationData.notifyAfterRasterization;
     }
 
+    if(animationData.resendFlag & VectorAnimationTask::RESEND_FRAME_SPEED_FACTOR)
+    {
+      mFrameSpeedFactor = animationData.frameSpeedFactor;
+
+      // Recalculate frame duration with new frame speed factor.
+      mFrameDurationMicroSeconds = CalculateFrameDurationMicroSeconds(mFrameRate, mFrameSpeedFactor);
+    }
+
     if(animationData.resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
     {
       mVectorRenderer.InvalidateBuffer();
index c741be1..20f3b7c 100644 (file)
@@ -77,6 +77,7 @@ public:
     RESEND_NEED_RESOURCE_READY        = 1 << 7,
     RESEND_DYNAMIC_PROPERTY           = 1 << 8,
     RESEND_NOTIFY_AFTER_RASTERIZATION = 1 << 9,
+    RESEND_FRAME_SPEED_FACTOR         = 1 << 10,
   };
 
   /**
@@ -96,6 +97,7 @@ public:
       height(0),
       loopCount(-1),
       playStateId(0),
+      frameSpeedFactor(1.0f),
       notifyAfterRasterization(false)
     {
     }
@@ -112,7 +114,9 @@ public:
       height                   = rhs.height;
       loopCount                = rhs.loopCount;
       playStateId              = rhs.playStateId;
+      frameSpeedFactor         = rhs.frameSpeedFactor;
       notifyAfterRasterization = rhs.notifyAfterRasterization;
+
       dynamicProperties.insert(dynamicProperties.end(), rhs.dynamicProperties.begin(), rhs.dynamicProperties.end());
       return *this;
     }
@@ -128,6 +132,7 @@ public:
     uint32_t                             height;
     int32_t                              loopCount;
     uint32_t                             playStateId;
+    float                                frameSpeedFactor;
     bool                                 notifyAfterRasterization;
   };
 
@@ -406,6 +411,7 @@ private:
   TimePoint                            mNextFrameStartTime;
   int64_t                              mFrameDurationMicroSeconds;
   float                                mFrameRate;
+  float                                mFrameSpeedFactor;
   uint32_t                             mCurrentFrame;
   uint32_t                             mTotalFrame;
   uint32_t                             mStartFrame;
index 5638ec3..ff9b777 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/devel-api/adaptor-framework/thread-settings.h>
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
 #include <thread>
 
 namespace Dali
@@ -37,6 +38,8 @@ namespace
 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
 #endif
 
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false);
+
 } // unnamed namespace
 
 VectorAnimationThread::VectorAnimationThread()
@@ -46,6 +49,8 @@ VectorAnimationThread::VectorAnimationThread()
   mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
   mConditionalWait(),
   mEventTriggerMutex(),
+  mAnimationTasksMutex(),
+  mTaskCompletedMutex(),
   mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
   mTraceFactory(Dali::Adaptor::Get().GetTraceFactory()),
   mNeedToSleep(false),
@@ -59,17 +64,15 @@ VectorAnimationThread::VectorAnimationThread()
   mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
 }
 
+/// Event thread called
 VectorAnimationThread::~VectorAnimationThread()
 {
+  DALI_TRACE_SCOPE(gTraceFilter, "VECTOR_ANIMATION_THREAD_DESTROY");
+
   // Stop the thread
   {
     ConditionalWait::ScopedLock lock(mConditionalWait);
-    // Wait until some event thread trigger relative job finished.
-    {
-      Mutex::ScopedLock lock(mEventTriggerMutex);
-      mDestroyThread = true;
-    }
-    mNeedToSleep = false;
+    Finalize();
     mConditionalWait.Notify(lock);
   }
 
@@ -78,93 +81,93 @@ VectorAnimationThread::~VectorAnimationThread()
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
 
-  Join();
-}
+  // Mark as sleep thread destroyed now.
+  mSleepThread.Finalize();
 
-void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
-{
-  ConditionalWait::ScopedLock lock(mConditionalWait);
+  DALI_LOG_DEBUG_INFO("VectorAnimationThread Join request\n");
+
+  Join();
 
-  // Find if the task is already in the list except loading task
-  auto iter = std::find_if(mAnimationTasks.begin(), mAnimationTasks.end(), [task](VectorAnimationTaskPtr& element) { return (element == task && !element->IsLoadRequested()); });
-  if(iter == mAnimationTasks.end())
+  // We need to wait all working tasks are completed before destructing this thread.
+  while(mWorkingTasks.size() > 0)
   {
-    auto currentTime = task->CalculateNextFrameTime(true); // Rasterize as soon as possible
+    DALI_LOG_DEBUG_INFO("Still waiting WorkingTasks [%zu]\n", mWorkingTasks.size());
+    ConditionalWait::ScopedLock lock(mConditionalWait);
 
-    bool inserted = false;
-    for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
+    // ConditionalWait notifyed when task complete.
+    // If task complete, then remove working tasks list and then wait again, until all working tasks are completed.
+    decltype(mCompletedTasksQueue) completedTasksQueue;
     {
-      auto nextTime = (*iter)->GetNextFrameTime();
-      if(nextTime > currentTime)
+      Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+      completedTasksQueue.swap(mCompletedTasksQueue);
+    }
+    if(completedTasksQueue.empty())
+    {
+      mConditionalWait.Wait(lock);
+      // Task completed may have been added to the queue while we were waiting.
       {
-        mAnimationTasks.insert(iter, task);
-        inserted = true;
-        break;
+        Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+        completedTasksQueue.swap(mCompletedTasksQueue);
       }
     }
 
-    if(!inserted)
+    DALI_LOG_DEBUG_INFO("Completed task queue [%zu]\n", completedTasksQueue.size());
+
+    for(auto& taskPair : completedTasksQueue)
     {
-      mAnimationTasks.push_back(task);
+      mWorkingTasks.erase(taskPair.first);
     }
+  }
+}
 
+/// Event thread called
+void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
+{
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+
+  // Rasterize as soon as possible
+  if(MoveTasksToAnimation(task, true))
+  {
     mNeedToSleep = false;
     // wake up the animation thread
     mConditionalWait.Notify(lock);
   }
 }
 
+/// Worker thread called
 void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool success, bool keepAnimation)
 {
-  if(!mDestroyThread)
-  {
-    ConditionalWait::ScopedLock lock(mConditionalWait);
-    bool                        needRasterize = false;
-
-    auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
-    if(workingTask != mWorkingTasks.end())
-    {
-      mWorkingTasks.erase(workingTask);
-    }
+  ConditionalWait::ScopedLock lock(mConditionalWait);
 
-    // Check pending task
-    if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
-    {
-      needRasterize = true;
-    }
+  Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
 
-    if(keepAnimation && success)
-    {
-      if(mCompletedTasks.end() == std::find(mCompletedTasks.begin(), mCompletedTasks.end(), task))
-      {
-        mCompletedTasks.push_back(task);
-        needRasterize = true;
-      }
-    }
+  // DevNote : We need to add task queue, and notify even if mDestroyThread is true.
+  // Since we should make ensure that all working tasks are completed before destroying the thread.
+  mCompletedTasksQueue.emplace_back(task, success && keepAnimation);
 
-    if(needRasterize)
-    {
-      mNeedToSleep = false;
-      // wake up the animation thread
-      mConditionalWait.Notify(lock);
-    }
-  }
+  // wake up the animation thread.
+  // Note that we should not make mNeedToSleep as false now.
+  mConditionalWait.Notify(lock);
 }
 
+/// VectorAnimationThread::SleepThread called, Mutex SleepThread::mAwakeCallbackMutex is locked
 void VectorAnimationThread::OnAwakeFromSleep()
 {
-  if(!mDestroyThread)
+  if(DALI_LIKELY(!mDestroyThread))
   {
+    ConditionalWait::ScopedLock lock(mConditionalWait);
+
     mNeedToSleep = false;
     // wake up the animation thread
-    mConditionalWait.Notify();
+    mConditionalWait.Notify(lock);
   }
 }
 
+/// Worker thread called
 void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint32_t argument)
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
-  if(!mDestroyThread)
+  if(DALI_LIKELY(!mDestroyThread))
   {
     mTriggerEventCallbacks.emplace_back(callback, argument);
 
@@ -176,20 +179,22 @@ void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback, uint
   }
 }
 
+/// Event thread called
 void VectorAnimationThread::RemoveEventTriggerCallbacks(CallbackBase* callback)
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
-  if(!mDestroyThread)
+  if(DALI_LIKELY(!mDestroyThread))
   {
     auto iter = std::remove_if(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), [&callback](std::pair<CallbackBase*, uint32_t>& item) { return item.first == callback; });
     mTriggerEventCallbacks.erase(iter, mTriggerEventCallbacks.end());
   }
 }
 
+/// Worker thread called
 void VectorAnimationThread::RequestForceRenderOnce()
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
-  if(!mDestroyThread)
+  if(DALI_LIKELY(!mDestroyThread))
   {
     mForceRenderOnce = true;
 
@@ -201,44 +206,62 @@ void VectorAnimationThread::RequestForceRenderOnce()
   }
 }
 
+/// Event thread called
+void VectorAnimationThread::Finalize()
+{
+  Mutex::ScopedLock eventTriggerLock(mEventTriggerMutex);
+  Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+  Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
+  // Wait until some event thread trigger, and tasks relative job finished.
+  if(DALI_LIKELY(!mDestroyThread))
+  {
+    DALI_LOG_DEBUG_INFO("Mark VectorAnimationThread destroyed\n");
+    mDestroyThread = true;
+  }
+  mNeedToSleep = false;
+}
+
+/// VectorAnimationThread called
 void VectorAnimationThread::Run()
 {
   SetThreadName("VectorAnimationThread");
   mLogFactory.InstallLogFunction();
   mTraceFactory.InstallTraceFunction();
 
-  while(!mDestroyThread)
+  while(DALI_LIKELY(!mDestroyThread))
   {
     Rasterize();
   }
 }
 
-void VectorAnimationThread::Rasterize()
+/// Event & VectorAnimationThread called
+bool VectorAnimationThread::MoveTasksToAnimation(VectorAnimationTaskPtr task, bool useCurrentTime)
 {
-  // Lock while popping task out from the queue
-  ConditionalWait::ScopedLock lock(mConditionalWait);
+  Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
 
-  // conditional wait
-  if(mNeedToSleep)
+  if(DALI_LIKELY(!mDestroyThread))
   {
-    mConditionalWait.Wait(lock);
-  }
-
-  mNeedToSleep = true;
-
-  // Process completed tasks
-  for(auto&& task : mCompletedTasks)
-  {
-    if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
+    DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "VECTOR_ANIMATION_THREAD_ANIMATION_TASK", [&](std::ostringstream& oss) {
+      oss << "[" << mAnimationTasks.size() << "," << useCurrentTime << "]";
+    });
+
+    // Find if the task is already in the list except loading task
+    auto iter = std::find_if(mAnimationTasks.begin(), mAnimationTasks.end(), [task, useCurrentTime](VectorAnimationTaskPtr& element) {
+      return (element == task) &&
+             (!useCurrentTime ||            // If we don't need to use current time (i.e. CompletedTasks)
+              !element->IsLoadRequested()); // Or we need to use current time And loading completed.
+    });
+
+    if(iter == mAnimationTasks.end())
     {
-      // Should use the frame rate of the animation file
-      auto nextFrameTime = task->CalculateNextFrameTime(false);
+      // Use the frame rate of the animation file, or use current time.
+      auto nextFrameTime = task->CalculateNextFrameTime(useCurrentTime);
 
       bool inserted = false;
       for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
       {
-        auto time = (*iter)->GetNextFrameTime();
-        if(time > nextFrameTime)
+        auto nextTime = (*iter)->GetNextFrameTime();
+        if(nextTime > nextFrameTime)
         {
           mAnimationTasks.insert(iter, task);
           inserted = true;
@@ -250,47 +273,162 @@ void VectorAnimationThread::Rasterize()
       {
         mAnimationTasks.push_back(task);
       }
+
+      DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "VECTOR_ANIMATION_THREAD_ANIMATION_TASK", [&](std::ostringstream& oss) {
+        oss << "[" << mAnimationTasks.size() << "]";
+      });
+
+      return true;
     }
+    DALI_TRACE_END(gTraceFilter, "VECTOR_ANIMATION_THREAD_ANIMATION_TASK");
   }
-  mCompletedTasks.clear();
+  return false;
+}
 
-  // pop out the next task from the queue
-  for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
+/// VectorAnimationThread called
+void VectorAnimationThread::MoveTasksToCompleted(CompletedTasksContainer&& completedTasksQueue)
+{
+  // DevNote : We need to consume task queue, and notify even if mDestroyThread is true.
+  // Since we should make ensure that all working tasks are completed before destroying the thread.
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "VECTOR_ANIMATION_THREAD_COMPLETED_TASK", [&](std::ostringstream& oss) {
+    oss << "[w:" << mWorkingTasks.size() << ",c:" << mCompletedTasks.size() << ",i:" << completedTasksQueue.size() << "]";
+  });
+  bool needRasterize = false;
+
+  for(auto&& taskPair : completedTasksQueue)
   {
-    VectorAnimationTaskPtr nextTask = *it;
-
-    auto currentTime   = std::chrono::steady_clock::now();
-    auto nextFrameTime = nextTask->GetNextFrameTime();
+    auto& task          = taskPair.first;
+    bool  keepAnimation = taskPair.second;
 
-#if defined(DEBUG_ENABLED)
-//    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
+    VectorAnimationTaskSet::const_iterator workingIter = mWorkingTasks.find(task);
+    if(workingIter != mWorkingTasks.cend())
+    {
+      mWorkingTasks.erase(workingIter);
+    }
 
-//    DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
-#endif
-    if(nextFrameTime <= currentTime)
+    if(DALI_LIKELY(!mDestroyThread))
     {
-      // If the task is not in the working list
-      if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
+      // Check pending task
+      if(!needRasterize)
       {
-        it = mAnimationTasks.erase(it);
+        Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
+        if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
+        {
+          needRasterize = true;
+        }
+      }
 
-        // Add it to the working list
-        mWorkingTasks.push_back(nextTask);
-        mAsyncTaskManager.AddTask(nextTask);
+      if(keepAnimation)
+      {
+        VectorAnimationTaskSet::const_iterator completedIter = mCompletedTasks.lower_bound(task);
+        if(completedIter == mCompletedTasks.cend() || task < *completedIter)
+        {
+          mCompletedTasks.insert(completedIter, task);
+          needRasterize = true;
+        }
       }
-      else
+    }
+  }
+
+  if(needRasterize)
+  {
+    mNeedToSleep = false;
+  }
+
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "VECTOR_ANIMATION_THREAD_COMPLETED_TASK", [&](std::ostringstream& oss) {
+    oss << "[w:" << mWorkingTasks.size() << ",c:" << mCompletedTasks.size() << ",r?" << needRasterize << ",s?" << mNeedToSleep << "]";
+  });
+}
+
+/// VectorAnimationThread called
+void VectorAnimationThread::Rasterize()
+{
+  // Lock while popping task out from the queue
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+
+  // conditional wait
+  while(DALI_LIKELY(!mDestroyThread) && mNeedToSleep)
+  {
+    // ConditionalWait notifyed when sleep done, or task complete.
+    // If task complete, then we need to re-validate the tasks container, and then sleep again.
+    decltype(mCompletedTasksQueue) completedTasksQueue;
+    {
+      Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+      completedTasksQueue.swap(mCompletedTasksQueue);
+    }
+    if(completedTasksQueue.empty())
+    {
+      mConditionalWait.Wait(lock);
+      // Task completed may have been added to the queue while we were waiting.
       {
-        it++;
+        Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+        completedTasksQueue.swap(mCompletedTasksQueue);
       }
     }
-    else
+
+    MoveTasksToCompleted(std::move(completedTasksQueue));
+  }
+
+  mNeedToSleep = true;
+
+  // Process completed tasks
+  for(auto&& task : mCompletedTasks)
+  {
+    // Should use the frame rate of the animation file
+    MoveTasksToAnimation(task, false);
+  }
+  mCompletedTasks.clear();
+
+  {
+    Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
+    if(DALI_LIKELY(!mDestroyThread))
     {
-      mSleepThread.SleepUntil(nextFrameTime);
-      break;
+      if(mAnimationTasks.size() > 0u)
+      {
+        DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "VECTOR_ANIMATION_THREAD_ANIMATION_TASK2", [&](std::ostringstream& oss) {
+          oss << "[" << mAnimationTasks.size() << "]";
+        });
+
+        // pop out the next task from the queue
+        for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
+        {
+          VectorAnimationTaskPtr nextTask = *it;
+
+          auto currentTime   = std::chrono::steady_clock::now();
+          auto nextFrameTime = nextTask->GetNextFrameTime();
+
+          if(nextFrameTime <= currentTime)
+          {
+            // If the task is not in the working list
+            VectorAnimationTaskSet::const_iterator workingIter = mWorkingTasks.lower_bound(nextTask);
+            if(workingIter == mWorkingTasks.cend() || nextTask < *workingIter)
+            {
+              it = mAnimationTasks.erase(it);
+
+              // Add it to the working list
+              mWorkingTasks.insert(workingIter, nextTask);
+              mAsyncTaskManager.AddTask(nextTask);
+            }
+            else
+            {
+              it++;
+            }
+          }
+          else
+          {
+            mSleepThread.SleepUntil(nextFrameTime);
+            break;
+          }
+        }
+        DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "VECTOR_ANIMATION_THREAD_ANIMATION_TASK2", [&](std::ostringstream& oss) {
+          oss << "[a:" << mAnimationTasks.size() << ",w:" << mWorkingTasks.size() << "]";
+        });
+      }
     }
   }
 }
 
+/// Event thread called (Due to mTrigger triggered)
 void VectorAnimationThread::OnEventCallbackTriggered()
 {
   while(true)
@@ -316,6 +454,7 @@ void VectorAnimationThread::OnEventCallbackTriggered()
   }
 }
 
+/// Event thread called
 std::pair<CallbackBase*, uint32_t> VectorAnimationThread::GetNextEventCallback()
 {
   Mutex::ScopedLock lock(mEventTriggerMutex);
@@ -346,22 +485,46 @@ VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
 
 VectorAnimationThread::SleepThread::~SleepThread()
 {
+  DALI_TRACE_SCOPE(gTraceFilter, "VECTOR_ANIMATION_SLEEP_THREAD_DESTROY");
+
   // Stop the thread
   {
     ConditionalWait::ScopedLock lock(mConditionalWait);
-    mDestroyThread = true;
+    Finalize();
+
     mConditionalWait.Notify(lock);
   }
 
+  DALI_LOG_DEBUG_INFO("VectorAnimationThread::SleepThread Join request\n");
+
   Join();
 }
 
+/// VectorAnimationThread called
 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
 {
   ConditionalWait::ScopedLock lock(mConditionalWait);
-  mSleepTimePoint = timeToSleepUntil;
-  mNeedToSleep    = true;
-  mConditionalWait.Notify(lock);
+
+  Mutex::ScopedLock sleepLock(mSleepRequestMutex);
+
+  if(DALI_LIKELY(!mDestroyThread))
+  {
+    mSleepTimePoint = timeToSleepUntil;
+    mNeedToSleep    = true;
+    mConditionalWait.Notify(lock);
+  }
+}
+
+void VectorAnimationThread::SleepThread::Finalize()
+{
+  Mutex::ScopedLock awakeLock(mAwakeCallbackMutex);
+  Mutex::ScopedLock sleepLock(mSleepRequestMutex);
+  if(DALI_LIKELY(!mDestroyThread))
+  {
+    DALI_LOG_DEBUG_INFO("Mark VectorAnimationThread::SleepThread destroyed\n");
+    mDestroyThread = true;
+  }
+  mAwakeCallback.reset();
 }
 
 void VectorAnimationThread::SleepThread::Run()
@@ -372,38 +535,43 @@ void VectorAnimationThread::SleepThread::Run()
 
   while(!mDestroyThread)
   {
-    bool                                               needToSleep;
+    bool needToSleep = false;
+
     std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
 
     {
       ConditionalWait::ScopedLock lock(mConditionalWait);
+      Mutex::ScopedLock           sleepLock(mSleepRequestMutex);
 
-      needToSleep    = mNeedToSleep;
-      sleepTimePoint = mSleepTimePoint;
+      if(DALI_LIKELY(!mDestroyThread))
+      {
+        needToSleep    = mNeedToSleep;
+        sleepTimePoint = mSleepTimePoint;
 
-      mNeedToSleep = false;
+        mNeedToSleep = false;
+      }
     }
 
     if(needToSleep)
     {
-#if defined(DEBUG_ENABLED)
-//      auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
-
-//      DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
-#endif
+      DALI_TRACE_SCOPE(gTraceFilter, "VECTOR_ANIMATION_SLEEP_THREAD");
 
       std::this_thread::sleep_until(sleepTimePoint);
 
-      if(mAwakeCallback)
       {
-        CallbackBase::Execute(*mAwakeCallback);
+        Mutex::ScopedLock awakeLock(mAwakeCallbackMutex);
+        if(DALI_LIKELY(mAwakeCallback))
+        {
+          CallbackBase::Execute(*mAwakeCallback);
+        }
       }
     }
 
     {
       ConditionalWait::ScopedLock lock(mConditionalWait);
-      if(!mDestroyThread && !mNeedToSleep)
+      if(DALI_LIKELY(!mDestroyThread) && !mNeedToSleep)
       {
+        DALI_TRACE_SCOPE(gTraceFilter, "VECTOR_ANIMATION_SLEEP_THREAD_WAIT");
         mConditionalWait.Wait(lock);
       }
     }
index 8b820cc..61ff71b 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/common/set-wrapper.h>
 #include <dali/devel-api/threading/conditional-wait.h>
 #include <dali/devel-api/threading/mutex.h>
 #include <dali/devel-api/threading/thread.h>
@@ -95,6 +96,11 @@ public:
    */
   void RequestForceRenderOnce();
 
+  /**
+   * @brief Finalize the thread.
+   */
+  void Finalize();
+
 protected:
   /**
    * @brief The entry function of the animation thread.
@@ -103,6 +109,20 @@ protected:
 
 private:
   /**
+   * @brief Move given tasks to mAnimationTasks if required.
+   * @return True if task added. False if not.
+   */
+  bool MoveTasksToAnimation(VectorAnimationTaskPtr task, bool useCurrentTime);
+
+  using CompletedTasksContainer = std::vector<std::pair<VectorAnimationTaskPtr, bool>>; ///< Pair of completed task, and rasterize required.
+
+  /**
+   * @brief Move given tasks to mCompletedTasks if required.
+   */
+  void MoveTasksToCompleted(CompletedTasksContainer&& completedTasksQueue);
+
+private:
+  /**
    * @brief Rasterizes the tasks.
    */
   void Rasterize();
@@ -138,6 +158,11 @@ private:
      */
     void SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil);
 
+    /**
+     * @brief Finalizes the sleep thread. This will make ensure that we don't touch VectorAnimationThread.
+     */
+    void Finalize();
+
   protected:
     /**
      * @brief The entry function of the animation thread.
@@ -149,7 +174,10 @@ private:
     SleepThread& operator=(const SleepThread& thread) = delete;
 
   private:
-    ConditionalWait                                    mConditionalWait;
+    ConditionalWait mConditionalWait;
+    Mutex           mAwakeCallbackMutex; ///< Mutex to check validatoin of mAwakeCallback
+    Mutex           mSleepRequestMutex;  ///< Mutex to change sleep time point.
+
     std::unique_ptr<CallbackBase>                      mAwakeCallback;
     std::chrono::time_point<std::chrono::steady_clock> mSleepTimePoint;
     const Dali::LogFactoryInterface&                   mLogFactory;
@@ -167,13 +195,21 @@ private:
   VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete;
 
 private:
-  std::vector<VectorAnimationTaskPtr>             mAnimationTasks;
-  std::vector<VectorAnimationTaskPtr>             mCompletedTasks;
-  std::vector<VectorAnimationTaskPtr>             mWorkingTasks;
+  std::vector<VectorAnimationTaskPtr> mAnimationTasks; ///< Animation processing tasks, ordered by next frame time.
+
+  using VectorAnimationTaskSet = std::set<VectorAnimationTaskPtr>;
+  VectorAnimationTaskSet mCompletedTasks; ///< Temperal storage for completed tasks. Thread warning : This should be touched only at VectorAnimationThread.
+  VectorAnimationTaskSet mWorkingTasks;   ///< Tasks which are currently being processed. Thread warning : This should be touched only at VectorAnimationThread.
+
+  std::vector<std::pair<VectorAnimationTaskPtr, bool>> mCompletedTasksQueue; ///< Queue of completed tasks from worker thread. pair of task, and rasterize required.
+                                                                             ///< It will be moved at begin of Rasterize().
+
   std::vector<std::pair<CallbackBase*, uint32_t>> mTriggerEventCallbacks{}; // Callbacks are not owned
   SleepThread                                     mSleepThread;
   ConditionalWait                                 mConditionalWait;
   Mutex                                           mEventTriggerMutex;
+  Mutex                                           mAnimationTasksMutex; ///< Mutex to change + get mAnimationTasks from event thread
+  Mutex                                           mTaskCompletedMutex;  ///< Mutex to collect completed tasks to mCompletedTasksQueue from worker threads
   std::unique_ptr<EventThreadCallback>            mEventTrigger{};
   const Dali::LogFactoryInterface&                mLogFactory;
   const Dali::TraceFactoryInterface&              mTraceFactory;
index 8c9f0fe..b830276 100644 (file)
@@ -56,7 +56,7 @@ ArcVisualPtr ArcVisual::New(VisualFactoryCache& factoryCache, const Property::Ma
 }
 
 ArcVisual::ArcVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, static_cast<Toolkit::Visual::Type>(Toolkit::DevelVisual::ARC)),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, static_cast<Toolkit::Visual::Type>(Toolkit::DevelVisual::ARC)),
   mThickness(0.0f),
   mRadius(0.0f),
   mStartAngle(0.0f),
index 322017f..ea6ea67 100644 (file)
@@ -55,7 +55,7 @@ BorderVisualPtr BorderVisual::New(VisualFactoryCache& factoryCache, const Proper
 }
 
 BorderVisual::BorderVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::BORDER),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::BORDER),
   mBorderColor(Color::TRANSPARENT),
   mBorderSize(0.f),
   mBorderColorIndex(Property::INVALID_INDEX),
diff --git a/dali-toolkit/internal/visuals/color/color-visual-shader-factory.cpp b/dali-toolkit/internal/visuals/color/color-visual-shader-factory.cpp
new file mode 100644 (file)
index 0000000..c4ca4e9
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/color/color-visual-shader-factory.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+namespace
+{
+
+constexpr VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[] = {
+  VisualFactoryCache::COLOR_SHADER,
+  VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER,
+  VisualFactoryCache::COLOR_SHADER_BORDERLINE,
+  VisualFactoryCache::COLOR_SHADER_ROUNDED_BORDERLINE,
+  VisualFactoryCache::COLOR_SHADER_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE,
+};
+
+constexpr VisualFactoryCache::ShaderType SHADER_TYPE_WITH_CUTOUT_TABLE[] = {
+  VisualFactoryCache::COLOR_SHADER_CUTOUT,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_ROUNDED_CORNER,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_BORDERLINE,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE,
+};
+
+// enum of required list when we select shader
+enum ColorVisualRequireFlag
+{
+  DEFAULT        = 0,
+  ROUNDED_CORNER = 1 << 0,
+  BORDERLINE     = 1 << 1,
+  BLUR           = 1 << 2,
+};
+
+constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_ROUNDED_BLUR = 300;
+
+static constexpr auto PREDEFINED_SHADER_TYPE_COUNT = 2u;
+
+constexpr std::string_view VertexPredefines[PREDEFINED_SHADER_TYPE_COUNT]{
+  "",                                     //VisualFactoryCache::COLOR_SHADER
+  "#define IS_REQUIRED_ROUNDED_CORNER\n", //VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER
+};
+constexpr std::string_view FragmentPredefines[PREDEFINED_SHADER_TYPE_COUNT]{
+  "",                                     //VisualFactoryCache::COLOR_SHADER
+  "#define IS_REQUIRED_ROUNDED_CORNER\n", //VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER
+};
+constexpr VisualFactoryCache::ShaderType ShaderTypePredefines[PREDEFINED_SHADER_TYPE_COUNT]{
+  VisualFactoryCache::ShaderType::COLOR_SHADER,
+  VisualFactoryCache::ShaderType::COLOR_SHADER_ROUNDED_CORNER,
+};
+}
+
+namespace ColorVisualShaderFeature
+{
+
+FeatureBuilder::FeatureBuilder()
+: mColorRoundCorner(RoundedCorner::DISABLED),
+  mColorBorderline(Borderline::DISABLED),
+  mColorBlur(Blur::DISABLED),
+  mColorCutout(Cutout::DISABLED)
+{
+}
+
+FeatureBuilder& FeatureBuilder::EnableRoundCorner(bool enableRoundedCorner)
+{
+  mColorRoundCorner = (enableRoundedCorner ? RoundedCorner::ENABLED : RoundedCorner::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableBorderLine(bool enableBorderLine)
+{
+  mColorBorderline = (enableBorderLine ? Borderline::ENABLED : Borderline::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableBlur(bool enableBlur)
+{
+  mColorBlur = (enableBlur ? Blur::ENABLED : Blur::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableCutout(bool enableCutout)
+{
+  mColorCutout = (enableCutout ? Cutout::ENABLED : Cutout::DISABLED);
+  return *this;
+}
+
+VisualFactoryCache::ShaderType FeatureBuilder::GetShaderType() const
+{
+  VisualFactoryCache::ShaderType shaderType = VisualFactoryCache::COLOR_SHADER;
+  uint32_t  shaderTypeFlag = ColorVisualRequireFlag::DEFAULT;
+  if(mColorBlur)
+  {
+    shaderTypeFlag |= ColorVisualRequireFlag::BLUR;
+  }
+  if(mColorRoundCorner)
+  {
+    shaderTypeFlag |= ColorVisualRequireFlag::ROUNDED_CORNER;
+  }
+  if(mColorBorderline && !mColorBlur)
+  {
+    shaderTypeFlag |= ColorVisualRequireFlag::BORDERLINE;
+  }
+
+  shaderType = mColorCutout ? SHADER_TYPE_WITH_CUTOUT_TABLE[shaderTypeFlag] : SHADER_TYPE_TABLE[shaderTypeFlag];
+
+  return shaderType;
+}
+
+void FeatureBuilder::GetVertexShaderPrefixList(std::string& vertexShaderPrefixList) const
+{
+  if(mColorRoundCorner == RoundedCorner::ENABLED)
+  {
+    vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
+  }
+  if(mColorBlur == Blur::ENABLED)
+  {
+    vertexShaderPrefixList += "#define IS_REQUIRED_BLUR\n";
+  }
+  if(mColorBorderline == Borderline::ENABLED && mColorBlur == RoundedCorner::DISABLED)
+  {
+    vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
+  }
+  if(mColorCutout == Cutout::ENABLED)
+  {
+    vertexShaderPrefixList += "#define IS_REQUIRED_CUTOUT\n";
+  }
+}
+
+void FeatureBuilder::GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const
+{
+  if(mColorRoundCorner == RoundedCorner::ENABLED)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
+  }
+  if(mColorBlur == Blur::ENABLED)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_BLUR\n";
+    // If shader version doesn't support latest blur with corner radius, Let we use legacy code.
+    if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_ROUNDED_BLUR))
+    {
+      fragmentShaderPrefixList += "#define SL_VERSION_LOW\n";
+    }
+  }
+  if(mColorBorderline == Borderline::ENABLED && mColorBlur == RoundedCorner::DISABLED)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
+  }
+  if(mColorCutout == Cutout::ENABLED)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_CUTOUT\n";
+  }
+}
+
+} // namespace ColorVisualShaderFeature
+
+ColorVisualShaderFactory::ColorVisualShaderFactory()
+{
+}
+
+ColorVisualShaderFactory::~ColorVisualShaderFactory()
+{
+}
+
+Shader ColorVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, const ColorVisualShaderFeature::FeatureBuilder& featureBuilder)
+{
+  Shader                         shader;
+  VisualFactoryCache::ShaderType  shaderType = featureBuilder.GetShaderType();
+  shader     = factoryCache.GetShader(shaderType);
+
+  if(!shader)
+  {
+    std::string vertexShaderPrefixList;
+    std::string fragmentShaderPrefixList;
+    featureBuilder.GetVertexShaderPrefixList(vertexShaderPrefixList);
+    featureBuilder.GetFragmentShaderPrefixList(fragmentShaderPrefixList);
+
+    std::string vertexShader   = std::string(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_COLOR_VISUAL_SHADER_VERT.data());
+    std::string fragmentShader = std::string(Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_COLOR_VISUAL_SHADER_FRAG.data());
+
+    shader = factoryCache.GenerateAndSaveShader(shaderType, vertexShader, fragmentShader);
+  }
+  return shader;
+
+}
+
+bool ColorVisualShaderFactory::AddPrecompiledShader(PrecompileShaderOption& option)
+{
+  ShaderFlagList shaderOption = option.GetShaderOptions();
+
+  auto featureBuilder = ColorVisualShaderFeature::FeatureBuilder();
+  std::string vertexPrefixList;
+  std::string fragmentPrefixList;
+  CreatePrecompileShader(featureBuilder, shaderOption);
+
+  VisualFactoryCache::ShaderType type = featureBuilder.GetShaderType();
+  featureBuilder.GetVertexShaderPrefixList(vertexPrefixList);
+  featureBuilder.GetFragmentShaderPrefixList(fragmentPrefixList);
+  return SavePrecompileShader(type, vertexPrefixList, fragmentPrefixList );
+}
+
+void ColorVisualShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
+{
+  std::vector<std::string_view> vertexPrefix;
+  std::vector<std::string_view> fragmentPrefix;
+  std::vector<std::string_view> shaderName;
+  int                           shaderCount = 0;
+  shaders.shaderCount                       = 0;
+
+  // precompile requested shader first
+  for(uint32_t i = 0u; i < mRequestedPrecompileShader.size(); i++ )
+  {
+    vertexPrefix.push_back(mRequestedPrecompileShader[i].vertexPrefix);
+    fragmentPrefix.push_back(mRequestedPrecompileShader[i].fragmentPrefix);
+    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+    shaderCount++;
+  }
+
+  for(uint32_t i = 0u; i < PREDEFINED_SHADER_TYPE_COUNT; ++i)
+  {
+    vertexPrefix.push_back(VertexPredefines[i]);
+    fragmentPrefix.push_back(FragmentPredefines[i]);
+    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+    shaderCount++;
+  }
+
+  shaders.vertexPrefix   = std::move(vertexPrefix);
+  shaders.fragmentPrefix = std::move(fragmentPrefix);
+  shaders.shaderName     = std::move(shaderName);
+  shaders.vertexShader   = SHADER_COLOR_VISUAL_SHADER_VERT;
+  shaders.fragmentShader = SHADER_COLOR_VISUAL_SHADER_FRAG;
+  shaders.shaderCount    = shaderCount;
+  shaders.custom = false;
+}
+
+void ColorVisualShaderFactory::CreatePrecompileShader(ColorVisualShaderFeature::FeatureBuilder& builder, const ShaderFlagList& option)
+{
+  for(uint32_t i = 0; i < option.size(); ++i)
+  {
+    if(option[i] == PrecompileShaderOption::Flag::ROUNDED_CORNER)
+    {
+      builder.EnableRoundCorner(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::BORDERLINE)
+    {
+      builder.EnableBorderLine(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::BLUR_EDGE)
+    {
+      builder.EnableBlur(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::CUTOUT)
+    {
+      builder.EnableCutout(true);
+    }
+  }
+}
+
+bool ColorVisualShaderFactory::SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix)
+{
+  for(uint32_t i = 0u; i< PREDEFINED_SHADER_TYPE_COUNT; i++)
+  {
+    if(ShaderTypePredefines[i] == shader)
+    {
+      DALI_LOG_WARNING("This shader already added list(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  for(uint32_t i = 0u; i< mRequestedPrecompileShader.size(); i++)
+  {
+    if(mRequestedPrecompileShader[i].type == shader)
+    {
+      DALI_LOG_WARNING("This shader already requsted(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  RequestShaderInfo info;
+  info.type = shader;
+  info.vertexPrefix = vertexPrefix;
+  info.fragmentPrefix = fragmentPrefix;
+  mRequestedPrecompileShader.push_back(info);
+  DALI_LOG_RELEASE_INFO("Add precompile shader success!!(%s)",Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(shader, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+  return true;
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/color/color-visual-shader-factory.h b/dali-toolkit/internal/visuals/color/color-visual-shader-factory.h
new file mode 100644 (file)
index 0000000..7ef6f56
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef DALI_TOOLKIT_COLOR_VISUAL_SHADER_FACTORY_H
+#define DALI_TOOLKIT_COLOR_VISUAL_SHADER_FACTORY_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali-toolkit/internal/visuals/visual-shader-factory-interface.h>
+#include <string_view>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+namespace ColorVisualShaderFeature
+{
+namespace RoundedCorner
+{
+/**
+ * @brief Whether use RoundedCorner, or not
+ */
+enum Type
+{
+  DISABLED = 0, ///< Color visual doesn't use rounded corner
+  ENABLED       ///< Color visual uses rounded corner
+};
+} // namespace RoundedCorner
+
+namespace Blur
+{
+/**
+ * @brief Whether use Blur, or not
+ */
+enum Type
+{
+  DISABLED = 0, ///< Color visual doesn't use blur
+  ENABLED       ///< Color visual uses blur
+};
+} // namespace Blur
+
+namespace Borderline
+{
+/**
+ * @brief Whether use Borderline
+ */
+enum Type
+{
+  DISABLED = 0, /// Color visual doesn't use Borderline
+  ENABLED       /// Color visual uses Borderline
+};
+} // namespace Borderline
+
+namespace Cutout
+{
+/**
+ * @brief Whether use Cutout, or not
+ */
+enum Type
+{
+  DISABLED = 0, ///< Color visual doesn't use Cutout
+  ENABLED       ///< Color visual uses Cutout
+};
+} // namespace Cutout
+
+class FeatureBuilder
+{
+public:
+  FeatureBuilder();
+  FeatureBuilder& EnableRoundCorner(bool enableRoundCorner);
+  FeatureBuilder& EnableBorderLine(bool enableBorderLine);
+  FeatureBuilder& EnableBlur(bool enableBlur);
+  FeatureBuilder& EnableCutout(bool enableCutout);
+
+  VisualFactoryCache::ShaderType GetShaderType() const;
+  void GetVertexShaderPrefixList(std::string& vertexShaderPrefixList) const;
+  void GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const;
+
+  bool IsEnabledRoundCorner() const
+  {
+    return mColorRoundCorner == ColorVisualShaderFeature::RoundedCorner::ENABLED;
+  }
+  bool IsEnabledBorderLine() const
+  {
+    return mColorBorderline == ColorVisualShaderFeature::Borderline::ENABLED;
+  }
+  bool IsEnabledBlur() const
+  {
+    return mColorBlur == ColorVisualShaderFeature::Blur::ENABLED;
+  }
+  bool IsEnabledCutout() const
+  {
+    return mColorCutout == ColorVisualShaderFeature::Cutout::ENABLED;
+  }
+
+private:
+  RoundedCorner::Type mColorRoundCorner : 2; ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED
+  Borderline::Type    mColorBorderline : 2;  ///< Whether use border line, or not. default as Borderline::DISABLED
+  Blur::Type          mColorBlur : 2;        ///< Whether use blur, or not. default as Blur::DISABLED
+  Cutout::Type        mColorCutout : 2;      ///< Whether use cutout, or not. default as Cutout::DISABLED
+};
+} // namespace ColorVisualShaderFeature
+
+/**
+ * ColorVisualShaderFactory is an object that provides and shares shaders between color visuals
+ */
+class ColorVisualShaderFactory : public VisualShaderFactoryInterface
+{
+public:
+  /**
+   * @brief Constructor
+   */
+  ColorVisualShaderFactory();
+
+  /**
+   * @brief Destructor
+   */
+  ~ColorVisualShaderFactory() override;
+
+public:
+  /**
+   * @brief Get the standard color rendering shader.
+   * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+   * @param[in] featureBuilder Collection of current text shader's features
+   * @return The standard text rendering shader with features.
+  */
+  Shader GetShader(VisualFactoryCache& factoryCache, const ColorVisualShaderFeature::FeatureBuilder& featureBuilder);
+
+public: // Implementation of VisualShaderFactoryInterface
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::AddPrecompiledShader
+   */
+  bool AddPrecompiledShader(PrecompileShaderOption& option) override;
+
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::GetPreCompiledShader
+   */
+  void GetPreCompiledShader(RawShaderData& shaders) override;
+
+private:
+  /**
+   * @brief Create pre-compiled shader for image with builder and option.
+   */
+  void CreatePrecompileShader(ColorVisualShaderFeature::FeatureBuilder& builder, const ShaderFlagList& option);
+  /**
+   * @brief Check if cached hash value is valid or not.
+   */
+  bool SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix);
+
+protected:
+  /**
+   * Undefined copy constructor.
+   */
+  ColorVisualShaderFactory(const ColorVisualShaderFactory&) = delete;
+
+  /**
+   * Undefined assignment operator.
+   */
+  ColorVisualShaderFactory& operator=(const ColorVisualShaderFactory& rhs) = delete;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_COLOR_VISUAL_SHADER_FACTORY_H
index cb20865..5f3365c 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "color-visual.h"
+#include <dali-toolkit/internal/visuals/color/color-visual.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/rendering/renderer-devel.h>
@@ -24,7 +24,6 @@
 #include <dali/public-api/rendering/decorated-visual-renderer.h>
 
 //INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
@@ -42,41 +41,32 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(0);
+const int CUSTOM_PROPERTY_COUNT(0); ///< Note : cutout policy property will be registered only of the cutout view is used.
+                                    ///<        We don't need to reserve that property memory always.
 
-VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[6] =
-  {
-    VisualFactoryCache::COLOR_SHADER,
-    VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER,
-    VisualFactoryCache::COLOR_SHADER_BORDERLINE,
-    VisualFactoryCache::COLOR_SHADER_ROUNDED_BORDERLINE,
-    VisualFactoryCache::COLOR_SHADER_BLUR_EDGE,
-    VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE,
-};
-
-// enum of required list when we select shader
-enum ColorVisualRequireFlag
-{
-  DEFAULT        = 0,
-  ROUNDED_CORNER = 1 << 0,
-  BORDERLINE     = 1 << 1,
-  BLUR           = 1 << 2,
-};
+// cutout policies
+DALI_ENUM_TO_STRING_TABLE_BEGIN(CUTOUT_POLICY)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelColorVisual::CutoutPolicy, NONE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelColorVisual::CutoutPolicy, CUTOUT_VIEW)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelColorVisual::CutoutPolicy, CUTOUT_VIEW_WITH_CORNER_RADIUS)
+DALI_ENUM_TO_STRING_TABLE_END(CUTOUT_POLICY)
 
-constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_ROUNDED_BLUR = 300;
 } // unnamed namespace
-ColorVisualPtr ColorVisual::New(VisualFactoryCache& factoryCache, const Property::Map& properties)
+
+ColorVisualPtr ColorVisual::New(VisualFactoryCache& factoryCache, ColorVisualShaderFactory& shaderFactory, const Property::Map& properties)
 {
-  ColorVisualPtr colorVisualPtr(new ColorVisual(factoryCache));
+  ColorVisualPtr colorVisualPtr(new ColorVisual(factoryCache, shaderFactory));
   colorVisualPtr->SetProperties(properties);
   colorVisualPtr->Initialize();
   return colorVisualPtr;
 }
 
-ColorVisual::ColorVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::COLOR),
+ColorVisual::ColorVisual(VisualFactoryCache& factoryCache, ColorVisualShaderFactory& shaderFactory)
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::COLOR),
   mBlurRadius(0.0f),
-  mAlwaysUsingBlurRadius(false)
+  mCutoutPolicy(DevelColorVisual::CutoutPolicy::NONE),
+  mAlwaysUsingBlurRadius(false),
+  mColorVisualShaderFactory(shaderFactory)
 {
 }
 
@@ -141,6 +131,22 @@ void ColorVisual::DoSetProperties(const Property::Map& propertyMap)
       }
     }
   }
+
+  Property::Value* cutoutPolicyValue = propertyMap.Find(Toolkit::DevelColorVisual::Property::CUTOUT_POLICY, CUTOUT_POLICY_NAME);
+  if(cutoutPolicyValue)
+  {
+    int cutoutPolicy = static_cast<int>(DevelColorVisual::CutoutPolicy::NONE) - 1; ///< Make always invalid
+    if(DALI_UNLIKELY(!Scripting::GetEnumerationProperty(*cutoutPolicyValue, CUTOUT_POLICY_TABLE, CUTOUT_POLICY_TABLE_COUNT, cutoutPolicy)))
+    {
+      std::ostringstream oss;
+      oss << *cutoutPolicyValue;
+      DALI_LOG_ERROR("ColorVisual:DoSetProperties:: CUTOUT_POLICY property has incorrect type : %d, value : %s\n", cutoutPolicyValue->GetType(), oss.str().c_str());
+    }
+    else
+    {
+      mCutoutPolicy = static_cast<DevelColorVisual::CutoutPolicy::Type>(cutoutPolicy);
+    }
+  }
 }
 
 void ColorVisual::DoSetOnScene(Actor& actor)
@@ -161,6 +167,7 @@ void ColorVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Clear();
   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::COLOR);
   map.Insert(Toolkit::ColorVisual::Property::MIX_COLOR, mImpl->mMixColor);
+  map.Insert(Toolkit::DevelColorVisual::Property::CUTOUT_POLICY, mCutoutPolicy);
 
   if(mImpl->mRenderer)
   {
@@ -223,67 +230,25 @@ void ColorVisual::OnInitialize()
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
   }
 
+  if(IsCutoutRequired())
+  {
+    int cutoutWithCornerRadius = (mCutoutPolicy == DevelColorVisual::CutoutPolicy::CUTOUT_VIEW_WITH_CORNER_RADIUS) ? 1 : 0;
+    mImpl->mRenderer.RegisterUniqueProperty("uCutoutWithCornerRadius", cutoutWithCornerRadius);
+  }
+
   // Register transform properties
   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
 }
 
 Shader ColorVisual::GenerateShader() const
 {
-  Shader                         shader;
-  VisualFactoryCache::ShaderType shaderType;
-
-  bool roundedCorner  = IsRoundedCornerRequired();
-  bool borderline     = IsBorderlineRequired();
-  bool blur           = IsBlurRequired();
-  int  shaderTypeFlag = ColorVisualRequireFlag::DEFAULT;
-
-  if(blur)
-  {
-    // If we use blur, just ignore borderline
-    borderline = false;
-    shaderTypeFlag |= ColorVisualRequireFlag::BLUR;
-  }
-  if(roundedCorner)
-  {
-    shaderTypeFlag |= ColorVisualRequireFlag::ROUNDED_CORNER;
-  }
-  if(borderline)
-  {
-    shaderTypeFlag |= ColorVisualRequireFlag::BORDERLINE;
-  }
-
-  shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
-  shader     = mFactoryCache.GetShader(shaderType);
-  if(!shader)
-  {
-    std::string vertexShaderPrefixList;
-    std::string fragmentShaderPrefixList;
-    if(roundedCorner)
-    {
-      vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
-      fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
-    }
-    if(blur)
-    {
-      vertexShaderPrefixList += "#define IS_REQUIRED_BLUR\n";
-      fragmentShaderPrefixList += "#define IS_REQUIRED_BLUR\n";
-
-      // If shader version doesn't support latest blur with corner radius, Let we use legacy code.
-      if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_ROUNDED_BLUR))
-      {
-        fragmentShaderPrefixList += "#define SL_VERSION_LOW\n";
-      }
-    }
-    if(borderline)
-    {
-      vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
-      fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
-    }
-
-    shader = mFactoryCache.GenerateAndSaveShader(shaderType,
-                                                 Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_COLOR_VISUAL_SHADER_VERT.data(),
-                                                 Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_COLOR_VISUAL_SHADER_FRAG.data());
-  }
+  Shader shader = mColorVisualShaderFactory.GetShader(
+    mFactoryCache,
+    ColorVisualShaderFeature::FeatureBuilder()
+    .EnableBlur(IsBlurRequired())
+    .EnableBorderLine(IsBorderlineRequired())
+    .EnableRoundCorner(IsRoundedCornerRequired())
+    .EnableCutout(IsCutoutRequired()));
 
   return shader;
 }
@@ -331,6 +296,11 @@ bool ColorVisual::IsBlurRequired() const
   return mAlwaysUsingBlurRadius || !EqualsZero(blurRadius);
 }
 
+bool ColorVisual::IsCutoutRequired() const
+{
+  return (mCutoutPolicy != DevelColorVisual::CutoutPolicy::NONE);
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 0a8a8b2..44f1180 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_COLOR_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,9 @@
 #include <dali/public-api/common/intrusive-ptr.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
+#include <dali-toolkit/internal/visuals/color/color-visual-shader-factory.h>
 
 namespace Dali
 {
@@ -49,10 +51,11 @@ public:
    * @brief Create a new color visual.
    *
    * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+   * @param[in] shaderFactory A reference to the ColorVisualShaderFactory object
    * @param[in] properties A Property::Map containing settings for this visual
    * @return A smart-pointer to the newly allocated visual.
    */
-  static ColorVisualPtr New(VisualFactoryCache& factoryCache, const Property::Map& properties);
+  static ColorVisualPtr New(VisualFactoryCache& factoryCache, ColorVisualShaderFactory& shaderFactory, const Property::Map& properties);
 
 public: // from Visual
   /**
@@ -75,8 +78,9 @@ protected:
    * @brief Constructor.
    *
    * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+   * @param[in] shaderFactory A reference to the ColorVisualShaderFactory object
    */
-  ColorVisual(VisualFactoryCache& factoryCache);
+  ColorVisual(VisualFactoryCache& factoryCache, ColorVisualShaderFactory& shaderFactory);
 
   /**
    * @brief A reference counted object may only be deleted by calling Unreference().
@@ -131,6 +135,13 @@ protected:
    */
   bool IsBlurRequired() const;
 
+  /**
+   * @brief Query whether the visual requires to cutout feature.
+   *
+   * @return Returns true if the cutout is required, false otherwise.
+   */
+  bool IsCutoutRequired() const;
+
 private:
   // Undefined
   ColorVisual(const ColorVisual& colorRenderer);
@@ -139,8 +150,11 @@ private:
   ColorVisual& operator=(const ColorVisual& colorRenderer);
 
 private:
-  float mBlurRadius;                ///< The blur radius
-  bool  mAlwaysUsingBlurRadius : 1; ///< Whether we need the blur radius in shader always.
+  float mBlurRadius; ///< The blur radius
+
+  DevelColorVisual::CutoutPolicy::Type mCutoutPolicy : 3;          ///< The policy of cutout
+  bool                                 mAlwaysUsingBlurRadius : 1; ///< Whether we need the blur radius in shader always.
+  ColorVisualShaderFactory&            mColorVisualShaderFactory;  ///< The shader factory for color visual.
 };
 
 } // namespace Internal
diff --git a/dali-toolkit/internal/visuals/custom-shader-factory.cpp b/dali-toolkit/internal/visuals/custom-shader-factory.cpp
new file mode 100644 (file)
index 0000000..eb05b7b
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/custom-shader-factory.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+CustomShaderFactory::CustomShaderFactory()
+{
+}
+
+CustomShaderFactory::~CustomShaderFactory()
+{
+}
+
+bool CustomShaderFactory::AddPrecompiledShader(PrecompileShaderOption& option)
+{
+  auto shaderName     = option.GetShaderName();
+  auto vertexShader   = option.GetVertexShader();
+  auto fragmentShader = option.GetFragmentShader();
+  return SavePrecompileShader(shaderName, vertexShader, fragmentShader);
+}
+
+void CustomShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
+{
+  std::vector<std::string_view> vertexPrefix;
+  std::vector<std::string_view> fragmentPrefix;
+  std::vector<std::string_view> shaderName;
+  int                           shaderCount = 0;
+  shaders.shaderCount                       = 0;
+
+  // precompile requested shader first
+  for(uint32_t i = 0; i < mRequestedPrecompileShader.size(); i++)
+  {
+    vertexPrefix.push_back(mRequestedPrecompileShader[i].vertexPrefix);
+    fragmentPrefix.push_back(mRequestedPrecompileShader[i].fragmentPrefix);
+    shaderName.push_back(mRequestedPrecompileShader[i].name);
+    shaderCount++;
+  }
+
+  shaders.vertexPrefix   = std::move(vertexPrefix);
+  shaders.fragmentPrefix = std::move(fragmentPrefix);
+  shaders.shaderName     = std::move(shaderName);
+  shaders.vertexShader   = ""; // Custom shader use prefix shader only. No need to set vertexShader and fragmentShader.
+  shaders.fragmentShader = ""; // Custom shader use prefix shader only. No need to set vertexShader and fragmentShader.
+  shaders.shaderCount    = std::move(shaderCount);
+  shaders.custom         = true;
+}
+
+bool CustomShaderFactory::SavePrecompileShader(std::string& shaderName, std::string& vertexShader, std::string& fragmentShader)
+{
+  RequestShaderInfo info;
+  info.type           = VisualFactoryCache::SHADER_TYPE_MAX; ///< Not be used
+  info.name           = shaderName;
+  info.vertexPrefix   = vertexShader;
+  info.fragmentPrefix = fragmentShader;
+  mRequestedPrecompileShader.push_back(info);
+  DALI_LOG_RELEASE_INFO("Add precompile shader success!!(%s)", shaderName.c_str());
+  return true;
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/custom-shader-factory.h b/dali-toolkit/internal/visuals/custom-shader-factory.h
new file mode 100644 (file)
index 0000000..5401c73
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef DALI_TOOLKIT_CUSTOM_SHADER_FACTORY_H
+#define DALI_TOOLKIT_CUSTOM_SHADER_FACTORY_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali-toolkit/internal/visuals/visual-shader-factory-interface.h>
+#include <string_view>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+/**
+ * CustomShaderFactory is an object that provides custom shader
+ */
+class CustomShaderFactory : public VisualShaderFactoryInterface
+{
+public:
+  /**
+   * @brief Constructor
+   */
+  CustomShaderFactory();
+
+  /**
+   * @brief Destructor
+   */
+  ~CustomShaderFactory() override;
+
+public: // Implementation of VisualShaderFactoryInterface
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::AddPrecompiledShader
+   */
+  bool AddPrecompiledShader(PrecompileShaderOption& option) override;
+
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::GetPreCompiledShader
+   */
+  void GetPreCompiledShader(RawShaderData& shaders) override;
+
+private:
+  /**
+   * @brief Save the custom shader
+   */
+  bool SavePrecompileShader(std::string& shaderName, std::string& vertexPrefix, std::string& fragmentPrefix);
+
+protected:
+  /**
+   * Undefined copy constructor.
+   */
+  CustomShaderFactory(const CustomShaderFactory&) = delete;
+
+  /**
+   * Undefined assignment operator.
+   */
+  CustomShaderFactory& operator=(const CustomShaderFactory& rhs) = delete;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_CUSTOM_SHADER_FACTORY_H
index 066d506..6884fbe 100644 (file)
@@ -129,7 +129,7 @@ GradientVisualPtr GradientVisual::New(VisualFactoryCache& factoryCache, const Pr
 }
 
 GradientVisual::GradientVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::GRADIENT),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::GRADIENT),
   mGradientType(LINEAR),
   mIsOpaque(true)
 {
diff --git a/dali-toolkit/internal/visuals/image-visual-shader-feature-builder.cpp b/dali-toolkit/internal/visuals/image-visual-shader-feature-builder.cpp
deleted file mode 100644 (file)
index 6939cd1..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// CLASS HEADER
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
-
-namespace Dali
-{
-namespace Toolkit
-{
-namespace Internal
-{
-namespace
-{
-// enum of required list when we select shader
-enum class ImageVisualRequireFlag : uint32_t
-{
-  DEFAULT          = 0,
-  ROUNDED_CORNER   = 1 << 0,
-  BORDERLINE       = 1 << 1,
-  ALPHA_MASKING    = 1 << 2,
-  COLOR_CONVERSION = 1 << 3,
-
-  UNIFIED_YUV_AND_RGB = 1 << 2, // Special enum to trick unified YUV and RGB.
-};
-
-static constexpr auto          SHADER_TYPE_COUNT = 16u;
-VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[SHADER_TYPE_COUNT] =
-  {
-    VisualFactoryCache::IMAGE_SHADER,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER,
-    VisualFactoryCache::IMAGE_SHADER_BORDERLINE,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE,
-    VisualFactoryCache::IMAGE_SHADER_MASKING,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_MASKING,
-    VisualFactoryCache::IMAGE_SHADER_BORDERLINE_MASKING,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING,
-    VisualFactoryCache::IMAGE_SHADER_YUV_TO_RGB,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_TO_RGB,
-    VisualFactoryCache::IMAGE_SHADER_BORDERLINE_YUV_TO_RGB,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_TO_RGB,
-    VisualFactoryCache::IMAGE_SHADER_YUV_AND_RGB,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_AND_RGB,
-    VisualFactoryCache::IMAGE_SHADER_BORDERLINE_YUV_AND_RGB,
-    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_AND_RGB};
-} // unnamed namespace
-
-ImageVisualShaderFeatureBuilder::ImageVisualShaderFeatureBuilder()
-: mTextureAtlas(ImageVisualShaderFeature::TextureAtlas::DISABLED),
-  mDefaultTextureWrapMode(ImageVisualShaderFeature::DefaultTextureWrapMode::APPLY),
-  mRoundedCorner(ImageVisualShaderFeature::RoundedCorner::DISABLED),
-  mBorderline(ImageVisualShaderFeature::Borderline::DISABLED),
-  mAlphaMaskingOnRendering(ImageVisualShaderFeature::AlphaMaskingOnRendering::DISABLED),
-  mColorConversion(ImageVisualShaderFeature::ColorConversion::DONT_NEED),
-  mTexture()
-{
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::EnableTextureAtlas(bool enableTextureAtlas)
-{
-  mTextureAtlas = (enableTextureAtlas ? ImageVisualShaderFeature::TextureAtlas::ENABLED : ImageVisualShaderFeature::TextureAtlas::DISABLED);
-  return *this;
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode)
-{
-  mDefaultTextureWrapMode = (applyDefaultTextureWrapMode ? ImageVisualShaderFeature::DefaultTextureWrapMode::APPLY : ImageVisualShaderFeature::DefaultTextureWrapMode::DO_NOT_APPLY);
-  return *this;
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::EnableRoundedCorner(bool enableRoundedCorner)
-{
-  mRoundedCorner = (enableRoundedCorner ? ImageVisualShaderFeature::RoundedCorner::ENABLED : ImageVisualShaderFeature::RoundedCorner::DISABLED);
-  return *this;
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::EnableBorderline(bool enableBorderline)
-{
-  mBorderline = (enableBorderline ? ImageVisualShaderFeature::Borderline::ENABLED : ImageVisualShaderFeature::Borderline::DISABLED);
-  return *this;
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::SetTextureForFragmentShaderCheck(const Dali::Texture& texture)
-{
-  mTexture = texture;
-  return *this;
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering)
-{
-  mAlphaMaskingOnRendering = (enableAlphaMaskingOnRendering ? ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED : ImageVisualShaderFeature::AlphaMaskingOnRendering::DISABLED);
-  return *this;
-}
-
-ImageVisualShaderFeatureBuilder& ImageVisualShaderFeatureBuilder::EnableYuvToRgb(bool enableYuvToRgb, bool enableUnifiedYuvAndRgb)
-{
-  mColorConversion = (enableUnifiedYuvAndRgb ? ImageVisualShaderFeature::ColorConversion::UNIFIED_YUV_AND_RGB : (enableYuvToRgb ? ImageVisualShaderFeature::ColorConversion::YUV_TO_RGB : ImageVisualShaderFeature::ColorConversion::DONT_NEED));
-  return *this;
-}
-
-VisualFactoryCache::ShaderType ImageVisualShaderFeatureBuilder::GetShaderType()
-{
-  VisualFactoryCache::ShaderType shaderType = VisualFactoryCache::IMAGE_SHADER;
-  if(mTextureAtlas == ImageVisualShaderFeature::TextureAtlas::ENABLED)
-  {
-    if(mDefaultTextureWrapMode == ImageVisualShaderFeature::DefaultTextureWrapMode::APPLY)
-    {
-      shaderType = VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP;
-    }
-    else
-    {
-      shaderType = VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP;
-    }
-  }
-  else
-  {
-    uint32_t shaderTypeFlag = static_cast<uint32_t>(ImageVisualRequireFlag::DEFAULT);
-    if(mRoundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED)
-    {
-      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ROUNDED_CORNER);
-    }
-    if(mBorderline == ImageVisualShaderFeature::Borderline::ENABLED)
-    {
-      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::BORDERLINE);
-    }
-    if(mAlphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED)
-    {
-      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ALPHA_MASKING);
-    }
-    else if(mColorConversion == ImageVisualShaderFeature::ColorConversion::YUV_TO_RGB) // Not support gpu masking and color conversion at the same time now
-    {
-      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::COLOR_CONVERSION);
-    }
-    else if(mColorConversion == ImageVisualShaderFeature::ColorConversion::UNIFIED_YUV_AND_RGB)
-    {
-      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::COLOR_CONVERSION);
-      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::UNIFIED_YUV_AND_RGB);
-    }
-    shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
-  }
-
-  return shaderType;
-}
-
-ImageVisualShaderFeature::ChangeFragmentShader::Type ImageVisualShaderFeatureBuilder::NeedToChangeFragmentShader()
-{
-  return (mTexture && DevelTexture::IsNative(mTexture))
-           ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE
-           : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE;
-}
-
-void ImageVisualShaderFeatureBuilder::GetVertexShaderPrefixList(std::string& vertexShaderPrefixList)
-{
-  if(mTextureAtlas != ImageVisualShaderFeature::TextureAtlas::ENABLED)
-  {
-    if(mRoundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED)
-    {
-      vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
-    }
-    if(mBorderline == ImageVisualShaderFeature::Borderline::ENABLED)
-    {
-      vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
-    }
-    if(mAlphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED)
-    {
-      vertexShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING\n";
-    }
-  }
-}
-
-void ImageVisualShaderFeatureBuilder::GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList)
-{
-  if(mTextureAtlas == ImageVisualShaderFeature::TextureAtlas::ENABLED)
-  {
-    if(mDefaultTextureWrapMode == ImageVisualShaderFeature::DefaultTextureWrapMode::APPLY)
-    {
-      fragmentShaderPrefixList += "#define ATLAS_DEFAULT_WARP\n";
-    }
-    else
-    {
-      fragmentShaderPrefixList += "#define ATLAS_CUSTOM_WARP\n";
-    }
-  }
-  else
-  {
-    if(mRoundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
-    }
-    if(mBorderline == ImageVisualShaderFeature::Borderline::ENABLED)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
-    }
-    if(mAlphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING\n";
-    }
-    else if(mColorConversion == ImageVisualShaderFeature::ColorConversion::YUV_TO_RGB)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_YUV_TO_RGB\n";
-    }
-    else if(mColorConversion == ImageVisualShaderFeature::ColorConversion::UNIFIED_YUV_AND_RGB)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_UNIFIED_YUV_AND_RGB\n";
-    }
-  }
-}
-
-Dali::Texture ImageVisualShaderFeatureBuilder::GetTexture()
-{
-  return mTexture;
-}
-
-bool ImageVisualShaderFeatureBuilder::IsEnabledAlphaMaskingOnRendering() const
-{
-  return mAlphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED;
-}
-
-} // namespace Internal
-
-} // namespace Toolkit
-
-} // namespace Dali
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "image-atlas-manager.h"
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
 
 // EXTERNAL HEADER
 #include <dali/devel-api/adaptor-framework/image-loading.h>
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_ATLAS_MANAGER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
  */
 
 // CLASS HEADER
-#include <dali-toolkit/internal/visuals/image-visual-shader-debug.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-debug.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
  */
 
 // CLASS HEADER
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/rendering/texture-devel.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-debug.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-debug.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali/integration-api/debug.h>
 
@@ -36,13 +36,17 @@ namespace
 {
 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
+constexpr float ALPHA_PRE_MULTIPLIED(1.0f);
+
+constexpr int CUSTOM_PROPERTY_COUNT(2); // PixelArea, pre-multiplied alpha
+
 constexpr int              NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMAGE_SHADER - VisualFactoryCache::ShaderType::IMAGE_SHADER;
 constexpr std::string_view Y_FLIP_MASK_TEXTURE       = "uYFlipMaskTexture";
 constexpr float            NOT_FLIP_MASK_TEXTURE     = 0.0f;
 
-constexpr auto SHADER_TYPE_COUNT = 6u;
+constexpr auto PREDEFINED_SHADER_TYPE_COUNT = 6u;
 
-constexpr std::string_view VertexPredefines[SHADER_TYPE_COUNT]{
+constexpr std::string_view VertexPredefines[PREDEFINED_SHADER_TYPE_COUNT]{
   "",                                     // VisualFactoryCache::IMAGE_SHADER,
   "#define IS_REQUIRED_ROUNDED_CORNER\n", // VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER,
   "",                                     // VisualFactoryCache::IMAGE_SHADER_YUV_TO_RGB,
@@ -50,7 +54,7 @@ constexpr std::string_view VertexPredefines[SHADER_TYPE_COUNT]{
   "",                                     // VisualFactoryCache::IMAGE_SHADER_YUV_AND_RGB,
   "#define IS_REQUIRED_ROUNDED_CORNER\n", // VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_AND_RGB,
 };
-constexpr std::string_view FragmentPredefines[SHADER_TYPE_COUNT]{
+constexpr std::string_view FragmentPredefines[PREDEFINED_SHADER_TYPE_COUNT]{
   "",                                                                              // VisualFactoryCache::IMAGE_SHADER,
   "#define IS_REQUIRED_ROUNDED_CORNER\n",                                          // VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER,
   "#define IS_REQUIRED_YUV_TO_RGB\n",                                              // VisualFactoryCache::IMAGE_SHADER_YUV_TO_RGB,
@@ -58,7 +62,7 @@ constexpr std::string_view FragmentPredefines[SHADER_TYPE_COUNT]{
   "#define IS_REQUIRED_UNIFIED_YUV_AND_RGB\n",                                     // VisualFactoryCache::IMAGE_SHADER_YUV_AND_RGB,
   "#define IS_REQUIRED_ROUNDED_CORNER\n#define IS_REQUIRED_UNIFIED_YUV_AND_RGB\n", // VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_AND_RGB,
 };
-constexpr VisualFactoryCache::ShaderType ShaderTypePredefines[SHADER_TYPE_COUNT]{
+constexpr VisualFactoryCache::ShaderType ShaderTypePredefines[PREDEFINED_SHADER_TYPE_COUNT]{
   VisualFactoryCache::ShaderType::IMAGE_SHADER,
   VisualFactoryCache::ShaderType::IMAGE_SHADER_ROUNDED_CORNER,
   VisualFactoryCache::ShaderType::IMAGE_SHADER_YUV_TO_RGB,
@@ -77,7 +81,7 @@ ImageVisualShaderFactory::~ImageVisualShaderFactory()
 {
 }
 
-Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, ImageVisualShaderFeatureBuilder& featureBuilder)
+Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, const ImageVisualShaderFeature::FeatureBuilder& featureBuilder)
 {
   Shader                         shader;
   VisualFactoryCache::ShaderType shaderType = featureBuilder.GetShaderType();
@@ -142,7 +146,16 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, Ima
   }
 
   shader = factoryCache.GenerateAndSaveShader(shaderType, vertexShader, fragmentShader);
+
+  shader.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT +
+                                 (featureBuilder.IsEnabledAlphaMaskingOnRendering() ? 1 : 0));
+
   shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
+
+  // Most of image visual shader user (like svg, animated vector image visual) use pre-multiplied alpha.
+  // If the visual dont want to using pre-multiplied alpha, it should be set as 0.0f as renderer side.
+  shader.RegisterProperty(PREMULTIPLIED_ALPHA, ALPHA_PRE_MULTIPLIED);
+
   if(featureBuilder.IsEnabledAlphaMaskingOnRendering())
   {
     shader.RegisterProperty(Y_FLIP_MASK_TEXTURE, NOT_FLIP_MASK_TEXTURE);
@@ -174,6 +187,21 @@ std::string_view ImageVisualShaderFactory::GetFragmentShaderSource()
   return gFragmentShaderNoAtlas;
 }
 
+bool ImageVisualShaderFactory::AddPrecompiledShader(PrecompileShaderOption& option)
+{
+  ShaderFlagList shaderOption = option.GetShaderOptions();
+
+  auto        featureBuilder = ImageVisualShaderFeature::FeatureBuilder();
+  std::string vertexPrefixList;
+  std::string fragmentPrefixList;
+  CreatePrecompileShader(featureBuilder, shaderOption);
+
+  VisualFactoryCache::ShaderType type = featureBuilder.GetShaderType();
+  featureBuilder.GetVertexShaderPrefixList(vertexPrefixList);
+  featureBuilder.GetFragmentShaderPrefixList(fragmentPrefixList);
+  return SavePrecompileShader(type, vertexPrefixList, fragmentPrefixList);
+}
+
 void ImageVisualShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
 {
   std::vector<std::string_view> vertexPrefix;
@@ -181,7 +209,16 @@ void ImageVisualShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
   std::vector<std::string_view> shaderName;
   shaders.shaderCount = 0;
   int shaderCount     = 0;
-  for(uint32_t i = 0; i < SHADER_TYPE_COUNT; ++i)
+
+  for(uint32_t i = 0u; i < mRequestedPrecompileShader.size(); i++)
+  {
+    vertexPrefix.push_back(mRequestedPrecompileShader[i].vertexPrefix);
+    fragmentPrefix.push_back(mRequestedPrecompileShader[i].fragmentPrefix);
+    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+    shaderCount++;
+  }
+
+  for(uint32_t i = 0u; i < PREDEFINED_SHADER_TYPE_COUNT; ++i)
   {
     vertexPrefix.push_back(VertexPredefines[i]);
     fragmentPrefix.push_back(FragmentPredefines[i]);
@@ -189,12 +226,79 @@ void ImageVisualShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
     shaderCount++;
   }
 
-  shaders.vertexPrefix   = vertexPrefix;
-  shaders.fragmentPrefix = fragmentPrefix;
-  shaders.shaderName     = shaderName;
+  shaders.vertexPrefix   = std::move(vertexPrefix);
+  shaders.fragmentPrefix = std::move(fragmentPrefix);
+  shaders.shaderName     = std::move(shaderName);
   shaders.vertexShader   = SHADER_IMAGE_VISUAL_SHADER_VERT;
   shaders.fragmentShader = SHADER_IMAGE_VISUAL_SHADER_FRAG;
   shaders.shaderCount    = shaderCount;
+  shaders.custom         = false;
+}
+
+void ImageVisualShaderFactory::CreatePrecompileShader(ImageVisualShaderFeature::FeatureBuilder& builder, const ShaderFlagList& option)
+{
+  for(uint32_t i = 0; i < option.size(); ++i)
+  {
+    if(option[i] == PrecompileShaderOption::Flag::ATLAS_DEFAULT)
+    {
+      builder.EnableTextureAtlas(true);
+      builder.ApplyDefaultTextureWrapMode(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::ATLAS_CUSTOM)
+    {
+      builder.EnableTextureAtlas(true);
+      builder.ApplyDefaultTextureWrapMode(false);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::ROUNDED_CORNER)
+    {
+      builder.EnableRoundedCorner(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::BORDERLINE)
+    {
+      builder.EnableBorderline(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::MASKING)
+    {
+      builder.EnableAlphaMaskingOnRendering(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::YUV_TO_RGB)
+    {
+      builder.EnableYuvToRgb(true, false);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::YUV_AND_RGB)
+    {
+      builder.EnableYuvToRgb(false, true);
+    }
+  }
+}
+
+bool ImageVisualShaderFactory::SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix)
+{
+  for(uint32_t i = 0u; i < PREDEFINED_SHADER_TYPE_COUNT; i++)
+  {
+    if(ShaderTypePredefines[i] == shader)
+    {
+      DALI_LOG_WARNING("This shader already added list(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  for(uint32_t i = 0u; i < mRequestedPrecompileShader.size(); i++)
+  {
+    if(mRequestedPrecompileShader[i].type == shader)
+    {
+      DALI_LOG_WARNING("This shader already requsted(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  RequestShaderInfo info;
+  info.type           = shader;
+  info.vertexPrefix   = vertexPrefix;
+  info.fragmentPrefix = fragmentPrefix;
+  mRequestedPrecompileShader.push_back(info);
+  DALI_LOG_RELEASE_INFO("Add precompile shader success!!(%s)", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(shader, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+  return true;
 }
 
 } // namespace Internal
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_VISUAL_SHADER_FACTORY_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 // EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/visual-shader-factory-interface.h>
 #include <string_view>
 
 namespace Dali
@@ -34,7 +36,7 @@ namespace Internal
 /**
  * ImageVisualShaderFactory is an object that provides and shares shaders between image visuals
  */
-class ImageVisualShaderFactory
+class ImageVisualShaderFactory : public VisualShaderFactoryInterface
 {
 public:
   /**
@@ -53,7 +55,7 @@ public:
    * @param[in] featureBuilder Collection of current image shader's features
    * @return The standard image rendering shader with features.
    */
-  Shader GetShader(VisualFactoryCache& factoryCache, ImageVisualShaderFeatureBuilder& featureBuilder);
+  Shader GetShader(VisualFactoryCache& factoryCache, const ImageVisualShaderFeature::FeatureBuilder& featureBuilder);
 
   /**
    * @brief Request the default vertex shader source.
@@ -67,11 +69,25 @@ public:
    */
   std::string_view GetFragmentShaderSource();
 
+public: // Implementation of VisualShaderFactoryInterface
   /**
-   * @brief Get the default shader source.
-   * @param[in] shaders shaderList for precompile
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::AddPrecompiledShader
    */
-  void GetPreCompiledShader(RawShaderData& shaders);
+  bool AddPrecompiledShader(PrecompileShaderOption& option) override;
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::GetPreCompiledShader
+   */
+  void GetPreCompiledShader(RawShaderData& shaders) override;
+
+private:
+  /**
+   * @brief Create pre-compiled shader for image with builder and option.
+   */
+  void CreatePrecompileShader(ImageVisualShaderFeature::FeatureBuilder& builder, const ShaderFlagList& option);
+  /**
+   * @brief Check if cached hash value is valid or not.
+   */
+  bool SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix);
 
 protected:
   /**
diff --git a/dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.cpp b/dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.cpp
new file mode 100644 (file)
index 0000000..de9b32d
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+// enum of required list when we select shader
+enum class ImageVisualRequireFlag : uint32_t
+{
+  DEFAULT          = 0,
+  ROUNDED_CORNER   = 1 << 0,
+  BORDERLINE       = 1 << 1,
+  ALPHA_MASKING    = 1 << 2,
+  COLOR_CONVERSION = 1 << 3,
+
+  UNIFIED_YUV_AND_RGB = 1 << 2, // Special enum to trick unified YUV and RGB.
+};
+
+static constexpr auto          SHADER_TYPE_COUNT = 16u;
+VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[SHADER_TYPE_COUNT] =
+  {
+    VisualFactoryCache::IMAGE_SHADER,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER,
+    VisualFactoryCache::IMAGE_SHADER_BORDERLINE,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE,
+    VisualFactoryCache::IMAGE_SHADER_MASKING,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_MASKING,
+    VisualFactoryCache::IMAGE_SHADER_BORDERLINE_MASKING,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING,
+    VisualFactoryCache::IMAGE_SHADER_YUV_TO_RGB,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_TO_RGB,
+    VisualFactoryCache::IMAGE_SHADER_BORDERLINE_YUV_TO_RGB,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_TO_RGB,
+    VisualFactoryCache::IMAGE_SHADER_YUV_AND_RGB,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_AND_RGB,
+    VisualFactoryCache::IMAGE_SHADER_BORDERLINE_YUV_AND_RGB,
+    VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_AND_RGB};
+} // unnamed namespace
+
+namespace ImageVisualShaderFeature
+{
+FeatureBuilder::FeatureBuilder()
+: mTextureAtlas(TextureAtlas::DISABLED),
+  mDefaultTextureWrapMode(DefaultTextureWrapMode::APPLY),
+  mRoundedCorner(RoundedCorner::DISABLED),
+  mBorderline(Borderline::DISABLED),
+  mAlphaMaskingOnRendering(AlphaMaskingOnRendering::DISABLED),
+  mColorConversion(ColorConversion::DONT_NEED),
+  mTexture()
+{
+}
+
+FeatureBuilder& FeatureBuilder::EnableTextureAtlas(bool enableTextureAtlas)
+{
+  mTextureAtlas = (enableTextureAtlas ? TextureAtlas::ENABLED : TextureAtlas::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode)
+{
+  mDefaultTextureWrapMode = (applyDefaultTextureWrapMode ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableRoundedCorner(bool enableRoundedCorner)
+{
+  mRoundedCorner = (enableRoundedCorner ? RoundedCorner::ENABLED : RoundedCorner::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableBorderline(bool enableBorderline)
+{
+  mBorderline = (enableBorderline ? Borderline::ENABLED : Borderline::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::SetTextureForFragmentShaderCheck(const Dali::Texture& texture)
+{
+  mTexture = texture;
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering)
+{
+  mAlphaMaskingOnRendering = (enableAlphaMaskingOnRendering ? AlphaMaskingOnRendering::ENABLED : AlphaMaskingOnRendering::DISABLED);
+  return *this;
+}
+
+FeatureBuilder& FeatureBuilder::EnableYuvToRgb(bool enableYuvToRgb, bool enableUnifiedYuvAndRgb)
+{
+  mColorConversion = (enableUnifiedYuvAndRgb ? ColorConversion::UNIFIED_YUV_AND_RGB : (enableYuvToRgb ? ColorConversion::YUV_TO_RGB : ColorConversion::DONT_NEED));
+  return *this;
+}
+
+VisualFactoryCache::ShaderType FeatureBuilder::GetShaderType() const
+{
+  VisualFactoryCache::ShaderType shaderType = VisualFactoryCache::IMAGE_SHADER;
+  if(mTextureAtlas == TextureAtlas::ENABLED)
+  {
+    if(mDefaultTextureWrapMode == DefaultTextureWrapMode::APPLY)
+    {
+      shaderType = VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP;
+    }
+    else
+    {
+      shaderType = VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP;
+    }
+  }
+  else
+  {
+    uint32_t shaderTypeFlag = static_cast<uint32_t>(ImageVisualRequireFlag::DEFAULT);
+    if(mRoundedCorner == RoundedCorner::ENABLED)
+    {
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ROUNDED_CORNER);
+    }
+    if(mBorderline == Borderline::ENABLED)
+    {
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::BORDERLINE);
+    }
+    if(mAlphaMaskingOnRendering == AlphaMaskingOnRendering::ENABLED)
+    {
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ALPHA_MASKING);
+    }
+    else if(mColorConversion == ColorConversion::YUV_TO_RGB) // Not support gpu masking and color conversion at the same time now
+    {
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::COLOR_CONVERSION);
+    }
+    else if(mColorConversion == ColorConversion::UNIFIED_YUV_AND_RGB)
+    {
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::COLOR_CONVERSION);
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::UNIFIED_YUV_AND_RGB);
+    }
+    shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
+  }
+
+  return shaderType;
+}
+
+ChangeFragmentShader::Type FeatureBuilder::NeedToChangeFragmentShader() const
+{
+  return (mTexture && DevelTexture::IsNative(mTexture))
+           ? ChangeFragmentShader::NEED_CHANGE
+           : ChangeFragmentShader::DONT_CHANGE;
+}
+
+void FeatureBuilder::GetVertexShaderPrefixList(std::string& vertexShaderPrefixList) const
+{
+  if(mTextureAtlas != TextureAtlas::ENABLED)
+  {
+    if(mRoundedCorner == RoundedCorner::ENABLED)
+    {
+      vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
+    }
+    if(mBorderline == Borderline::ENABLED)
+    {
+      vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
+    }
+    if(mAlphaMaskingOnRendering == AlphaMaskingOnRendering::ENABLED)
+    {
+      vertexShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING\n";
+    }
+  }
+}
+
+void FeatureBuilder::GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const
+{
+  if(mTextureAtlas == TextureAtlas::ENABLED)
+  {
+    if(mDefaultTextureWrapMode == DefaultTextureWrapMode::APPLY)
+    {
+      fragmentShaderPrefixList += "#define ATLAS_DEFAULT_WARP\n";
+    }
+    else
+    {
+      fragmentShaderPrefixList += "#define ATLAS_CUSTOM_WARP\n";
+    }
+  }
+  else
+  {
+    if(mRoundedCorner == RoundedCorner::ENABLED)
+    {
+      fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
+    }
+    if(mBorderline == Borderline::ENABLED)
+    {
+      fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
+    }
+    if(mAlphaMaskingOnRendering == AlphaMaskingOnRendering::ENABLED)
+    {
+      fragmentShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING\n";
+    }
+    else if(mColorConversion == ColorConversion::YUV_TO_RGB)
+    {
+      fragmentShaderPrefixList += "#define IS_REQUIRED_YUV_TO_RGB\n";
+    }
+    else if(mColorConversion == ColorConversion::UNIFIED_YUV_AND_RGB)
+    {
+      fragmentShaderPrefixList += "#define IS_REQUIRED_UNIFIED_YUV_AND_RGB\n";
+    }
+  }
+}
+
+Dali::Texture FeatureBuilder::GetTexture() const
+{
+  return mTexture;
+}
+
+bool FeatureBuilder::IsEnabledAlphaMaskingOnRendering() const
+{
+  return mAlphaMaskingOnRendering == AlphaMaskingOnRendering::ENABLED;
+}
+
+} // namespace ImageVisualShaderFeature
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_VISUAL_SHADER_FEATURE_BUILDER_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@
  */
 
 // EXTERNAL INCLUDES
-#include <dali/public-api/rendering/texture.h>
-#include <dali/devel-api/rendering/texture-devel.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali/devel-api/rendering/texture-devel.h>
+#include <dali/public-api/rendering/texture.h>
 
 // INTERNAL INCLUDES
 
@@ -121,51 +121,51 @@ enum Type
 };
 } // namespace ColorConversion
 
-} // namespace ImageVisualShaderFeature
-
 /**
  * @brief Collection of current image visual feature. Only use for ImageVisualShaderFactory::GetShader()
  */
-class ImageVisualShaderFeatureBuilder
+class FeatureBuilder
 {
 public:
-  ImageVisualShaderFeatureBuilder();
+  FeatureBuilder();
 
-  ImageVisualShaderFeatureBuilder& EnableTextureAtlas(bool enableTextureAtlas);
+  FeatureBuilder& EnableTextureAtlas(bool enableTextureAtlas);
 
-  ImageVisualShaderFeatureBuilder& ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode);
+  FeatureBuilder& ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode);
 
-  ImageVisualShaderFeatureBuilder& EnableRoundedCorner(bool enableRoundedCorner);
+  FeatureBuilder& EnableRoundedCorner(bool enableRoundedCorner);
 
-  ImageVisualShaderFeatureBuilder& EnableBorderline(bool enableBorderline);
+  FeatureBuilder& EnableBorderline(bool enableBorderline);
 
-  ImageVisualShaderFeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture);
+  FeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture);
 
-  ImageVisualShaderFeatureBuilder& EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering);
+  FeatureBuilder& EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering);
 
-  ImageVisualShaderFeatureBuilder& EnableYuvToRgb(bool enableYuvToRgb, bool enableUnifiedYuvAndRgb = false);
+  FeatureBuilder& EnableYuvToRgb(bool enableYuvToRgb, bool enableUnifiedYuvAndRgb = false);
 
-  VisualFactoryCache::ShaderType GetShaderType();
+  VisualFactoryCache::ShaderType GetShaderType() const;
 
-  ImageVisualShaderFeature::ChangeFragmentShader::Type NeedToChangeFragmentShader();
+  ChangeFragmentShader::Type NeedToChangeFragmentShader() const;
 
-  void GetVertexShaderPrefixList(std::string& vertexShaderPrefixList);
-  void GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList);
+  void GetVertexShaderPrefixList(std::string& vertexShaderPrefixList) const;
+  void GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const;
 
-  Dali::Texture GetTexture();
+  Dali::Texture GetTexture() const;
 
   bool IsEnabledAlphaMaskingOnRendering() const;
 
 private:
-  ImageVisualShaderFeature::TextureAtlas::Type            mTextureAtlas : 2;            ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED
-  ImageVisualShaderFeature::DefaultTextureWrapMode::Type  mDefaultTextureWrapMode : 2;  ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY
-  ImageVisualShaderFeature::RoundedCorner::Type           mRoundedCorner : 2;           ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED
-  ImageVisualShaderFeature::Borderline::Type              mBorderline : 2;              ///< Whether use borderline, or not. default as Borderline::DISABLED
-  ImageVisualShaderFeature::AlphaMaskingOnRendering::Type mAlphaMaskingOnRendering : 2; ///< Whether use runtime alpha masking, or not. default as AlphaMaskingOnRendering::DISABLED
-  ImageVisualShaderFeature::ColorConversion::Type         mColorConversion : 2;         ///< Whether the color format conversion is needed or not
-  Dali::Texture                                           mTexture;                     ///< Texture to check whether we need to change fragment shader or not
+  TextureAtlas::Type            mTextureAtlas : 2;            ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED
+  DefaultTextureWrapMode::Type  mDefaultTextureWrapMode : 2;  ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY
+  RoundedCorner::Type           mRoundedCorner : 2;           ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED
+  Borderline::Type              mBorderline : 2;              ///< Whether use borderline, or not. default as Borderline::DISABLED
+  AlphaMaskingOnRendering::Type mAlphaMaskingOnRendering : 2; ///< Whether use runtime alpha masking, or not. default as AlphaMaskingOnRendering::DISABLED
+  ColorConversion::Type         mColorConversion : 2;         ///< Whether the color format conversion is needed or not
+  Dali::Texture                 mTexture;                     ///< Texture to check whether we need to change fragment shader or not
 };
 
+} // namespace ImageVisualShaderFeature
+
 } // namespace Internal
 
 } // namespace Toolkit
index 02d08af..f299975 100644 (file)
@@ -25,6 +25,7 @@
 #include <dali/devel-api/rendering/texture-devel.h>
 #include <dali/devel-api/scripting/enum-helper.h>
 #include <dali/devel-api/scripting/scripting.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/actors/layer.h>
 #include <dali/public-api/adaptor-framework/async-task-manager.h>
@@ -34,9 +35,9 @@
 // INTERNAL HEADERS
 #include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
 #include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
@@ -54,7 +55,7 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(7); // ltr, wrap, pixel area, atlas, pixalign, crop to mask, mask texture ratio
+const int CUSTOM_PROPERTY_COUNT(8); // ltr, wrap, pixel area, atlas, pixalign, crop to mask, mask texture ratio, pre-multiplied alpha
 
 // fitting modes
 DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE)
@@ -62,6 +63,7 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SCALE_TO_FILL)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_WIDTH)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_HEIGHT)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, VISUAL_FITTING)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, DEFAULT)
 DALI_ENUM_TO_STRING_TABLE_END(FITTING_MODE)
 
@@ -127,6 +129,7 @@ const NameIndexMatch NAME_INDEX_MATCH_TABLE[] =
     {RELEASE_POLICY_NAME, Toolkit::ImageVisual::Property::RELEASE_POLICY},
     {ORIENTATION_CORRECTION_NAME, Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION},
     {FAST_TRACK_UPLOADING_NAME, Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING},
+    {SYNCHRONOUS_SIZING, Toolkit::DevelImageVisual::Property::SYNCHRONOUS_SIZING},
 };
 const int NAME_INDEX_MATCH_TABLE_SIZE = sizeof(NAME_INDEX_MATCH_TABLE) / sizeof(NAME_INDEX_MATCH_TABLE[0]);
 
@@ -180,13 +183,15 @@ ImageVisual::ImageVisual(VisualFactoryCache&       factoryCache,
                          ImageDimensions           size,
                          FittingMode::Type         fittingMode,
                          Dali::SamplingMode::Type  samplingMode)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::IMAGE),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::IMAGE),
   mPixelArea(FULL_TEXTURE_RECT),
   mPixelAreaIndex(Property::INVALID_INDEX),
+  mPreMultipliedAlphaIndex(Property::INVALID_INDEX),
   mPlacementActor(),
   mImageUrl(imageUrl),
   mMaskingData(),
   mDesiredSize(size),
+  mLastRequiredSize(size),
   mTextureId(TextureManager::INVALID_TEXTURE_ID),
   mTextures(),
   mImageVisualShaderFactory(shaderFactory),
@@ -208,7 +213,7 @@ ImageVisual::ImageVisual(VisualFactoryCache&       factoryCache,
 
 ImageVisual::~ImageVisual()
 {
-  if(Stage::IsInstalled())
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable()))
   {
     if(mImageUrl.IsValid())
     {
@@ -218,12 +223,12 @@ ImageVisual::~ImageVisual()
       if(mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
       {
         TextureManager& textureManager = mFactoryCache.GetTextureManager();
-        textureManager.RemoveExternalTexture(mImageUrl.GetUrl());
+        textureManager.RemoveExternalTexture(mImageUrl);
       }
       else if(mImageUrl.IsBufferResource())
       {
         TextureManager& textureManager = mFactoryCache.GetTextureManager();
-        textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
+        textureManager.RemoveEncodedImageBuffer(mImageUrl);
       }
     }
 
@@ -263,7 +268,7 @@ void ImageVisual::DoSetProperties(const Property::Map& propertyMap)
   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
   {
     auto attemptAtlasing = AttemptAtlasing();
-    LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
+    LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mDesiredSize, TextureManager::ReloadPolicy::CACHED);
   }
 }
 
@@ -456,6 +461,12 @@ void ImageVisual::DoSetProperty(Property::Index index, const Property::Value& va
       value.Get(mUseFastTrackUploading);
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::SYNCHRONOUS_SIZING:
+    {
+      value.Get(mUseSynchronousSizing);
+      break;
+    }
   }
 }
 
@@ -473,8 +484,48 @@ void ImageVisual::AllocateMaskData()
 
 void ImageVisual::GetNaturalSize(Vector2& naturalSize)
 {
-  if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
+  if(mUseSynchronousSizing && (mLastRequiredSize.GetWidth() > 0 && mLastRequiredSize.GetHeight() > 0))
+  {
+    if(mImpl->mRenderer)
+    {
+      auto textureSet = mImpl->mRenderer.GetTextures();
+      if(textureSet && textureSet.GetTextureCount())
+      {
+        auto texture = textureSet.GetTexture(0);
+        if(texture)
+        {
+          if(mTextureSize != Vector2::ZERO)
+          {
+            naturalSize = mTextureSize;
+            return;
+          }
+        }
+      }
+    }
+
+    naturalSize.x = mLastRequiredSize.GetWidth();
+    naturalSize.y = mLastRequiredSize.GetHeight();
+    return;
+  }
+  else if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
   {
+    if(mImpl->mRenderer)
+    {
+      auto textureSet = mImpl->mRenderer.GetTextures();
+      if(textureSet && textureSet.GetTextureCount())
+      {
+        auto texture = textureSet.GetTexture(0);
+        if(texture)
+        {
+          if(mTextureSize != Vector2::ZERO)
+          {
+            naturalSize = mTextureSize;
+            return;
+          }
+        }
+      }
+    }
+
     naturalSize.x = mDesiredSize.GetWidth();
     naturalSize.y = mDesiredSize.GetHeight();
     return;
@@ -514,7 +565,9 @@ void ImageVisual::GetNaturalSize(Vector2& naturalSize)
   {
     if(mImageUrl.GetProtocolType() == VisualUrl::LOCAL)
     {
-      ImageDimensions dimensions = Dali::GetClosestImageSize(mImageUrl.GetUrl());
+      // Note that We don't consider desired image size for this case.
+      // Just use (0, 0) value for desired size of image.
+      ImageDimensions dimensions = Dali::GetClosestImageSize(mImageUrl.GetUrl(), ImageDimensions(0, 0), mFittingMode, mSamplingMode, mOrientationCorrection);
 
       if(dimensions != ImageDimensions(0, 0))
       {
@@ -554,7 +607,7 @@ void ImageVisual::OnInitialize()
   if(mImageUrl.IsValid() && (mImageUrl.IsBufferResource() || mImageUrl.GetProtocolType() == VisualUrl::TEXTURE))
   {
     TextureManager& textureManager = mFactoryCache.GetTextureManager();
-    textureManager.UseExternalResource(mImageUrl.GetUrl());
+    textureManager.UseExternalResource(mImageUrl);
   }
 
   // Generate geometry and shader. Note that we should check AddOn when generate geometry, due to LoadPolicy::IMMEDIATE case
@@ -576,10 +629,12 @@ void ImageVisual::OnInitialize()
   }
 }
 
-void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection, TextureManager::ReloadPolicy forceReload)
+void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, const Dali::ImageDimensions& size, TextureManager::ReloadPolicy forceReload)
 {
   TextureManager& textureManager = mFactoryCache.GetTextureManager();
 
+  mLastRequiredSize = size;
+
   ImageAtlasManagerPtr atlasManager        = nullptr;
   AtlasUploadObserver* atlasUploadObserver = nullptr;
   auto                 textureObserver     = this;
@@ -623,6 +678,7 @@ void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& te
        forceReload == TextureManager::ReloadPolicy::CACHED &&
        (mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE) &&
        !synchronousLoading &&
+       !mUseSynchronousSizing &&
        !atlasing &&
        !mImpl->mCustomShader &&
        !(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()))
@@ -631,13 +687,14 @@ void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& te
     }
     else if(mUseFastTrackUploading)
     {
-      DALI_LOG_DEBUG_INFO("FastTrack : Fail to load fast track. mUrl : [%s]%s%s%s%s%s%s%s%s\n",
+      DALI_LOG_DEBUG_INFO("FastTrack : Fail to load fast track. mUrl : [%s]%s%s%s%s%s%s%s%s%s\n",
                           mImageUrl.GetEllipsedUrl().c_str(),
                           (mLoadPolicy != Toolkit::ImageVisual::LoadPolicy::ATTACHED) ? "/ mLoadPolicy != ATTACHED" : "",
                           (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::DETACHED) ? "/ mReleasePolicy != DETACHED" : "",
                           (forceReload != TextureManager::ReloadPolicy::CACHED) ? "/ forceReload != CACHED" : "",
                           (!(mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE)) ? "/ url is not image" : "",
                           (synchronousLoading) ? "/ synchronousLoading" : "",
+                          (mUseSynchronousSizing) ? "/ useSynchronousSizing " : "",
                           (atlasing) ? "/ atlasing" : "",
                           (mImpl->mCustomShader) ? "/ use customs shader" : "",
                           (mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()) ? "/ use masking url" : "");
@@ -651,7 +708,7 @@ void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& te
     EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
 
     // Set new TextureSet with fast track loading task
-    mFastTrackLoadingTask = new FastTrackLoadingTask(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mOrientationCorrection, preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, mFactoryCache.GetLoadYuvPlanes(), MakeCallback(this, &ImageVisual::FastLoadComplete));
+    mFastTrackLoadingTask = new FastTrackLoadingTask(mImageUrl, size, mFittingMode, mSamplingMode, mOrientationCorrection, preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, mFactoryCache.GetLoadYuvPlanes(), MakeCallback(this, &ImageVisual::FastLoadComplete));
 
     TextureSet textureSet = TextureSet::New();
     if(!mFastTrackLoadingTask->mLoadPlanesAvaliable)
@@ -679,7 +736,7 @@ void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& te
   }
   else
   {
-    textures = textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
+    textures = textureManager.LoadTexture(mImageUrl, size, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
   }
 
   if(textures)
@@ -717,7 +774,7 @@ void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& te
   }
 }
 
-bool ImageVisual::AttemptAtlasing()
+bool ImageVisual::AttemptAtlasing() const
 {
   return (!mImpl->mCustomShader && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()) && mAttemptAtlasing);
 }
@@ -739,7 +796,7 @@ void ImageVisual::InitializeRenderer()
   {
     if(mTextureId == TextureManager::INVALID_TEXTURE_ID)
     {
-      LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
+      LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mUseSynchronousSizing ? mLastRequiredSize : mDesiredSize, TextureManager::ReloadPolicy::CACHED);
     }
     else
     {
@@ -877,9 +934,11 @@ void ImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
   if(mImageUrl.IsValid())
   {
+    Dali::ImageDimensions size = mUseSynchronousSizing ? mLastRequiredSize : mDesiredSize;
+
     map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
-    map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
-    map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
+    map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, size.GetWidth());
+    map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, size.GetHeight());
   }
 
   map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
@@ -914,6 +973,7 @@ void ImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, mOrientationCorrection);
 
   map.Insert(Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING, mUseFastTrackUploading);
+  map.Insert(Toolkit::DevelImageVisual::Property::SYNCHRONOUS_SIZING, mUseSynchronousSizing);
 }
 
 void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -922,9 +982,26 @@ void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
   if(mImageUrl.IsValid())
   {
-    map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
-    map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
+    Dali::ImageDimensions size = mUseSynchronousSizing ? mLastRequiredSize : mDesiredSize;
+    map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, size.GetWidth());
+    map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, size.GetHeight());
+  }
+}
+
+void ImageVisual::EnablePreMultipliedAlpha(bool preMultiplied)
+{
+  if(mImpl->mRenderer)
+  {
+    if(mPreMultipliedAlphaIndex != Property::INVALID_INDEX || !preMultiplied)
+    {
+      // RegisterUniqueProperty call SetProperty internally.
+      // Register PREMULTIPLIED_ALPHA only if it become false.
+      // Default PREMULTIPLIED_ALPHA value is 1.0f, at image-visual-shader-factory.cpp
+      mPreMultipliedAlphaIndex = mImpl->mRenderer.RegisterUniqueProperty(mPreMultipliedAlphaIndex, PREMULTIPLIED_ALPHA, preMultiplied ? 1.0f : 0.0f);
+    }
   }
+
+  Visual::Base::EnablePreMultipliedAlpha(preMultiplied);
 }
 
 void ImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
@@ -941,7 +1018,11 @@ void ImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::P
       ResourceReady(Toolkit::Visual::ResourceStatus::PREPARING);
       mLoadState = TextureManager::LoadState::NOT_STARTED;
 
-      LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::FORCED);
+      // Need to reset textureset after change load state.
+      mTextures.Reset();
+
+      Dali::ImageDimensions size = mUseSynchronousSizing ? mLastRequiredSize : mDesiredSize;
+      LoadTexture(attemptAtlasing, mAtlasRect, mTextures, size, TextureManager::ReloadPolicy::FORCED);
       break;
     }
   }
@@ -953,6 +1034,31 @@ void ImageVisual::OnSetTransform()
   {
     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
   }
+
+  if(mUseSynchronousSizing)
+  {
+    // Get current visual size
+    Vector2  size                    = mImpl->mTransform.GetVisualSize(mImpl->mControlSize);
+    uint32_t maximumNumber           = std::numeric_limits<uint16_t>::max();
+    uint32_t sizeWidth               = static_cast<uint32_t>(roundf(size.width));
+    sizeWidth                        = std::min(sizeWidth, maximumNumber);
+    uint32_t sizeHeight              = static_cast<uint32_t>(roundf(size.height));
+    sizeHeight                       = std::min(sizeHeight, maximumNumber);
+    Dali::ImageDimensions visualSize = Dali::ImageDimensions(sizeWidth, sizeHeight);
+
+    // Reload if visual size is updated
+    if(mLastRequiredSize != visualSize)
+    {
+      RemoveTexture();
+      mLoadState = TextureManager::LoadState::NOT_STARTED;
+
+      // Need to reset textureset after change load state.
+      mTextures.Reset();
+
+      bool attemptAtlasing = AttemptAtlasing();
+      LoadTexture(attemptAtlasing, mAtlasRect, mTextures, visualSize, TextureManager::ReloadPolicy::CACHED);
+    }
+  }
 }
 
 void ImageVisual::UpdateShader()
@@ -1226,7 +1332,7 @@ Shader ImageVisual::GenerateShader() const
     // Create and cache the standard shader
     shader = mImageVisualShaderFactory.GetShader(
       mFactoryCache,
-      ImageVisualShaderFeatureBuilder()
+      ImageVisualShaderFeature::FeatureBuilder()
         .EnableTextureAtlas(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED && !useNativeImage)
         .ApplyDefaultTextureWrapMode(mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE)
         .EnableRoundedCorner(IsRoundedCornerRequired())
@@ -1346,6 +1452,9 @@ void ImageVisual::ResetRenderer()
   ComputeTextureSize();
 
   mLoadState = TextureManager::LoadState::NOT_STARTED;
+
+  // Need to reset textureset after change load state.
+  mTextures.Reset();
 }
 
 void ImageVisual::ShowBrokenImage()
@@ -1403,7 +1512,7 @@ void ImageVisual::ResetFastTrackLoadingTask()
 Geometry ImageVisual::GenerateGeometry(TextureManager::TextureId textureId, bool createForce)
 {
   Geometry geometry;
-  if(Stage::IsInstalled())
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable()))
   {
     if(mImpl->mCustomShader)
     {
index 60a67f1..690624c 100644 (file)
@@ -131,7 +131,7 @@ public:
                             const VisualUrl&          imageUrl,
                             const Property::Map&      properties,
                             ImageDimensions           size         = ImageDimensions(),
-                            FittingMode::Type         fittingMode  = FittingMode::DEFAULT,
+                            FittingMode::Type         fittingMode  = FittingMode::VISUAL_FITTING,
                             Dali::SamplingMode::Type  samplingMode = SamplingMode::BOX_THEN_LINEAR);
 
   /**
@@ -151,7 +151,7 @@ public:
                             ImageVisualShaderFactory& shaderFactory,
                             const VisualUrl&          imageUrl,
                             ImageDimensions           size         = ImageDimensions(),
-                            FittingMode::Type         fittingMode  = FittingMode::DEFAULT,
+                            FittingMode::Type         fittingMode  = FittingMode::VISUAL_FITTING,
                             Dali::SamplingMode::Type  samplingMode = SamplingMode::BOX_THEN_LINEAR);
 
 public: // from Visual
@@ -171,6 +171,11 @@ public: // from Visual
   void DoCreateInstancePropertyMap(Property::Map& map) const override;
 
   /**
+   * @copydoc Visual::Base::EnablePreMultipliedAlpha
+   */
+  void EnablePreMultipliedAlpha(bool preMultiplied) override;
+
+  /**
    * @copydoc Visual::Base::OnDoAction
    */
   void OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) override;
@@ -275,16 +280,16 @@ private:
    * @param[in, out] atlasing flag if the image has been put in a atlas (true), passing false will not atlas even if possible.
    * @param[out] atlasRect if atlasing is used this the texture area of the image in the atlas.
    * @param[out] textures resulting texture set from the image loading.
-   * @param[in] orientationCorrection flag determines if orientation correction should be performed
+   * @param[in] size if mUseSynchronousSizing is true this is the size of visual, else it is mDesiredSize
    * @param[in] forceReload flag determines if the texture should be reloaded from its source or use the cached texture.
    */
-  void LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection, TextureManager::ReloadPolicy forceReload);
+  void LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, const Dali::ImageDimensions& size, TextureManager::ReloadPolicy forceReload);
 
   /**
    * @brief Checks if atlasing should be attempted
    * @return bool returns true if atlasing can be attempted.
    */
-  bool AttemptAtlasing();
+  bool AttemptAtlasing() const;
 
   /**
    * @brief Initializes the Dali::Renderer from the image url
@@ -362,13 +367,16 @@ private:
   Geometry GenerateGeometry(TextureManager::TextureId textureId, bool createForce);
 
 private:
-  Vector4                            mPixelArea;
-  Property::Index                    mPixelAreaIndex;
+  Vector4         mPixelArea;
+  Property::Index mPixelAreaIndex;
+  Property::Index mPreMultipliedAlphaIndex; ///< Index of premultipliedAlpha uniform.
+
   WeakHandle<Actor>                  mPlacementActor;
   VisualUrl                          mImageUrl;
   TextureManager::MaskingDataPointer mMaskingData;
 
   Dali::ImageDimensions     mDesiredSize;
+  Dali::ImageDimensions     mLastRequiredSize;
   TextureManager::TextureId mTextureId;
   TextureSet                mTextures;
   Vector2                   mTextureSize;
@@ -395,6 +403,7 @@ private:
   bool                                            mUseFastTrackUploading{false};  ///< True if we use fast tack feature.
   bool                                            mRendererAdded{false};          ///< True if renderer added into actor.
   bool                                            mUseBrokenImageRenderer{false}; ///< True if renderer changed as broken image.
+  bool                                            mUseSynchronousSizing{false};   ///< True if we need to synchronize image texture size to visual size, otherwise use mDesiredSize.
 };
 
 } // namespace Internal
index 3196a1a..7c8b0b8 100644 (file)
@@ -103,7 +103,7 @@ MeshVisualPtr MeshVisual::New(VisualFactoryCache& factoryCache, const Property::
 }
 
 MeshVisual::MeshVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::MESH),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::MESH),
   mShadingMode(Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING),
   mUseTexture(true),
   mUseMipmapping(true),
diff --git a/dali-toolkit/internal/visuals/npatch-shader-factory.cpp b/dali-toolkit/internal/visuals/npatch-shader-factory.cpp
new file mode 100644 (file)
index 0000000..26fa64c
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/npatch-shader-factory.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+NpatchShaderFactory::NpatchShaderFactory()
+: mNpatchXStretchCount(0),
+  mNpatchYStretchCount(0),
+  mNpatchMaskingEnable(false)
+{
+}
+
+NpatchShaderFactory::~NpatchShaderFactory()
+{
+}
+
+bool NpatchShaderFactory::AddPrecompiledShader(PrecompileShaderOption& option)
+{
+  ShaderFlagList shaderOption = option.GetShaderOptions();
+
+  // Find Masking flag
+  for(uint32_t i = 0; i < shaderOption.size(); ++i)
+  {
+    if(shaderOption[i] == PrecompileShaderOption::Flag::MASKING)
+    {
+      mNpatchMaskingEnable = true;
+    }
+  }
+
+  mNpatchXStretchCount = option.GetNpatchXStretchCount();
+  mNpatchYStretchCount = option.GetNpatchYStretchCount();
+
+  std::string vertexShader;
+  std::string fragmentShader;
+  GetVertexShader(vertexShader);
+  GetFragmentShader(fragmentShader);
+
+  VisualFactoryCache::ShaderType shaderType =  mNpatchMaskingEnable? VisualFactoryCache::ShaderType::NINE_PATCH_MASK_SHADER : VisualFactoryCache::ShaderType::NINE_PATCH_SHADER;
+  return SavePrecompileShader(shaderType, vertexShader, fragmentShader);
+}
+
+void NpatchShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
+{
+  std::vector<std::string_view> vertexPrefix;
+  std::vector<std::string_view> fragmentPrefix;
+  std::vector<std::string_view> shaderName;
+  int                           shaderCount = 0;
+  shaders.shaderCount                       = 0;
+
+  // precompile requested shader first
+  for(uint32_t i = 0; i < mRequestedPrecompileShader.size(); i++ )
+  {
+    vertexPrefix.push_back(mRequestedPrecompileShader[i].vertexPrefix);
+    fragmentPrefix.push_back(mRequestedPrecompileShader[i].fragmentPrefix);
+    shaderName.push_back(mRequestedPrecompileShader[i].name);
+    shaderCount++;
+  }
+
+  shaders.vertexPrefix   = std::move(vertexPrefix);
+  shaders.fragmentPrefix = std::move(fragmentPrefix);
+  shaders.shaderName     = std::move(shaderName);
+  shaders.vertexShader   = ""; // Custom shader use prefix shader only. No need to set vertexShader and fragmentShader.
+  shaders.fragmentShader = ""; // Custom shader use prefix shader only. No need to set vertexShader and fragmentShader.
+  shaders.shaderCount    = std::move(shaderCount);
+  shaders.custom = true;
+}
+
+void NpatchShaderFactory::GetVertexShader(std::string& vertexShader) const
+{
+  if(DALI_LIKELY((mNpatchXStretchCount == 1 && mNpatchYStretchCount == 1) ||
+                  (mNpatchXStretchCount == 0 && mNpatchYStretchCount == 0)))
+  {
+    vertexShader += SHADER_NPATCH_VISUAL_3X3_SHADER_VERT;
+  }
+  else if(mNpatchXStretchCount > 0 || mNpatchYStretchCount > 0)
+  {
+    std::stringstream vertextShaderStream;
+    vertextShaderStream << "#define FACTOR_SIZE_X " << mNpatchXStretchCount + 2 << "\n"
+                        << "#define FACTOR_SIZE_Y " << mNpatchYStretchCount + 2 << "\n"
+                        << SHADER_NPATCH_VISUAL_SHADER_VERT;
+    vertexShader += vertextShaderStream.str();
+  }
+}
+
+void NpatchShaderFactory::GetFragmentShader(std::string& fragmentShader) const
+{
+  fragmentShader += (mNpatchMaskingEnable ? SHADER_NPATCH_VISUAL_MASK_SHADER_FRAG : SHADER_NPATCH_VISUAL_SHADER_FRAG);
+}
+
+bool NpatchShaderFactory::SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexShader, std::string& fragmentShader)
+{
+  for(uint32_t i = 0u; i< mRequestedPrecompileShader.size(); i++)
+  {
+    if(mRequestedPrecompileShader[i].type == shader)
+    {
+      DALI_LOG_WARNING("This shader already requsted(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  std::string shaderName = Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(shader, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT);
+  if(!((mNpatchXStretchCount == 1 && mNpatchYStretchCount == 1) || (mNpatchXStretchCount == 0 && mNpatchYStretchCount == 0)))
+  {
+    if(mNpatchXStretchCount > 0 || mNpatchYStretchCount > 0)
+    {
+      std::stringstream shaderNameStream;
+      shaderNameStream << "NINE_PATCH_SHADER_" << mNpatchXStretchCount << "x" << mNpatchYStretchCount;
+      shaderName = shaderNameStream.str();
+    }
+  }
+
+  RequestShaderInfo info;
+  info.type = shader;
+  info.name = shaderName;
+  info.vertexPrefix = vertexShader;
+  info.fragmentPrefix = fragmentShader;
+  mRequestedPrecompileShader.push_back(info);
+  DALI_LOG_RELEASE_INFO("Add precompile shader success!!(%s)",Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(shader, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+  return true;
+}
+
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/npatch-shader-factory.h b/dali-toolkit/internal/visuals/npatch-shader-factory.h
new file mode 100644 (file)
index 0000000..c3c42f4
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef DALI_TOOLKIT_NPATCH_SHADER_FACTORY_H
+#define DALI_TOOLKIT_NPATCH_SHADER_FACTORY_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali-toolkit/internal/visuals/visual-shader-factory-interface.h>
+#include <string_view>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+/**
+ * NpatchShaderFactory is an object that provides custom shader
+ */
+class NpatchShaderFactory : public VisualShaderFactoryInterface
+{
+public:
+  /**
+   * @brief Constructor
+   */
+  NpatchShaderFactory();
+
+  /**
+   * @brief Destructor
+   */
+  ~NpatchShaderFactory() override;
+
+public: // Implementation of VisualShaderFactoryInterface
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::AddPrecompiledShader
+   */
+  bool AddPrecompiledShader(PrecompileShaderOption& option) override;
+
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::GetPreCompiledShader
+   */
+  void GetPreCompiledShader(RawShaderData& shaders) override;
+
+private:
+  /**
+   * @brief Get the NPatch vertex shader. this is used for generating pre-compiled shader.
+   */
+  void GetVertexShader(std::string& vertexShader) const;
+
+  /**
+   * @brief Get the NPatch fragment shader. this is used for generating pre-compiled shader
+   */
+  void GetFragmentShader(std::string& fragmentShader) const;
+
+  /**
+   * @brief Save the npatch shader
+   */
+  bool SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix);
+
+protected:
+  /**
+   * Undefined copy constructor.
+   */
+  NpatchShaderFactory(const NpatchShaderFactory&) = delete;
+
+  /**
+   * Undefined assignment operator.
+   */
+  NpatchShaderFactory& operator=(const NpatchShaderFactory& rhs) = delete;
+
+private:
+  // For Npatch
+  uint32_t mNpatchXStretchCount;
+  uint32_t mNpatchYStretchCount;
+  bool     mNpatchMaskingEnable;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_NPATCH_SHADER_FACTORY_H
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include <dali-toolkit/internal/visuals/npatch-data.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-data.h>
 
 // INTERNAL HEADERS
 #include <dali-toolkit/internal/visuals/rendering-addon.h>
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_NPATCH_DATA_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include <dali-toolkit/internal/visuals/npatch-loader.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-loader.h>
 
 // INTERNAL HEADERS
 #include <dali-toolkit/internal/visuals/rendering-addon.h>
@@ -51,7 +51,7 @@ NPatchLoader::~NPatchLoader()
 {
   if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
   {
-    Adaptor::Get().UnregisterProcessor(*this, true);
+    Adaptor::Get().UnregisterProcessorOnce(*this, true);
     mRemoveProcessorRegistered = false;
   }
 }
@@ -160,7 +160,7 @@ void NPatchLoader::RequestRemove(NPatchData::NPatchDataId id, TextureUploadObser
   if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
   {
     mRemoveProcessorRegistered = true;
-    Adaptor::Get().RegisterProcessor(*this, true);
+    Adaptor::Get().RegisterProcessorOnce(*this, true);
   }
 }
 
@@ -188,6 +188,8 @@ void NPatchLoader::Process(bool postProcessor)
     oss << "[" << mRemoveQueue.size() << "]";
   });
 
+  mRemoveProcessorRegistered = false;
+
   for(auto& iter : mRemoveQueue)
   {
     Remove(iter.first, iter.second);
@@ -195,12 +197,6 @@ void NPatchLoader::Process(bool postProcessor)
 
   mRemoveQueue.clear();
 
-  if(Adaptor::IsAvailable())
-  {
-    Adaptor::Get().UnregisterProcessor(*this, true);
-    mRemoveProcessorRegistered = false;
-  }
-
   DALI_TRACE_END(gTraceFilter, "DALI_NPATCH_LOADER_PROCESS_REMOVE_QUEUE");
 }
 
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_NPATCH_LOADER_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/utility/npatch-utilities.h>
 #include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
-#include <dali-toolkit/internal/visuals/npatch-data.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-data.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
 
 namespace Dali
@@ -153,7 +153,7 @@ private:
     NPatchInfo& operator=(const NPatchInfo& info) = delete; // Do not use copy assign
 
     NPatchDataPtr mData;
-    std::int16_t  mReferenceCount; ///< The number of N-patch visuals that use this data.
+    int32_t       mReferenceCount; ///< The number of N-patch visuals that use this data.
   };
 
   /**
index 1047eaf..cfb348a 100644 (file)
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/utility/npatch-helper.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
-#include <dali-toolkit/internal/visuals/npatch-loader.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-loader.h>
 #include <dali-toolkit/internal/visuals/rendering-addon.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
@@ -48,7 +49,7 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(5); // fixed(3),stretch,aux
+const int CUSTOM_PROPERTY_COUNT(6); // fixed(3),stretch,aux,pre-muliplied alpha
 }
 
 /////////////////NPatchVisual////////////////
@@ -93,6 +94,9 @@ void NPatchVisual::LoadImages()
                                ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
                                : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
+    // Register PREMULTIPLIED_ALPHA property here.
+    mPreMultipliedAlphaIndex = mImpl->mRenderer.RegisterUniqueProperty(mPreMultipliedAlphaIndex, PREMULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled() ? 1.0f : 0.0f);
+
     TextureManager::MaskingDataPointer maskingDataPtr       = nullptr;
     ImageAtlasManagerPtr               imageAtlasManagerPtr = nullptr;
 
@@ -286,8 +290,18 @@ void NPatchVisual::DoCreateInstancePropertyMap(Property::Map& map) const
   }
 }
 
+void NPatchVisual::EnablePreMultipliedAlpha(bool preMultiplied)
+{
+  if(mImpl->mRenderer && mPreMultipliedAlphaIndex != Property::INVALID_INDEX)
+  {
+    mImpl->mRenderer.SetProperty(mPreMultipliedAlphaIndex, preMultiplied ? 1.0f : 0.0f);
+  }
+
+  Visual::Base::EnablePreMultipliedAlpha(preMultiplied);
+}
+
 NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::N_PATCH),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::N_PATCH),
   mPlacementActor(),
   mLoader(factoryCache.GetNPatchLoader()),
   mImageVisualShaderFactory(shaderFactory),
@@ -297,6 +311,7 @@ NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFa
   mAuxiliaryTextureSet(),
   mAuxiliaryTextureId(TextureManager::INVALID_TEXTURE_ID),
   mAuxiliaryResourceStatus(Toolkit::Visual::ResourceStatus::PREPARING),
+  mPreMultipliedAlphaIndex(Property::INVALID_INDEX),
   mBorderOnly(false),
   mBorder(),
   mAuxiliaryImageAlpha(0.0f),
@@ -307,7 +322,7 @@ NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFa
 
 NPatchVisual::~NPatchVisual()
 {
-  if(Stage::IsInstalled())
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable()))
   {
     if(mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER)
     {
@@ -332,7 +347,7 @@ void NPatchVisual::OnInitialize()
 {
   // Get basic geometry and shader
   Geometry geometry                        = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
-  auto     imageVisualShaderFeatureBuilder = ImageVisualShaderFeatureBuilder();
+  auto     imageVisualShaderFeatureBuilder = ImageVisualShaderFeature::FeatureBuilder();
   Shader   shader                          = mImageVisualShaderFactory.GetShader(
     mFactoryCache,
     imageVisualShaderFeatureBuilder);
index c94e5b4..efb29c2 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_N_PATCH_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -99,6 +99,11 @@ public: // from Visual
    */
   void DoCreateInstancePropertyMap(Property::Map& map) const override;
 
+  /**
+   * @copydoc Visual::Base::EnablePreMultipliedAlpha
+   */
+  void EnablePreMultipliedAlpha(bool preMultiplied) override;
+
 protected:
   /**
    * @brief Constructor.
@@ -224,6 +229,7 @@ private:
   TextureSet                                mAuxiliaryTextureSet;     ///< TextureSet of the auxiliary mask image
   TextureManager::TextureId                 mAuxiliaryTextureId;      ///< id of the auxiliary mask image (from TextureManager)
   Toolkit::Visual::ResourceStatus           mAuxiliaryResourceStatus; ///< resource status for auxiliary mask image
+  Property::Index                           mPreMultipliedAlphaIndex; ///< Index of premultipliedAlpha uniform. Only be used for auxiliary image.
   bool                                      mBorderOnly;              ///< if only border is desired
   Rect<int>                                 mBorder;                  ///< The size of the border
   float                                     mAuxiliaryImageAlpha;     ///< The alpha value for the auxiliary image only
index 1afbfcd..f55415b 100644 (file)
@@ -100,7 +100,7 @@ PrimitiveVisualPtr PrimitiveVisual::New(VisualFactoryCache& factoryCache, const
 }
 
 PrimitiveVisual::PrimitiveVisual(VisualFactoryCache& factoryCache)
-: Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::PRIMITIVE),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::PRIMITIVE),
   mScaleDimensions(Vector3::ONE),
   mScaleTopRadius(DEFAULT_SCALE_TOP_RADIUS),
   mScaleBottomRadius(DEFAULT_SCALE_BOTTOM_RADIUS),
diff --git a/dali-toolkit/internal/visuals/svg/svg-loader-observer.cpp b/dali-toolkit/internal/visuals/svg/svg-loader-observer.cpp
new file mode 100644 (file)
index 0000000..71ceb1b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/svg/svg-loader-observer.h>
+
+namespace Dali::Toolkit::Internal
+{
+SvgLoaderObserver::~SvgLoaderObserver()
+{
+  if(!mLoadDestructionSignal.Empty())
+  {
+    mLoadDestructionSignal.Emit(this);
+  }
+  if(!mRasterizeDestructionSignal.Empty())
+  {
+    mRasterizeDestructionSignal.Emit(this);
+  }
+}
+} // namespace Dali::Toolkit::Internal
diff --git a/dali-toolkit/internal/visuals/svg/svg-loader-observer.h b/dali-toolkit/internal/visuals/svg/svg-loader-observer.h
new file mode 100644 (file)
index 0000000..869b4a3
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef DALI_TOOLKIT_SVG_UPLOAD_OBSERVER_H
+#define DALI_TOOLKIT_SVG_UPLOAD_OBSERVER_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/vector-image-renderer.h>
+#include <dali/public-api/rendering/texture-set.h>
+
+// INTERNAL INCLUDES
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+/**
+ * @brief Base class used to observe the load / rasterize status of a svg.
+ */
+class SvgLoaderObserver
+{
+public:
+  typedef Signal<void(SvgLoaderObserver*)> DestructionSignalType; ///< Signal prototype for the Destruction Signal.
+
+public:
+  /**
+   * @brief Constructor.
+   */
+  SvgLoaderObserver() = default;
+
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~SvgLoaderObserver();
+
+  /**
+   * @brief Returns the destruction signal for load.
+   * This is emitted when the observer is destroyed.
+   * This is used by the observer notifier to mark this observer as destroyed (IE. It no longer needs notifying).
+   */
+  DestructionSignalType& LoadDestructionSignal()
+  {
+    return mLoadDestructionSignal;
+  }
+
+  /**
+   * @brief Returns the destruction signal for rasterize.
+   * This is emitted when the observer is destroyed.
+   * This is used by the observer notifier to mark this observer as destroyed (IE. It no longer needs notifying).
+   */
+  DestructionSignalType& RasterizeDestructionSignal()
+  {
+    return mRasterizeDestructionSignal;
+  }
+
+public:
+  /**
+   * The action to be taken once the async load has finished.
+   * This should be overridden by the deriving class.
+   *
+   * @param[in] loadId Id of load request.
+   * @param[in] vectorImageRenderer Renderer class for svg image. It could be empty handle if rasterize failed.
+   */
+  virtual void LoadComplete(int32_t loadId, Dali::VectorImageRenderer vectorImageRenderer) = 0;
+
+  /**
+   * The action to be taken once the async rasterize has finished.
+   * This should be overridden by the deriving class.
+   *
+   * @param[in] rasterizeId Id of rasterize request.
+   * @param[in] textureSet Rasterize texture set. It could be empty handle if rasterize failed.
+   * @param[in] atlasRect The atlas rect of the rasterized image.
+   */
+  virtual void RasterizeComplete(int32_t rasterizeId, Dali::TextureSet textureSet, Vector4 atlasRect) = 0;
+
+private:
+  DestructionSignalType mLoadDestructionSignal;      ///< The destruction signal emitted when the observer is destroyed.
+  DestructionSignalType mRasterizeDestructionSignal; ///< The destruction signal emitted when the observer is destroyed.
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_SVG_UPLOAD_OBSERVER_H
diff --git a/dali-toolkit/internal/visuals/svg/svg-loader.cpp b/dali-toolkit/internal/visuals/svg/svg-loader.cpp
new file mode 100644 (file)
index 0000000..12d9596
--- /dev/null
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/svg/svg-loader.h>
+
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/texture-manager/texture-manager-impl.h> ///< for EncodedImageBuffer
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/svg/svg-task.h>
+#include <dali-toolkit/internal/visuals/svg/svg-visual.h>
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+
+// EXTERNAL HEADERS
+#include <dali/devel-api/common/hash.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
+#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false);
+
+#ifdef DEBUG_ENABLED
+Debug::Filter* gSvgLoaderLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_SVG_LOADER");
+
+// clang-format off
+#define GET_LOAD_STATE_STRING(loadState) \
+  loadState == SvgLoader::LoadState::NOT_STARTED   ? "NOT_STARTED"   : \
+  loadState == SvgLoader::LoadState::LOADING       ? "LOADING"       : \
+  loadState == SvgLoader::LoadState::LOAD_FINISHED ? "LOAD_FINISHED" : \
+  loadState == SvgLoader::LoadState::CANCELLED     ? "CANCELLED"     : \
+  loadState == SvgLoader::LoadState::LOAD_FAILED   ? "LOAD_FAILED"   : \
+                                                     "Unknown"
+
+#define GET_RASTERIZE_STATE_STRING(rasterizeState) \
+  rasterizeState == SvgLoader::RasterizeState::NOT_STARTED   ? "NOT_STARTED"   : \
+  rasterizeState == SvgLoader::RasterizeState::RASTERIZING   ? "RASTERIZING"   : \
+  rasterizeState == SvgLoader::RasterizeState::UPLOADED      ? "UPLOADED"      : \
+  rasterizeState == SvgLoader::RasterizeState::UPLOAD_FAILED ? "UPLOAD_FAILED" : \
+                                                               "Unknown"
+// clang-format on
+#endif
+
+constexpr Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+
+/**
+ * @brief Helper function to set rasterize info from PixelData.
+ *
+ * @param[in] visualFactoryCache The visual factory cache. It will be used to get atlas manager faster.
+ * @param[in] pixelData The rasterized pixel data.
+ * @param[out] rasterizeInfo The rasterize info.
+ */
+void SetTextureSetToRasterizeInfo(VisualFactoryCache* visualFactoryCache, const Dali::PixelData rasterizedPixelData, SvgLoader::SvgRasterizeInfo& rasterizeInfo)
+{
+  rasterizeInfo.mAtlasAttempted = false;
+  if(rasterizeInfo.mAttemptAtlasing)
+  {
+    // Try to use atlas attempt
+    if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && visualFactoryCache))
+    {
+      auto atlasManager = visualFactoryCache->GetAtlasManager();
+      if(atlasManager)
+      {
+        Vector4 atlasRect;
+        auto    textureSet = atlasManager->Add(atlasRect, rasterizedPixelData);
+        if(textureSet) // atlasing
+        {
+          rasterizeInfo.mTextureSet     = textureSet;
+          rasterizeInfo.mAtlasRect      = atlasRect;
+          rasterizeInfo.mAtlasAttempted = true;
+
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "  rasterizeId:%d atlasAttempted:%d atlasRect:(%f %f %f %f)\n", rasterizeInfo.mId, rasterizeInfo.mAtlasAttempted, rasterizeInfo.mAtlasRect.x, rasterizeInfo.mAtlasRect.y, rasterizeInfo.mAtlasRect.z, rasterizeInfo.mAtlasRect.w);
+        }
+      }
+    }
+  }
+
+  if(!rasterizeInfo.mAtlasAttempted)
+  {
+    // Atlas failed. Convert pixelData to texture.
+    Dali::Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888, rasterizedPixelData.GetWidth(), rasterizedPixelData.GetHeight());
+    texture.Upload(rasterizedPixelData);
+
+    rasterizeInfo.mTextureSet = TextureSet::New();
+    rasterizeInfo.mTextureSet.SetTexture(0u, texture);
+
+    rasterizeInfo.mAtlasRect = FULL_TEXTURE_RECT;
+
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "  rasterizeId:%d atlasAttempted:%d rasterizedPixelSize:(%ux%u)\n", rasterizeInfo.mId, rasterizeInfo.mAtlasAttempted, rasterizedPixelData.GetWidth(), rasterizedPixelData.GetHeight());
+  }
+
+  rasterizeInfo.mRasterizeState = SvgLoader::RasterizeState::UPLOADED;
+}
+
+/**
+ * @brief Helper function to create a textureSet and atlasRect from rasterize info.
+ *
+ * @param[in] rasterizeInfo The rasterize info.
+ * @param[out] textureSet The textureSet.
+ * @param[out] atlasRect The atlasRect.
+ */
+void GetTextureSetFromRasterizeInfo(const SvgLoader::SvgRasterizeInfo& rasterizeInfo, Dali::TextureSet& textureSet, Vector4& atlasRect)
+{
+  if(!rasterizeInfo.mAtlasAttempted)
+  {
+    atlasRect = FULL_TEXTURE_RECT;
+    if(DALI_LIKELY(rasterizeInfo.mTextureSet && rasterizeInfo.mTextureSet.GetTextureCount() > 0u))
+    {
+      auto texture = rasterizeInfo.mTextureSet.GetTexture(0u);
+      if(DALI_LIKELY(texture))
+      {
+        // Always create new TextureSet here, so we don't share same TextureSets for multiple visuals.
+        textureSet = Dali::TextureSet::New();
+        textureSet.SetTexture(0u, texture);
+      }
+    }
+  }
+  else
+  {
+    textureSet = rasterizeInfo.mTextureSet;
+    atlasRect  = rasterizeInfo.mAtlasRect;
+  }
+}
+
+} // Anonymous namespace
+
+SvgLoader::SvgLoader()
+: mFactoryCache(nullptr),
+  mCurrentSvgLoadId(0),
+  mCurrentSvgRasterizeId(0),
+  mLoadingQueueLoadId(SvgLoader::INVALID_SVG_LOAD_ID),
+  mRasterizingQueueRasterizeId(SvgLoader::INVALID_SVG_RASTERIZE_ID),
+  mRemoveProcessorRegistered(false)
+{
+}
+
+SvgLoader::~SvgLoader()
+{
+  if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
+  {
+    Adaptor::Get().UnregisterProcessorOnce(*this, true);
+    mRemoveProcessorRegistered = false;
+  }
+}
+
+SvgLoader::SvgLoadId SvgLoader::GenerateUniqueSvgLoadId()
+{
+  // Skip invalid id generation.
+  if(DALI_UNLIKELY(mCurrentSvgLoadId == SvgLoader::INVALID_SVG_LOAD_ID))
+  {
+    mCurrentSvgLoadId = 0;
+  }
+  return mCurrentSvgLoadId++;
+}
+
+SvgLoader::SvgRasterizeId SvgLoader::GenerateUniqueSvgRasterizeId()
+{
+  // Skip invalid id generation.
+  if(DALI_UNLIKELY(mCurrentSvgRasterizeId == SvgLoader::INVALID_SVG_RASTERIZE_ID))
+  {
+    mCurrentSvgRasterizeId = 0;
+  }
+  return mCurrentSvgRasterizeId++;
+}
+
+SvgLoader::SvgLoadId SvgLoader::Load(const VisualUrl& url, float dpi, SvgLoaderObserver* svgObserver, bool synchronousLoading)
+{
+  SvgLoadId loadId     = SvgLoader::INVALID_SVG_LOAD_ID;
+  auto      cacheIndex = FindCacheIndexFromLoadCache(url, dpi);
+
+  // Newly append cache now.
+  if(cacheIndex == SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    loadId     = GenerateUniqueSvgLoadId();
+    cacheIndex = static_cast<SvgCacheIndex>(static_cast<uint32_t>(mLoadCache.size()));
+    mLoadCache.push_back(SvgLoadInfo(loadId, url, dpi));
+
+    if(url.IsBufferResource())
+    {
+      // Make encoded image buffer url valid until this SvgLoadInfo alive.
+      if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && mFactoryCache))
+      {
+        auto& textureManager = mFactoryCache->GetTextureManager();
+        textureManager.UseExternalResource(url);
+      }
+    }
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Load( url=%s dpi=%f observer=%p ) New cached index:%d loadId@%d\n", url.GetUrl().c_str(), dpi, svgObserver, cacheIndex, loadId);
+  }
+  else
+  {
+    DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mLoadCache.size() && "Invalid cache index");
+    loadId = mLoadCache[cacheIndex].mId;
+    ++mLoadCache[cacheIndex].mReferenceCount;
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Load( url=%s dpi=%f observer=%p ) Using cached index:%d loadId@%d\n", url.GetUrl().c_str(), dpi, svgObserver, cacheIndex, loadId);
+  }
+
+  auto& loadInfo = mLoadCache[cacheIndex];
+
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Load info id:%d, state:%s, refCount=%d\n", loadInfo.mId, GET_LOAD_STATE_STRING(loadInfo.mLoadState), static_cast<int>(loadInfo.mReferenceCount));
+
+  switch(loadInfo.mLoadState)
+  {
+    case LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
+    case LoadState::NOT_STARTED:
+    {
+      if(synchronousLoading)
+      {
+        // Do not add observer for sync load case.
+        LoadSynchronously(loadInfo, svgObserver);
+      }
+      else
+      {
+        LoadOrQueue(loadInfo, svgObserver);
+      }
+      break;
+    }
+    case LoadState::LOAD_FINISHED:
+    {
+      if(synchronousLoading || (mLoadingQueueLoadId == SvgLoader::INVALID_SVG_LOAD_ID && mRasterizingQueueRasterizeId == SvgLoader::INVALID_SVG_RASTERIZE_ID))
+      {
+        // Already load finished. Notify observer.
+        if(svgObserver)
+        {
+          svgObserver->LoadComplete(loadId, loadInfo.mLoadState == LoadState::LOAD_FINISHED ? loadInfo.mVectorImageRenderer : Dali::VectorImageRenderer());
+        }
+      }
+      else
+      {
+        // We should not notify observer yet. Queue it.
+        if(svgObserver)
+        {
+          LoadOrQueue(loadInfo, svgObserver);
+        }
+      }
+      break;
+    }
+    case LoadState::CANCELLED:
+    {
+      // A cancelled svg hasn't finished loading yet. Treat as a loading svg
+      // (it's ref count has already been incremented, above)
+      loadInfo.mLoadState = LoadState::LOADING;
+      DALI_FALLTHROUGH;
+    }
+    case LoadState::LOADING:
+    {
+      if(synchronousLoading)
+      {
+        // Do not add observer for sync load case.
+        // And also, we should not notify another observers even if we have already loaded.
+        LoadSynchronously(loadInfo, svgObserver);
+      }
+      else
+      {
+        AddLoadObserver(loadInfo, svgObserver);
+      }
+      break;
+    }
+  }
+
+  return loadId;
+}
+
+SvgLoader::SvgRasterizeId SvgLoader::Rasterize(SvgLoadId loadId, uint32_t width, uint32_t height, bool attemptAtlasing, SvgLoaderObserver* svgObserver, bool synchronousLoading)
+{
+  if(loadId == SvgLoader::INVALID_SVG_LOAD_ID)
+  {
+    return SvgLoader::INVALID_SVG_RASTERIZE_ID;
+  }
+
+  SvgRasterizeId rasterizeId = SvgLoader::INVALID_SVG_RASTERIZE_ID;
+  auto           cacheIndex  = FindCacheIndexFromRasterizeCache(loadId, width, height, attemptAtlasing);
+
+  // Newly append cache now.
+  if(cacheIndex == SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    // Increase loadId reference first
+    // It would be decreased at rasterizate removal.
+    {
+      auto loadCacheIndex = GetCacheIndexFromLoadCacheById(loadId);
+      DALI_ASSERT_ALWAYS(loadCacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX && "Invalid cache index");
+      ++mLoadCache[loadCacheIndex].mReferenceCount;
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Rasterize( loadId=%d Size=%ux%u atlas=%d observer=%p ) Increase loadId loadState:%s, refCount=%d\n", loadId, width, height, attemptAtlasing, svgObserver, GET_LOAD_STATE_STRING(mLoadCache[loadCacheIndex].mLoadState), static_cast<int>(mLoadCache[loadCacheIndex].mReferenceCount));
+    }
+
+    rasterizeId = GenerateUniqueSvgRasterizeId();
+    cacheIndex  = static_cast<SvgCacheIndex>(mRasterizeCache.size());
+    mRasterizeCache.push_back(SvgRasterizeInfo(rasterizeId, loadId, width, height, attemptAtlasing));
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Rasterize( loadId=%d Size=%ux%u atlas=%d observer=%p ) New cached index:%d rasterizeId@%d\n", loadId, width, height, attemptAtlasing, svgObserver, cacheIndex, rasterizeId);
+  }
+  else
+  {
+    DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mRasterizeCache.size() && "Invalid cache index");
+    rasterizeId = mRasterizeCache[cacheIndex].mId;
+    ++mRasterizeCache[cacheIndex].mReferenceCount;
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Rasterize( loadId=%d Size=%ux%u atlas=%d observer=%p ) Use cached index:%d rasterizeId@%d\n", loadId, width, height, attemptAtlasing, svgObserver, cacheIndex, rasterizeId);
+  }
+
+  auto& rasterizeInfo = mRasterizeCache[cacheIndex];
+
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::Rasterize info id:%d, state:%s, refCount=%d\n", rasterizeInfo.mId, GET_RASTERIZE_STATE_STRING(rasterizeInfo.mRasterizeState), static_cast<int>(rasterizeInfo.mReferenceCount));
+
+  switch(rasterizeInfo.mRasterizeState)
+  {
+    case RasterizeState::UPLOAD_FAILED: // Failed notifies observer which then stops observing.
+    case RasterizeState::NOT_STARTED:
+    {
+      if(synchronousLoading)
+      {
+        // Do not add observer for sync load case.
+        RasterizeSynchronously(rasterizeInfo, svgObserver);
+      }
+      else
+      {
+        RasterizeOrQueue(rasterizeInfo, svgObserver);
+      }
+      break;
+    }
+    case RasterizeState::UPLOADED:
+    {
+      if(synchronousLoading || (mLoadingQueueLoadId == SvgLoader::INVALID_SVG_LOAD_ID && mRasterizingQueueRasterizeId == SvgLoader::INVALID_SVG_RASTERIZE_ID))
+      {
+        // Already upload finished. Notify observer.
+        if(svgObserver)
+        {
+          Dali::TextureSet textureSet;
+          Vector4          atlasRect = FULL_TEXTURE_RECT;
+          if(rasterizeInfo.mRasterizeState == RasterizeState::UPLOADED)
+          {
+            GetTextureSetFromRasterizeInfo(rasterizeInfo, textureSet, atlasRect);
+          }
+          svgObserver->RasterizeComplete(rasterizeId, textureSet, atlasRect);
+        }
+      }
+      else
+      {
+        // We should not notify observer yet. Queue it.
+        if(svgObserver)
+        {
+          RasterizeOrQueue(rasterizeInfo, svgObserver);
+        }
+      }
+      break;
+    }
+    case RasterizeState::RASTERIZING:
+    {
+      if(synchronousLoading)
+      {
+        // Do not add observer for sync load case.
+        // And also, we should not notify another observers even if we have already loaded.
+        RasterizeSynchronously(rasterizeInfo, svgObserver);
+      }
+      else
+      {
+        AddRasterizeObserver(rasterizeInfo, svgObserver);
+      }
+      break;
+    }
+  }
+
+  return rasterizeId;
+}
+
+void SvgLoader::RequestLoadRemove(SvgLoader::SvgLoadId loadId, SvgLoaderObserver* svgObserver)
+{
+  // Remove observer first
+  auto cacheIndex = GetCacheIndexFromLoadCacheById(loadId);
+  if(cacheIndex == SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    return;
+  }
+  DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mLoadCache.size() && "Invalid cache index");
+
+  auto& loadInfo = mLoadCache[cacheIndex];
+  RemoveLoadObserver(loadInfo, svgObserver);
+  mLoadRemoveQueue.push_back(loadId);
+
+  if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
+  {
+    mRemoveProcessorRegistered = true;
+    Adaptor::Get().RegisterProcessorOnce(*this, true);
+  }
+}
+
+void SvgLoader::RequestRasterizeRemove(SvgLoader::SvgRasterizeId rasterizeId, SvgLoaderObserver* svgObserver, bool removalSynchronously)
+{
+  // Remove observer first
+  auto cacheIndex = GetCacheIndexFromRasterizeCacheById(rasterizeId);
+  if(cacheIndex == SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    return;
+  }
+  DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mRasterizeCache.size() && "Invalid cache index");
+
+  auto& rasterizeInfo = mRasterizeCache[cacheIndex];
+  RemoveRasterizeObserver(rasterizeInfo, svgObserver);
+
+  // Should not remove rasterize info if we are in notify observing.
+  if(removalSynchronously && (mLoadingQueueLoadId == SvgLoader::INVALID_SVG_LOAD_ID && mRasterizingQueueRasterizeId == SvgLoader::INVALID_SVG_RASTERIZE_ID))
+  {
+    RemoveRasterize(rasterizeId);
+  }
+  else
+  {
+    mRasterizeRemoveQueue.push_back(rasterizeId);
+
+    if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
+    {
+      mRemoveProcessorRegistered = true;
+      Adaptor::Get().RegisterProcessorOnce(*this, true);
+    }
+  }
+}
+
+Dali::VectorImageRenderer SvgLoader::GetVectorImageRenderer(SvgLoader::SvgLoadId loadId) const
+{
+  auto cacheIndex = GetCacheIndexFromLoadCacheById(loadId);
+  if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mLoadCache.size() && "Invalid cache index");
+    return mLoadCache[cacheIndex].mVectorImageRenderer;
+  }
+  return Dali::VectorImageRenderer();
+}
+
+void SvgLoader::Process(bool postProcessor)
+{
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_SVG_LOADER_PROCESS_REMOVE_QUEUE", [&](std::ostringstream& oss) {
+    oss << "[r:" << mRasterizeRemoveQueue.size() << ", l:" << mLoadRemoveQueue.size() << "]";
+  });
+
+  mRemoveProcessorRegistered = false;
+
+  for(auto& iter : mRasterizeRemoveQueue)
+  {
+    RemoveRasterize(iter);
+  }
+  mRasterizeRemoveQueue.clear();
+
+  for(auto& iter : mLoadRemoveQueue)
+  {
+    RemoveLoad(iter);
+  }
+  mLoadRemoveQueue.clear();
+
+  DALI_TRACE_END(gTraceFilter, "DALI_SVG_LOADER_PROCESS_REMOVE_QUEUE");
+}
+
+SvgLoader::SvgCacheIndex SvgLoader::GetCacheIndexFromLoadCacheById(const SvgLoader::SvgLoadId loadId) const
+{
+  const uint32_t size = static_cast<uint32_t>(mLoadCache.size());
+
+  for(uint32_t i = 0; i < size; ++i)
+  {
+    if(mLoadCache[i].mId == loadId)
+    {
+      return static_cast<SvgCacheIndex>(i);
+    }
+  }
+  return SvgLoader::INVALID_SVG_CACHE_INDEX;
+}
+
+SvgLoader::SvgCacheIndex SvgLoader::GetCacheIndexFromRasterizeCacheById(const SvgLoader::SvgRasterizeId rasterizeId) const
+{
+  const uint32_t size = static_cast<uint32_t>(mRasterizeCache.size());
+
+  for(uint32_t i = 0; i < size; ++i)
+  {
+    if(mRasterizeCache[i].mId == rasterizeId)
+    {
+      return static_cast<SvgCacheIndex>(i);
+    }
+  }
+  return SvgLoader::INVALID_SVG_CACHE_INDEX;
+}
+
+SvgLoader::SvgCacheIndex SvgLoader::FindCacheIndexFromLoadCache(const VisualUrl& imageUrl, float dpi) const
+{
+  const uint32_t size = static_cast<uint32_t>(mLoadCache.size());
+
+  // TODO : Let we use hash in future. For now, just PoC
+
+  for(uint32_t i = 0; i < size; ++i)
+  {
+    if(mLoadCache[i].mImageUrl.GetUrl() == imageUrl.GetUrl() &&
+       Dali::Equals(mLoadCache[i].mDpi, dpi))
+    {
+      return static_cast<SvgCacheIndex>(i);
+    }
+  }
+  return SvgLoader::INVALID_SVG_CACHE_INDEX;
+}
+
+SvgLoader::SvgCacheIndex SvgLoader::FindCacheIndexFromRasterizeCache(const SvgLoadId loadId, uint32_t width, uint32_t height, bool attemptAtlasing) const
+{
+  const uint32_t size = static_cast<uint32_t>(mRasterizeCache.size());
+
+  // TODO : Let we use hash in future. For now, just PoC
+
+  for(uint32_t i = 0; i < size; ++i)
+  {
+    if(mRasterizeCache[i].mLoadId == loadId &&
+       mRasterizeCache[i].mWidth == width &&
+       mRasterizeCache[i].mHeight == height)
+    {
+      // 1. If attemptAtlasing is true, then rasterizeInfo.mAttemptAtlasing should be true. The atlas rect result can be different.
+      // 2. If attemptAtlasing is false, then rasterizeInfo.mAtlasAttempted should be false. (We can use attempt failed result even if mAttemptAtlasing is true.)
+      if((attemptAtlasing && mRasterizeCache[i].mAttemptAtlasing) ||
+         (!attemptAtlasing && !mRasterizeCache[i].mAtlasAttempted))
+      {
+        return static_cast<SvgCacheIndex>(i);
+      }
+    }
+  }
+  return SvgLoader::INVALID_SVG_CACHE_INDEX;
+}
+
+void SvgLoader::RemoveLoad(SvgLoader::SvgLoadId loadId)
+{
+  auto cacheIndex = GetCacheIndexFromLoadCacheById(loadId);
+  if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mLoadCache.size() && "Invalid cache index");
+
+    auto& loadInfo(mLoadCache[cacheIndex]);
+
+    --loadInfo.mReferenceCount;
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::RemoveLoad( url=%s dpi=%f ) cached index:%d loadId@%d, state:%s, refCount=%d\n", loadInfo.mImageUrl.GetUrl().c_str(), loadInfo.mDpi, cacheIndex, loadId, GET_LOAD_STATE_STRING(loadInfo.mLoadState), static_cast<int>(loadInfo.mReferenceCount));
+
+    if(loadInfo.mReferenceCount <= 0)
+    {
+      if(loadInfo.mLoadState == LoadState::LOADING)
+      {
+        // Keep the load info in the cache, but mark it as cancelled.
+        // It will be removed when async load completed.
+        loadInfo.mLoadState = LoadState::CANCELLED;
+      }
+      else
+      {
+        if(loadInfo.mImageUrl.IsBufferResource())
+        {
+          // Unreference image buffer url from texture manager.
+          if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && mFactoryCache))
+          {
+            auto& textureManager = mFactoryCache->GetTextureManager();
+            textureManager.RemoveEncodedImageBuffer(loadInfo.mImageUrl);
+          }
+        }
+
+        // Remove the load info from the cache.
+        // Swap last data of cacheContainer.
+        if(static_cast<std::size_t>(cacheIndex + 1) < mLoadCache.size())
+        {
+          // Swap the value between current data and last data.
+          std::swap(mLoadCache[cacheIndex], mLoadCache.back());
+        }
+
+        // Now we can assume that latest data should be removed. pop_back.
+        mLoadCache.pop_back();
+
+        // Now, loadInfo is invalid
+      }
+    }
+  }
+}
+
+void SvgLoader::RemoveRasterize(SvgLoader::SvgRasterizeId rasterizeId)
+{
+  auto cacheIndex = GetCacheIndexFromRasterizeCacheById(rasterizeId);
+  if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mRasterizeCache.size() && "Invalid cache index");
+
+    auto& rasterizeInfo(mRasterizeCache[cacheIndex]);
+
+    --rasterizeInfo.mReferenceCount;
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::RemoveRasterize( loadId=%d Size=%ux%u ) cached index:%d rasterizeId@%d, state:%s, refCount=%d\n", rasterizeInfo.mLoadId, rasterizeInfo.mWidth, rasterizeInfo.mHeight, cacheIndex, rasterizeId, GET_RASTERIZE_STATE_STRING(rasterizeInfo.mRasterizeState), static_cast<int>(rasterizeInfo.mReferenceCount));
+
+    if(rasterizeInfo.mReferenceCount <= 0)
+    {
+      // Reduce the reference count of LoadId first.
+      RemoveLoad(rasterizeInfo.mLoadId);
+
+      if(rasterizeInfo.mRasterizeState == RasterizeState::RASTERIZING && rasterizeInfo.mTask)
+      {
+        // Cancel rasterize task immediatly!
+        Dali::AsyncTaskManager::Get().RemoveTask(rasterizeInfo.mTask);
+      }
+
+      if(Dali::Adaptor::IsAvailable() && rasterizeInfo.mAtlasAttempted && mFactoryCache)
+      {
+        // Remove the atlas from the atlas manager.
+        auto atlasManager = mFactoryCache->GetAtlasManager();
+        if(atlasManager)
+        {
+          atlasManager->Remove(rasterizeInfo.mTextureSet, rasterizeInfo.mAtlasRect);
+        }
+      }
+
+      // Remove the rasterize info from the cache.
+      // Swap last data of cacheContainer.
+      if(static_cast<std::size_t>(cacheIndex + 1) < mRasterizeCache.size())
+      {
+        // Swap the value between current data and last data.
+        std::swap(mRasterizeCache[cacheIndex], mRasterizeCache.back());
+      }
+
+      // Now we can assume that latest data should be removed. pop_back.
+      mRasterizeCache.pop_back();
+
+      // Now, rasterize is invalid
+    }
+  }
+}
+
+// Internal Methods for Load
+
+void SvgLoader::LoadOrQueue(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver)
+{
+  if(mLoadingQueueLoadId != SvgLoader::INVALID_SVG_LOAD_ID || mRasterizingQueueRasterizeId != SvgLoader::INVALID_SVG_RASTERIZE_ID)
+  {
+    mLoadQueue.PushBack(LoadQueueElement(loadInfo.mId, svgObserver));
+    if(svgObserver)
+    {
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Load)Connect DestructionSignal to observer:%p\n", svgObserver);
+      svgObserver->LoadDestructionSignal().Connect(this, &SvgLoader::LoadObserverDestroyed);
+    }
+  }
+  else
+  {
+    LoadRequest(loadInfo, svgObserver);
+  }
+}
+
+void SvgLoader::LoadRequest(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::LoadRequest(): id:%d observer:%p\n", loadInfo.mId, svgObserver);
+  AddLoadObserver(loadInfo, svgObserver);
+  loadInfo.mLoadState = LoadState::LOADING;
+
+  EncodedImageBuffer encodedImageBuffer;
+  if(loadInfo.mImageUrl.IsBufferResource())
+  {
+    // Make encoded image buffer url valid until this SvgLoadInfo alive.
+    if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && mFactoryCache))
+    {
+      auto& textureManager = mFactoryCache->GetTextureManager();
+      encodedImageBuffer   = textureManager.GetEncodedImageBuffer(loadInfo.mImageUrl);
+    }
+  }
+
+  loadInfo.mTask = new SvgLoadingTask(loadInfo.mVectorImageRenderer, loadInfo.mId, loadInfo.mImageUrl, encodedImageBuffer, loadInfo.mDpi, MakeCallback(this, &SvgLoader::AsyncLoadComplete));
+
+  Dali::AsyncTaskManager::Get().AddTask(loadInfo.mTask);
+}
+
+void SvgLoader::LoadSynchronously(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::LoadSynchronously(): id:%d observer:%p\n", loadInfo.mId, svgObserver);
+
+  EncodedImageBuffer encodedImageBuffer;
+  if(loadInfo.mImageUrl.IsBufferResource())
+  {
+    // Make encoded image buffer url valid until this SvgLoadInfo alive.
+    if(DALI_LIKELY(Dali::Adaptor::IsAvailable() && mFactoryCache))
+    {
+      auto& textureManager = mFactoryCache->GetTextureManager();
+      encodedImageBuffer   = textureManager.GetEncodedImageBuffer(loadInfo.mImageUrl);
+    }
+  }
+
+  // Note, we will not store this task after this API called.
+  auto loadingTask = new SvgLoadingTask(loadInfo.mVectorImageRenderer, loadInfo.mId, loadInfo.mImageUrl, encodedImageBuffer, loadInfo.mDpi, nullptr);
+  loadingTask->Process();
+  if(!loadingTask->HasSucceeded())
+  {
+    loadInfo.mLoadState = LoadState::LOAD_FAILED;
+  }
+  else
+  {
+    loadInfo.mLoadState = LoadState::LOAD_FINISHED;
+  }
+
+  if(svgObserver)
+  {
+    svgObserver->LoadComplete(loadInfo.mId, loadInfo.mLoadState == LoadState::LOAD_FINISHED ? loadInfo.mVectorImageRenderer : Dali::VectorImageRenderer());
+  }
+}
+
+void SvgLoader::AddLoadObserver(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::AddLoadObserver(): id:%d observer:%p\n", loadInfo.mId, svgObserver);
+
+  if(svgObserver)
+  {
+    loadInfo.mObservers.PushBack(svgObserver);
+
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Load)Connect DestructionSignal to observer:%p\n", svgObserver);
+    svgObserver->LoadDestructionSignal().Connect(this, &SvgLoader::LoadObserverDestroyed);
+  }
+}
+
+void SvgLoader::RemoveLoadObserver(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver)
+{
+  if(svgObserver)
+  {
+    const auto iterEnd = loadInfo.mObservers.End();
+    const auto iter    = std::find(loadInfo.mObservers.Begin(), iterEnd, svgObserver);
+    if(iter != iterEnd)
+    {
+      // Disconnect and remove the observer.
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Load)Disconnect DestructionSignal to observer:%p\n", svgObserver);
+      svgObserver->LoadDestructionSignal().Disconnect(this, &SvgLoader::LoadObserverDestroyed);
+      loadInfo.mObservers.Erase(iter);
+    }
+    else
+    {
+      // Given loadId might exist at load queue.
+      // Remove observer from the LoadQueue
+      for(auto&& element : mLoadQueue)
+      {
+        if(element.first == loadInfo.mId && element.second == svgObserver)
+        {
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "Remove observer from load queue (loadId:%d, observer:%p)\n", element.first, element.second);
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Load)Disconnect DestructionSignal to observer:%p\n", svgObserver);
+          svgObserver->LoadDestructionSignal().Disconnect(this, &SvgLoader::LoadObserverDestroyed);
+          element.second = nullptr;
+          break;
+        }
+      }
+    }
+  }
+}
+
+void SvgLoader::ProcessLoadQueue()
+{
+  for(auto&& element : mLoadQueue)
+  {
+    if(element.first == SvgLoader::INVALID_SVG_LOAD_ID)
+    {
+      continue;
+    }
+
+    auto cacheIndex = GetCacheIndexFromLoadCacheById(element.first);
+    if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+    {
+      DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mLoadCache.size() && "Invalid cache index");
+
+      SvgLoadInfo& loadInfo(mLoadCache[cacheIndex]);
+      const auto   loadId      = element.first;
+      auto*        svgObserver = element.second;
+
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::ProcessLoadQueue() loadId=%d, observer=%p, cacheIndex=@%d, loadState:%s\n", loadId, svgObserver, cacheIndex, GET_LOAD_STATE_STRING(loadInfo.mLoadState));
+
+      if((loadInfo.mLoadState == LoadState::LOAD_FINISHED) ||
+         (loadInfo.mLoadState == LoadState::LOAD_FAILED))
+      {
+        if(svgObserver)
+        {
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Load)Disconnect DestructionSignal to observer:%p\n", svgObserver);
+          svgObserver->LoadDestructionSignal().Disconnect(this, &SvgLoader::LoadObserverDestroyed);
+
+          svgObserver->LoadComplete(loadId, loadInfo.mLoadState == LoadState::LOAD_FINISHED ? loadInfo.mVectorImageRenderer : Dali::VectorImageRenderer());
+        }
+      }
+      else if(loadInfo.mLoadState == LoadState::LOADING)
+      {
+        // Note : LOADING state texture cannot be queue.
+        // This case be occured when same load id are queue in mLoadQueue.
+        AddLoadObserver(loadInfo, svgObserver);
+      }
+      else
+      {
+        LoadRequest(loadInfo, svgObserver);
+      }
+    }
+  }
+
+  mLoadQueue.Clear();
+}
+
+void SvgLoader::NotifyLoadObservers(SvgLoader::SvgLoadInfo& loadInfo)
+{
+  SvgLoadId loadId = loadInfo.mId;
+
+  // Empty handle if load failed
+  Dali::VectorImageRenderer vectorImageRenderer = loadInfo.mLoadState == LoadState::LOAD_FINISHED ? loadInfo.mVectorImageRenderer : Dali::VectorImageRenderer();
+
+  // If there is an observer: Notify the load is complete, whether successful or not,
+  // and erase it from the list
+  SvgLoadInfo* info = &loadInfo;
+
+  mLoadingQueueLoadId = loadInfo.mId;
+
+  // Reverse observer list that we can pop_back the observer.
+  std::reverse(info->mObservers.Begin(), info->mObservers.End());
+
+  while(info->mObservers.Count())
+  {
+    SvgLoaderObserver* observer = *(info->mObservers.End() - 1u);
+
+    // During LoadComplete() a Control ResourceReady() signal is emitted.
+    // During that signal the app may add remove /add SvgLoad (e.g. via
+    // ImageViews).
+    // It is possible for observers to be removed from the observer list,
+    // and it is also possible for the mLoadCache to be modified,
+    // invalidating the reference to the SvgLoadInfo struct.
+    // Texture load requests for the same URL are deferred until the end of this
+    // method.
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::NotifyLoadObservers() observer:%p loadId:%d url:%s loadState:%s\n", observer, loadId, info->mImageUrl.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->mLoadState));
+
+    // It is possible for the observer to be deleted.
+    // Disconnect and remove the observer first.
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Load)Disconnect DestructionSignal to observer:%p\n", observer);
+    observer->LoadDestructionSignal().Disconnect(this, &SvgLoader::LoadObserverDestroyed);
+
+    info->mObservers.Erase(info->mObservers.End() - 1u);
+
+    observer->LoadComplete(loadId, vectorImageRenderer);
+
+    // Get the textureInfo from the container again as it may have been invalidated.
+    auto cacheIndex = GetCacheIndexFromLoadCacheById(loadId);
+    if(cacheIndex == SvgLoader::INVALID_SVG_CACHE_INDEX)
+    {
+      break; // load info has been removed - can stop.
+    }
+    DALI_ASSERT_ALWAYS(cacheIndex < mLoadCache.size() && "Invalid cache index");
+    info = &mLoadCache[cacheIndex];
+  }
+
+  mLoadingQueueLoadId = SvgLoader::INVALID_SVG_LOAD_ID;
+
+  ProcessLoadQueue();
+  ProcessRasterizeQueue();
+}
+
+// Internal Methods for Rasterize
+
+void SvgLoader::RasterizeOrQueue(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver)
+{
+  if(mLoadingQueueLoadId != SvgLoader::INVALID_SVG_LOAD_ID || mRasterizingQueueRasterizeId != SvgLoader::INVALID_SVG_RASTERIZE_ID)
+  {
+    mRasterizeQueue.PushBack(RasterizeQueueElement(rasterizeInfo.mId, svgObserver));
+    if(svgObserver)
+    {
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Rasterize)Connect DestructionSignal to observer:%p\n", svgObserver);
+      svgObserver->RasterizeDestructionSignal().Connect(this, &SvgLoader::RasterizeObserverDestroyed);
+    }
+  }
+  else
+  {
+    RasterizeRequest(rasterizeInfo, svgObserver);
+  }
+}
+
+void SvgLoader::RasterizeRequest(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::RasterizeRequest(): id:%d observer:%p\n", rasterizeInfo.mId, svgObserver);
+  AddRasterizeObserver(rasterizeInfo, svgObserver);
+  rasterizeInfo.mRasterizeState = RasterizeState::RASTERIZING;
+
+  auto vectorImageRenderer = GetVectorImageRenderer(rasterizeInfo.mLoadId);
+
+  auto rasterizingTask = new SvgRasterizingTask(vectorImageRenderer, rasterizeInfo.mId, rasterizeInfo.mWidth, rasterizeInfo.mHeight, MakeCallback(this, &SvgLoader::AsyncRasterizeComplete));
+#ifdef TRACE_ENABLED
+  {
+    auto loadCacheIndex = GetCacheIndexFromLoadCacheById(rasterizeInfo.mLoadId);
+    rasterizingTask->SetUrl(mLoadCache[loadCacheIndex].mImageUrl);
+  }
+#endif
+
+  // Keep SvgTask at info.
+  rasterizeInfo.mTask = std::move(rasterizingTask);
+
+  Dali::AsyncTaskManager::Get().AddTask(rasterizeInfo.mTask);
+}
+
+void SvgLoader::RasterizeSynchronously(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::RasterizeSynchronously(): id:%d observer:%p\n", rasterizeInfo.mId, svgObserver);
+
+  auto vectorImageRenderer = GetVectorImageRenderer(rasterizeInfo.mLoadId);
+
+  // Note, we will not store this task after this API called.
+  auto rasterizingTask = new SvgRasterizingTask(vectorImageRenderer, rasterizeInfo.mId, rasterizeInfo.mWidth, rasterizeInfo.mHeight, nullptr);
+#ifdef TRACE_ENABLED
+  {
+    auto loadCacheIndex = GetCacheIndexFromLoadCacheById(rasterizeInfo.mLoadId);
+    rasterizingTask->SetUrl(mLoadCache[loadCacheIndex].mImageUrl);
+  }
+#endif
+  rasterizingTask->Process();
+
+  PixelData rasterizedPixelData = rasterizingTask->GetPixelData();
+
+  if(!rasterizingTask->HasSucceeded() || !rasterizedPixelData)
+  {
+    rasterizeInfo.mRasterizeState = RasterizeState::UPLOAD_FAILED;
+  }
+  else if(rasterizeInfo.mRasterizeState != RasterizeState::UPLOADED) ///< Check it to avoid duplicate Upload call.
+  {
+    SetTextureSetToRasterizeInfo(mFactoryCache, rasterizedPixelData, rasterizeInfo);
+  }
+
+  if(svgObserver)
+  {
+    Dali::TextureSet textureSet;
+    Vector4          atlasRect = FULL_TEXTURE_RECT;
+    if(rasterizeInfo.mRasterizeState == RasterizeState::UPLOADED)
+    {
+      GetTextureSetFromRasterizeInfo(rasterizeInfo, textureSet, atlasRect);
+    }
+    svgObserver->RasterizeComplete(rasterizeInfo.mId, textureSet, atlasRect);
+  }
+}
+
+void SvgLoader::AddRasterizeObserver(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::AddRasterizeObserver(): id:%d observer:%p\n", rasterizeInfo.mId, svgObserver);
+
+  if(svgObserver)
+  {
+    rasterizeInfo.mObservers.PushBack(svgObserver);
+
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Rasterize)Connect DestructionSignal to observer:%p\n", svgObserver);
+    svgObserver->RasterizeDestructionSignal().Connect(this, &SvgLoader::RasterizeObserverDestroyed);
+  }
+}
+
+void SvgLoader::RemoveRasterizeObserver(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver)
+{
+  if(svgObserver)
+  {
+    const auto iterEnd = rasterizeInfo.mObservers.End();
+    const auto iter    = std::find(rasterizeInfo.mObservers.Begin(), iterEnd, svgObserver);
+    if(iter != iterEnd)
+    {
+      // Disconnect and remove the observer.
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Rasterize)Disconnect DestructionSignal to observer:%p\n", svgObserver);
+      svgObserver->RasterizeDestructionSignal().Disconnect(this, &SvgLoader::RasterizeObserverDestroyed);
+      rasterizeInfo.mObservers.Erase(iter);
+    }
+    else
+    {
+      // Given rasterizeId might exist at rasterize queue.
+      // Remove observer from the RasterizeQueue
+      for(auto&& element : mRasterizeQueue)
+      {
+        if(element.first == rasterizeInfo.mId && element.second == svgObserver)
+        {
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "Remove observer from rasterize queue (rasterizeId:%d, observer:%p)\n", element.first, element.second);
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Rasterize)Disconnect DestructionSignal to observer:%p\n", svgObserver);
+          svgObserver->RasterizeDestructionSignal().Disconnect(this, &SvgLoader::RasterizeObserverDestroyed);
+          element.second = nullptr;
+          break;
+        }
+      }
+    }
+  }
+}
+
+void SvgLoader::ProcessRasterizeQueue()
+{
+  for(auto&& element : mRasterizeQueue)
+  {
+    if(element.first == SvgLoader::INVALID_SVG_RASTERIZE_ID)
+    {
+      continue;
+    }
+
+    auto cacheIndex = GetCacheIndexFromRasterizeCacheById(element.first);
+    if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+    {
+      DALI_ASSERT_ALWAYS(static_cast<size_t>(cacheIndex) < mRasterizeCache.size() && "Invalid cache index");
+
+      SvgRasterizeInfo& rasterizeInfo(mRasterizeCache[cacheIndex]);
+      const auto        rasterizeId = element.first;
+      auto*             svgObserver = element.second;
+
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::General, "SvgLoader::ProcessRasterizeQueue() rasterizeId=%d, observer=%p, cacheIndex=@%d, rasterizeState:%s\n", rasterizeId, svgObserver, cacheIndex, GET_RASTERIZE_STATE_STRING(rasterizeInfo.mRasterizeState));
+
+      if((rasterizeInfo.mRasterizeState == RasterizeState::UPLOADED) ||
+         (rasterizeInfo.mRasterizeState == RasterizeState::UPLOAD_FAILED))
+      {
+        if(svgObserver)
+        {
+          DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Rasterize)Disconnect DestructionSignal to observer:%p\n", svgObserver);
+          svgObserver->RasterizeDestructionSignal().Disconnect(this, &SvgLoader::RasterizeObserverDestroyed);
+
+          Dali::TextureSet textureSet;
+          Vector4          atlasRect = FULL_TEXTURE_RECT;
+          if(rasterizeInfo.mRasterizeState == RasterizeState::UPLOADED)
+          {
+            GetTextureSetFromRasterizeInfo(rasterizeInfo, textureSet, atlasRect);
+          }
+          svgObserver->RasterizeComplete(rasterizeId, textureSet, atlasRect);
+        }
+      }
+      else if(rasterizeInfo.mRasterizeState == RasterizeState::RASTERIZING)
+      {
+        // Note : RASTERIZING state texture cannot be queue.
+        // This case be occured when same load id are queue in mRasterizeQueue.
+        AddRasterizeObserver(rasterizeInfo, svgObserver);
+      }
+      else
+      {
+        RasterizeRequest(rasterizeInfo, svgObserver);
+      }
+    }
+  }
+
+  mRasterizeQueue.Clear();
+}
+
+void SvgLoader::NotifyRasterizeObservers(SvgLoader::SvgRasterizeInfo& rasterizeInfo)
+{
+  SvgRasterizeId rasterizeId = rasterizeInfo.mId;
+
+  const bool rasterizationSuccess = rasterizeInfo.mRasterizeState == RasterizeState::UPLOADED;
+
+  // If there is an observer: Notify the load is complete, whether successful or not,
+  // and erase it from the list
+  SvgRasterizeInfo* info = &rasterizeInfo;
+
+  mRasterizingQueueRasterizeId = rasterizeInfo.mId;
+
+  // Reverse observer list that we can pop_back the observer.
+  std::reverse(info->mObservers.Begin(), info->mObservers.End());
+
+  while(info->mObservers.Count())
+  {
+    SvgLoaderObserver* observer = *(info->mObservers.End() - 1u);
+
+    // During LoadComplete() a Control ResourceReady() signal is emitted.
+    // During that signal the app may add remove /add SvgLoad (e.g. via
+    // ImageViews).
+    // It is possible for observers to be removed from the observer list,
+    // and it is also possible for the mLoadCache to be modified,
+    // invalidating the reference to the SvgRasterizeInfo struct.
+    // Texture load requests for the same URL are deferred until the end of this
+    // method.
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::NotifyRasterizeObservers() observer:%p rasterizeId:%d loaderId:%d atlasAttempted:%d Size:%ux%u rasterizestate:%s\n", observer, rasterizeId, info->mLoadId, info->mAtlasAttempted, info->mWidth, info->mHeight, GET_RASTERIZE_STATE_STRING(info->mRasterizeState));
+
+    // It is possible for the observer to be deleted.
+    // Disconnect and remove the observer first.
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "  (Rasterize)Disconnect DestructionSignal to observer:%p\n", observer);
+    observer->RasterizeDestructionSignal().Disconnect(this, &SvgLoader::RasterizeObserverDestroyed);
+
+    info->mObservers.Erase(info->mObservers.End() - 1u);
+
+    Dali::TextureSet textureSet;
+    Vector4          atlasRect = FULL_TEXTURE_RECT;
+    if(rasterizationSuccess)
+    {
+      GetTextureSetFromRasterizeInfo(rasterizeInfo, textureSet, atlasRect);
+    }
+
+    observer->RasterizeComplete(rasterizeId, textureSet, atlasRect);
+
+    // Get the textureInfo from the container again as it may have been invalidated.
+    auto cacheIndex = GetCacheIndexFromRasterizeCacheById(rasterizeId);
+    if(cacheIndex == SvgLoader::INVALID_SVG_CACHE_INDEX)
+    {
+      break; // load info has been removed - can stop.
+    }
+    DALI_ASSERT_ALWAYS(cacheIndex < mRasterizeCache.size() && "Invalid cache index");
+    info = &mRasterizeCache[cacheIndex];
+  }
+
+  mRasterizingQueueRasterizeId = SvgLoader::INVALID_SVG_RASTERIZE_ID;
+
+  ProcessLoadQueue();
+  ProcessRasterizeQueue();
+}
+
+/// From SvgLoadingTask
+void SvgLoader::AsyncLoadComplete(SvgTaskPtr task)
+{
+  auto loadId     = task->GetId();
+  auto cacheIndex = GetCacheIndexFromLoadCacheById(loadId);
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::AsyncLoadComplete( loadId:%d CacheIndex:%u )\n", loadId, cacheIndex);
+  if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    DALI_ASSERT_ALWAYS(cacheIndex < mLoadCache.size() && "Invalid cache index");
+
+    SvgLoadInfo& loadInfo(mLoadCache[cacheIndex]);
+
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "  loadId:%d Url:%s CacheIndex:%d LoadState: %s\n", loadInfo.mId, loadInfo.mImageUrl.GetUrl().c_str(), cacheIndex, GET_LOAD_STATE_STRING(loadInfo.mLoadState));
+
+    if(loadInfo.mTask == task)
+    {
+      loadInfo.mTask.Reset();
+    }
+
+    if(loadInfo.mLoadState == LoadState::CANCELLED)
+    {
+      // Note : loadInfo can be invalidated after this call (as the mLoadCache may be modified)
+      RemoveLoad(loadId);
+    }
+    else
+    {
+      if(!task->HasSucceeded())
+      {
+        loadInfo.mLoadState = LoadState::LOAD_FAILED;
+      }
+      else
+      {
+        loadInfo.mLoadState = LoadState::LOAD_FINISHED;
+      }
+
+      // Note : loadInfo can be invalidated after this call (as the mLoadCache may be modified)
+      NotifyLoadObservers(loadInfo);
+    }
+  }
+}
+
+/// From SvgRasterizingTask
+void SvgLoader::AsyncRasterizeComplete(SvgTaskPtr task)
+{
+  auto rasterizeId = task->GetId();
+  auto cacheIndex  = GetCacheIndexFromRasterizeCacheById(rasterizeId);
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::AsyncRasterizeComplete( rasterizedId:%d CacheIndex:%u )\n", rasterizeId, cacheIndex);
+  if(cacheIndex != SvgLoader::INVALID_SVG_CACHE_INDEX)
+  {
+    DALI_ASSERT_ALWAYS(cacheIndex < mRasterizeCache.size() && "Invalid cache index");
+
+    SvgRasterizeInfo& rasterizeInfo(mRasterizeCache[cacheIndex]);
+
+    DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "  rasterizeId:%d loadId:%d attemptAtlasing:%d Size:%ux%u CacheIndex:%d RasterizeState: %s\n", rasterizeInfo.mId, rasterizeInfo.mLoadId, rasterizeInfo.mAttemptAtlasing, rasterizeInfo.mWidth, rasterizeInfo.mHeight, cacheIndex, GET_RASTERIZE_STATE_STRING(rasterizeInfo.mRasterizeState));
+
+    if(rasterizeInfo.mTask == task)
+    {
+      rasterizeInfo.mTask.Reset();
+    }
+
+    PixelData rasterizedPixelData = task->GetPixelData();
+
+    if(!task->HasSucceeded() || !rasterizedPixelData)
+    {
+      rasterizeInfo.mRasterizeState = RasterizeState::UPLOAD_FAILED;
+    }
+    else if(rasterizeInfo.mRasterizeState != RasterizeState::UPLOADED) ///< Check it to avoid duplicate Upload call. (e.g. sync rasterize)
+    {
+      SetTextureSetToRasterizeInfo(mFactoryCache, rasterizedPixelData, rasterizeInfo);
+    }
+
+    // Note : rasterizeInfo can be invalidated after this call (as the mRasterizeCache may be modified)
+    NotifyRasterizeObservers(rasterizeInfo);
+  }
+}
+
+/// From ~SvgVisual
+void SvgLoader::LoadObserverDestroyed(SvgLoaderObserver* observer)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::LoadObserverDestroyed(): observer:%p\n", observer);
+
+  for(auto&& loadInfo : mLoadCache)
+  {
+    for(auto iter = (loadInfo.mObservers.Begin()); iter != (loadInfo.mObservers.End());)
+    {
+      if(*iter == observer)
+      {
+        DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "  (Load)Remove observer from LoadCache (id:%d, observer:%p)\n", loadInfo.mId, observer);
+        iter = loadInfo.mObservers.Erase(iter);
+      }
+      else
+      {
+        ++iter;
+      }
+    }
+  }
+
+  // Remove element from the LoadQueue
+  for(auto&& element : mLoadQueue)
+  {
+    if(element.second == observer)
+    {
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "Remove observer from load queue (loadId:%d, observer:%p)\n", element.first, element.second);
+      element.first  = SvgLoader::INVALID_SVG_LOAD_ID;
+      element.second = nullptr;
+    }
+  }
+}
+
+void SvgLoader::RasterizeObserverDestroyed(SvgLoaderObserver* observer)
+{
+  DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "SvgLoader::RasterizeObserverDestroyed(): observer:%p\n", observer);
+
+  for(auto&& rasterizeInfo : mRasterizeCache)
+  {
+    for(auto iter = (rasterizeInfo.mObservers.Begin()); iter != (rasterizeInfo.mObservers.End());)
+    {
+      if(*iter == observer)
+      {
+        DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Concise, "  (Rasterize)Remove observer from RasterizeCache (id:%d, observer:%p)\n", rasterizeInfo.mId, observer);
+        iter = rasterizeInfo.mObservers.Erase(iter);
+      }
+      else
+      {
+        ++iter;
+      }
+    }
+  }
+
+  // Remove element from the RasterizeQueue
+  for(auto&& element : mRasterizeQueue)
+  {
+    if(element.second == observer)
+    {
+      DALI_LOG_INFO(gSvgLoaderLogFilter, Debug::Verbose, "Remove observer from rasterize queue (rasterizeId:%d, observer:%p)\n", element.first, element.second);
+      element.first  = SvgLoader::INVALID_SVG_RASTERIZE_ID;
+      element.second = nullptr;
+    }
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/svg/svg-loader.h b/dali-toolkit/internal/visuals/svg/svg-loader.h
new file mode 100644 (file)
index 0000000..0810514
--- /dev/null
@@ -0,0 +1,480 @@
+#ifndef DALI_TOOLKIT_SVG_LOADER_H
+#define DALI_TOOLKIT_SVG_LOADER_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/vector-image-renderer.h>
+#include <dali/integration-api/processor-interface.h>
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/rendering/texture-set.h>
+#include <string>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/svg/svg-loader-observer.h>
+#include <dali-toolkit/internal/visuals/svg/svg-task.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class VisualFactoryCache;
+
+/**
+ * The manager for loading Svg textures.
+ * It caches them internally for better performance; i.e. to avoid loading and
+ * parsing the files over and over.
+ *
+ * @note To use EncodedImageBuffer and AtlasManager, we need to set VisualFactoryCache.
+ */
+class SvgLoader : public ConnectionTracker, public Integration::Processor
+{
+public:
+  typedef int32_t SvgLoadId;      ///< The SvgLoadId type. This is used as a handle to refer to a particular SvgLoader Data.
+  typedef int32_t SvgRasterizeId; ///< The SvgRasterizeId type. This is used as a handle to refer to a particular SvgLoader Data.
+
+  static constexpr SvgLoadId      INVALID_SVG_LOAD_ID      = -1; ///< Used to represent a null SvgLoadId or error
+  static constexpr SvgRasterizeId INVALID_SVG_RASTERIZE_ID = -1; ///< Used to represent a null SvgRasterizeId or error
+
+  /**
+   * @brief The LoadState Enumeration represents the current state of a particular Svg data's life-cycle.
+   */
+  enum class LoadState
+  {
+    NOT_STARTED,   ///< Default
+    LOADING,       ///< Loading has been started, but not finished.
+    LOAD_FINISHED, ///< Loading has finished.
+    CANCELLED,     ///< Removed before loading completed
+    LOAD_FAILED    ///< Async loading failed, e.g. connection problem
+  };
+
+  /**
+   * @brief The RasterizeState Enumeration represents the current state of a particular Svg data's life-cycle.
+   */
+  enum class RasterizeState
+  {
+    NOT_STARTED,  ///< Default
+    RASTERIZING,  ///< Rasterizing has been started, but not finished.
+    UPLOADED,     ///< Upload has finished.
+    UPLOAD_FAILED ///< Async rasterizing failed, e.g. connection problem
+  };
+
+private:
+  typedef uint32_t               SvgCacheIndex; ///< The Cache index type. Only be used internally
+  static constexpr SvgCacheIndex INVALID_SVG_CACHE_INDEX = static_cast<SvgCacheIndex>(std::numeric_limits<SvgCacheIndex>::max());
+
+  using ObserverContainer = Dali::Vector<SvgLoaderObserver*>;
+
+public:
+  /**
+   * Constructor
+   */
+  SvgLoader();
+
+  /**
+   * Destructor, non-virtual as not a base class
+   */
+  ~SvgLoader();
+
+  /**
+   * @brief Request to load an SVG file.
+   * It will notify SvgLoaderObserver->LoadComplete before we call RequestLoadRemove.
+   * @note Even if synchronousLoading is true, we notify observer always.
+   * @note If we meat cached svg load, notify LoadComplete first, and then API function finished.
+   *
+   * @param[in] url to retrieve
+   * @param[in] dpi The DPI of the screen.
+   * @param[in] svgObserver The SvgVisual that requested loading.
+   * @param[in] synchronousLoading True if the image will be loaded in synchronous time.
+   * @return id of the load request.
+   */
+  SvgLoadId Load(const VisualUrl& url, float dpi, SvgLoaderObserver* svgObserver, bool synchronousLoading);
+
+  /**
+   * @brief Request to rasterize an SVG file.
+   * It will notify SvgLoaderObserver->RasterizeComplete before we call RequestRasterizeRemove.
+   * @note Even if synchronousLoading is true, we notify observer always.
+   * @note If we meat cached svg rasterize, notify RasterizeComplete first, and then API function finished.
+   *
+   * @todo RasterizeComplete will not be called forever if loading failed. Should we need to support it?
+   *
+   * @param[in] loaderId to retrieve
+   * @param[in] width The rasterization width.
+   * @param[in] height The rasterization height.
+   * @param[in] attemptAtlasing True if we need to attempt atlas the image.
+   * @param[in] svgObserver The SvgVisual that requested loading.
+   * @param[in] synchronousLoading True if the image will be loaded in synchronous time.
+   * @return id of the rasterize request.
+   */
+  SvgRasterizeId Rasterize(SvgLoadId loaderId, uint32_t width, uint32_t height, bool attemptAtlasing, SvgLoaderObserver* svgObserver, bool synchronousLoading);
+
+  /**
+   * @brief Request remove a texture matching id.
+   * Erase the observer from the observer list of cache if we need.
+   *
+   * @param[in] loadId cache data id
+   * @param[in] svgObserver The SvgVisual that requested loading.
+   */
+  void RequestLoadRemove(SvgLoadId loadId, SvgLoaderObserver* svgObserver);
+
+  /**
+   * @brief Request remove a texture matching id.
+   * Erase the observer from the observer list of cache if we need.
+   *
+   * @param[in] rasterizeId cache data id
+   * @param[in] svgObserver The SvgVisual that requested loading.
+   * @param[in] removalSynchronously Whether the removal should be done synchronously, or not.
+   *                                 It will be true when we want to ignore unused rasterize task.
+   */
+  void RequestRasterizeRemove(SvgRasterizeId rasterizeId, SvgLoaderObserver* svgObserver, bool removalSynchronously);
+
+  /**
+   * @brief Get the VectorImageRenderer of matching loadId.
+   *
+   * @param[in] loadId cache data id
+   * @return The vector image renderer of matching loadId.
+   */
+  VectorImageRenderer GetVectorImageRenderer(SvgLoadId loadId) const;
+
+protected: // Implementation of Processor
+  /**
+   * @copydoc Dali::Integration::Processor::Process()
+   */
+  void Process(bool postProcessor) override;
+
+  /**
+   * @copydoc Dali::Integration::Processor::GetProcessorName()
+   */
+  std::string_view GetProcessorName() const override
+  {
+    return "SvgLoader";
+  }
+
+private:
+  SvgLoadId GenerateUniqueSvgLoadId();
+
+  SvgRasterizeId GenerateUniqueSvgRasterizeId();
+
+  SvgCacheIndex GetCacheIndexFromLoadCacheById(const SvgLoadId loadId) const;
+
+  SvgCacheIndex GetCacheIndexFromRasterizeCacheById(const SvgRasterizeId rasterizeId) const;
+
+  SvgCacheIndex FindCacheIndexFromLoadCache(const VisualUrl& imageUrl, float dpi) const;
+
+  SvgCacheIndex FindCacheIndexFromRasterizeCache(const SvgLoadId loadId, uint32_t width, uint32_t height, bool attemptAtlasing) const;
+
+  /**
+   * @brief Remove a texture matching id.
+   * Erase the observer from the observer list of cache if we need.
+   * This API decrease cached SvgLoadInfo reference.
+   * If the SvgLoadInfo reference become 0, the VectorImageRenderer will be reset.
+   *
+   * @param[in] loadId cache data id
+   */
+  void RemoveLoad(SvgLoadId loadId);
+
+  /**
+   * @brief Remove a texture matching id.
+   * Erase the observer from the observer list of cache if we need.
+   * This API decrease cached SvgRasterizeInfo reference.
+   * If the SvgRasterizeInfo reference become 0, the textureSet will be reset.
+   *
+   * @param[in] rasterizeId cache data id
+   */
+  void RemoveRasterize(SvgRasterizeId rasterizeId);
+
+public:
+  /**
+   * @brief Information of Svg image load data
+   */
+  struct SvgLoadInfo
+  {
+    SvgLoadInfo(SvgLoadId loadId, const VisualUrl& url, float dpi)
+    : mId(loadId),
+      mTask(),
+      mImageUrl(url),
+      mDpi(dpi),
+      mLoadState(LoadState::NOT_STARTED),
+      mVectorImageRenderer(Dali::VectorImageRenderer::New()),
+      mObservers(),
+      mReferenceCount(1u)
+    {
+    }
+    ~SvgLoadInfo()
+    {
+    }
+
+    SvgLoadInfo(SvgLoadInfo&& info) noexcept // move constructor
+    : mId(info.mId),
+      mTask(std::move(info.mTask)),
+      mImageUrl(std::move(info.mImageUrl)),
+      mDpi(info.mDpi),
+      mLoadState(info.mLoadState),
+      mVectorImageRenderer(std::move(info.mVectorImageRenderer)),
+      mObservers(std::move(info.mObservers)),
+      mReferenceCount(info.mReferenceCount)
+    {
+      info.mTask.Reset();
+      info.mVectorImageRenderer.Reset();
+      info.mReferenceCount = 0;
+    }
+    SvgLoadInfo& operator=(SvgLoadInfo&& info) noexcept // move operator
+    {
+      if(this != &info)
+      {
+        mId   = info.mId;
+        mTask = std::move(info.mTask);
+
+        mImageUrl = std::move(info.mImageUrl);
+
+        mDpi = info.mDpi;
+
+        mLoadState           = info.mLoadState;
+        mVectorImageRenderer = std::move(info.mVectorImageRenderer);
+
+        mObservers = std::move(info.mObservers);
+
+        mReferenceCount = info.mReferenceCount;
+
+        info.mTask.Reset();
+        info.mVectorImageRenderer.Reset();
+        info.mReferenceCount = 0;
+      }
+      return *this;
+    }
+
+  private:
+    SvgLoadInfo()                        = delete;            // Do not use empty constructor
+    SvgLoadInfo(const SvgLoadInfo& info) = delete;            // Do not use copy constructor
+    SvgLoadInfo& operator=(const SvgLoadInfo& info) = delete; // Do not use copy assign
+
+  public:
+    SvgLoadId  mId;
+    SvgTaskPtr mTask; ///< Async task. It would be deleted when loading completed.
+
+    VisualUrl mImageUrl;
+    float     mDpi;
+
+    LoadState           mLoadState;
+    VectorImageRenderer mVectorImageRenderer;
+
+    ObserverContainer mObservers;
+
+    int32_t mReferenceCount; ///< The number of Svg visuals that use this data.
+  };
+
+  /**
+   * @brief Information of Svg image rasterize data
+   */
+  struct SvgRasterizeInfo
+  {
+    SvgRasterizeInfo(SvgRasterizeId rasterizeId, SvgLoadId loadId, uint32_t width, uint32_t height, bool attemptAtlasing)
+    : mId(rasterizeId),
+      mTask(),
+      mLoadId(loadId),
+      mWidth(width),
+      mHeight(height),
+      mAttemptAtlasing(attemptAtlasing),
+      mRasterizeState(RasterizeState::NOT_STARTED),
+      mTextureSet(),
+      mAtlasRect(Vector4(0.0f, 0.0f, 1.0f, 1.0f)),
+      mAtlasAttempted(attemptAtlasing), ///< Let we assume that atlas attempted until rasterize completed.
+      mObservers(),
+      mReferenceCount(1u)
+    {
+    }
+    ~SvgRasterizeInfo()
+    {
+    }
+    SvgRasterizeInfo(SvgRasterizeInfo&& info) noexcept // move constructor
+    : mId(info.mId),
+      mTask(std::move(info.mTask)),
+      mLoadId(info.mLoadId),
+      mWidth(info.mWidth),
+      mHeight(info.mHeight),
+      mAttemptAtlasing(info.mAttemptAtlasing),
+      mRasterizeState(info.mRasterizeState),
+      mTextureSet(std::move(info.mTextureSet)),
+      mAtlasRect(std::move(info.mAtlasRect)),
+      mAtlasAttempted(info.mAtlasAttempted),
+      mObservers(std::move(info.mObservers)),
+      mReferenceCount(info.mReferenceCount)
+    {
+      info.mTask.Reset();
+      info.mTextureSet.Reset();
+      info.mReferenceCount = 0;
+    }
+    SvgRasterizeInfo& operator=(SvgRasterizeInfo&& info) noexcept // move operator
+    {
+      if(this != &info)
+      {
+        mId   = info.mId;
+        mTask = std::move(info.mTask);
+
+        mLoadId          = info.mLoadId;
+        mWidth           = info.mWidth;
+        mHeight          = info.mHeight;
+        mAttemptAtlasing = info.mAttemptAtlasing;
+
+        mRasterizeState = info.mRasterizeState;
+        mTextureSet     = std::move(info.mTextureSet);
+        mAtlasRect      = std::move(info.mAtlasRect);
+        mAtlasAttempted = info.mAtlasAttempted;
+
+        mObservers = std::move(info.mObservers);
+
+        mReferenceCount = info.mReferenceCount;
+
+        info.mTask.Reset();
+        info.mTextureSet.Reset();
+        info.mReferenceCount = 0;
+      }
+      return *this;
+    }
+
+  private:
+    SvgRasterizeInfo()                             = delete;            // Do not use empty constructor
+    SvgRasterizeInfo(const SvgRasterizeInfo& info) = delete;            // Do not use copy constructor
+    SvgRasterizeInfo& operator=(const SvgRasterizeInfo& info) = delete; // Do not use copy assign
+
+  public:
+    SvgRasterizeId mId;
+    SvgTaskPtr     mTask; ///< Async task. It would be deleted when rasterizing completed.
+
+    SvgLoadId mLoadId;
+    uint32_t  mWidth;
+    uint32_t  mHeight;
+    bool      mAttemptAtlasing; ///< True if atlas requested.
+
+    RasterizeState   mRasterizeState;
+    Dali::TextureSet mTextureSet; ///< The texture set from atlas manager, or rasterized result at index 0.
+    Vector4          mAtlasRect;
+    bool             mAtlasAttempted; ///< True if atlasing attempted. False if atlasing failed
+
+    ObserverContainer mObservers;
+
+    int32_t mReferenceCount; ///< The number of Svg visuals that use this data.
+  };
+
+private: ///< Internal Methods for load
+  void LoadOrQueue(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver);
+  void LoadRequest(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver);
+  void LoadSynchronously(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver);
+  void AddLoadObserver(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver);
+  void RemoveLoadObserver(SvgLoader::SvgLoadInfo& loadInfo, SvgLoaderObserver* svgObserver);
+  void ProcessLoadQueue();
+
+  /**
+   * Notify the current observers that the svg load is complete,
+   * then remove the observers from the list.
+   * @param[in] loadInfo The load info.
+   */
+  void NotifyLoadObservers(SvgLoader::SvgLoadInfo& loadInfo);
+
+private: ///< Internal Methods for rasterize
+  void RasterizeOrQueue(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver);
+  void RasterizeRequest(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver);
+  void RasterizeSynchronously(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver);
+  void AddRasterizeObserver(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver);
+  void RemoveRasterizeObserver(SvgLoader::SvgRasterizeInfo& rasterizeInfo, SvgLoaderObserver* svgObserver);
+  void ProcessRasterizeQueue();
+
+  /**
+   * Notify the current observers that the svg rasterize is complete,
+   * then remove the observers from the list.
+   * @param[in] rasterizeInfo The rasterize info.
+   */
+  void NotifyRasterizeObservers(SvgLoader::SvgRasterizeInfo& rasterizeInfo);
+
+public: ///< Methods for the VisualFactoryCache.
+  void SetVisualFactoryCache(VisualFactoryCache& factoryCache)
+  {
+    mFactoryCache = &factoryCache;
+  }
+
+private:
+  /**
+   * @brief Common method to handle loading completion.
+   * @param[in] task The task that has completed.
+   */
+  void AsyncLoadComplete(SvgTaskPtr task);
+
+  /**
+   * @brief Common method to handle rasterizing completion.
+   * @param[in] task The task that has completed.
+   */
+  void AsyncRasterizeComplete(SvgTaskPtr task);
+
+  /**
+   * This is called by the SvgLoaderObserver when an observer is destroyed during loading.
+   * We use the callback to know when to remove an observer from our notify list.
+   * @param[in] observer The observer that generated the callback
+   */
+  void LoadObserverDestroyed(SvgLoaderObserver* svgObserver);
+
+  /**
+   * This is called by the SvgLoaderObserver when an observer is destroyed during rasterizing.
+   * We use the callback to know when to remove an observer from our notify list.
+   * @param[in] observer The observer that generated the callback
+   */
+  void RasterizeObserverDestroyed(SvgLoaderObserver* svgObserver);
+
+protected:
+  /**
+   * Undefined copy constructor.
+   */
+  SvgLoader(const SvgLoader&) = delete;
+
+  /**
+   * Undefined assignment operator.
+   */
+  SvgLoader& operator=(const SvgLoader& rhs) = delete;
+
+private:
+  VisualFactoryCache* mFactoryCache; ///< The holder of visual factory cache. It will be used when we want to get atlas manager;
+
+  SvgLoadId      mCurrentSvgLoadId;
+  SvgRasterizeId mCurrentSvgRasterizeId;
+
+  std::vector<SvgLoader::SvgLoadInfo>      mLoadCache{};
+  std::vector<SvgLoader::SvgRasterizeInfo> mRasterizeCache{};
+
+  using LoadQueueElement = std::pair<SvgLoadId, SvgLoaderObserver*>;
+  Dali::Vector<LoadQueueElement> mLoadQueue{};        ///< Queue of svg load after NotifyLoadObservers
+  SvgLoadId                      mLoadingQueueLoadId; ///< SvgLoadId when it is loading. it causes Load SVG to be queued.
+
+  using RasterizeQueueElement = std::pair<SvgRasterizeId, SvgLoaderObserver*>;
+  Dali::Vector<RasterizeQueueElement> mRasterizeQueue{};            ///< Queue of svg rasterze after NotifyRasterizeObservers
+  SvgRasterizeId                      mRasterizingQueueRasterizeId; ///< SvgRasterizeId when it is rasterizing. it causes Rasterize SVG to be queued.
+
+  std::vector<SvgLoadId>      mLoadRemoveQueue{};      ///< Queue of SvgLoader::SvgLoadInfo to remove at PostProcess. It will be cleared after PostProcess.
+  std::vector<SvgRasterizeId> mRasterizeRemoveQueue{}; ///< Queue of SvgLoader::SvgRasterizeInfo to remove at PostProcess. It will be cleared after PostProcess.
+
+  bool mRemoveProcessorRegistered : 1; ///< Flag if remove processor registered or not.
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_SVG_LOADER_H
index 0a4ff89..55df62e 100644 (file)
@@ -55,9 +55,10 @@ uint64_t GetNanoseconds()
 #endif
 } // namespace
 
-SvgTask::SvgTask(VectorImageRenderer vectorRenderer, CallbackBase* callback, AsyncTask::PriorityType priorityType)
+SvgTask::SvgTask(VectorImageRenderer vectorRenderer, int32_t id, CallbackBase* callback, AsyncTask::PriorityType priorityType)
 : AsyncTask(callback, priorityType),
   mVectorRenderer(vectorRenderer),
+  mId(id),
   mHasSucceeded(false)
 {
 }
@@ -77,8 +78,8 @@ VectorImageRenderer SvgTask::GetRenderer()
   return mVectorRenderer;
 }
 
-SvgLoadingTask::SvgLoadingTask(VectorImageRenderer vectorRenderer, const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, float dpi, CallbackBase* callback)
-: SvgTask(vectorRenderer, callback, url.GetProtocolType() == VisualUrl::ProtocolType::REMOTE ? AsyncTask::PriorityType::LOW : AsyncTask::PriorityType::HIGH),
+SvgLoadingTask::SvgLoadingTask(VectorImageRenderer vectorRenderer, int32_t id, const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, float dpi, CallbackBase* callback)
+: SvgTask(vectorRenderer, id, callback, url.GetProtocolType() == VisualUrl::ProtocolType::REMOTE ? AsyncTask::PriorityType::LOW : AsyncTask::PriorityType::HIGH),
   mImageUrl(url),
   mEncodedImageBuffer(encodedImageBuffer),
   mDpi(dpi)
@@ -101,15 +102,12 @@ void SvgLoadingTask::Process()
 #ifdef TRACE_ENABLED
   uint64_t mStartTimeNanoSceonds = 0;
   uint64_t mEndTimeNanoSceonds   = 0;
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+#endif
+
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_SVG_LOADING_TASK", [&](std::ostringstream& oss) {
     mStartTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << "[u:" << mImageUrl.GetEllipsedUrl() << "]";
-    // DALI_TRACE_BEGIN(gTraceFilter, "DALI_SVG_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("BEGIN: DALI_SVG_LOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 
   bool loadFailed = false;
 
@@ -154,20 +152,15 @@ void SvgLoadingTask::Process()
   }
 
   mHasSucceeded = !loadFailed;
-#ifdef TRACE_ENABLED
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-  {
+
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_SVG_LOADING_TASK", [&](std::ostringstream& oss) {
     mEndTimeNanoSceonds = GetNanoseconds();
-    std::ostringstream oss;
     oss << std::fixed << std::setprecision(3);
     oss << "[";
     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
     oss << "s:" << mHasSucceeded << " ";
     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
-    // DALI_TRACE_END(gTraceFilter, "DALI_SVG_LOADING_TASK"); ///< TODO : Open it if we can control trace log level
-    DALI_LOG_RELEASE_INFO("END: DALI_SVG_LOADING_TASK %s", oss.str().c_str());
-  }
-#endif
+  });
 }
 
 bool SvgLoadingTask::IsReady()
@@ -175,8 +168,8 @@ bool SvgLoadingTask::IsReady()
   return true;
 }
 
-SvgRasterizingTask::SvgRasterizingTask(VectorImageRenderer vectorRenderer, uint32_t width, uint32_t height, CallbackBase* callback)
-: SvgTask(vectorRenderer, callback),
+SvgRasterizingTask::SvgRasterizingTask(VectorImageRenderer vectorRenderer, int32_t id, uint32_t width, uint32_t height, CallbackBase* callback)
+: SvgTask(vectorRenderer, id, callback),
   mWidth(width),
   mHeight(height)
 {
@@ -209,7 +202,11 @@ void SvgRasterizingTask::Process()
   {
     DALI_LOG_ERROR("Rasterize is failed!\n");
     DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_SVG_RASTERIZE_TASK", [&](std::ostringstream& oss) {
-      oss << "[s:" << mWidth << "x" << mHeight << " ";
+      mEndTimeNanoSceonds = GetNanoseconds();
+      oss << std::fixed << std::setprecision(3);
+      oss << "[failed/";
+      oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
+      oss << "s:" << mWidth << "x" << mHeight << " ";
       oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
     });
     return;
@@ -224,6 +221,10 @@ void SvgRasterizingTask::Process()
     oss << "[";
     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
     oss << "s:" << mWidth << "x" << mHeight << " ";
+    if(mPixelData.GetWidth() != mWidth || mPixelData.GetHeight() != mHeight)
+    {
+      oss << "p:" << mPixelData.GetWidth() << "x" << mPixelData.GetHeight() << " ";
+    }
     oss << "u:" << mImageUrl.GetEllipsedUrl() << "]";
   });
 }
index e23a8ae..5885ee7 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_SVG_TASK_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,10 +55,11 @@ public:
   /**
    * Constructor
    * @param[in] vectorRenderer The vector rasterizer.
+   * @param[in] id The id of this task.
    * @param[in] callback The callback that is called when the operation is completed.
    * @param[in] priorityType The priority of this task.
    */
-  SvgTask(VectorImageRenderer vectorRenderer, CallbackBase* callback, AsyncTask::PriorityType priorityType = AsyncTask::PriorityType::DEFAULT);
+  SvgTask(VectorImageRenderer vectorRenderer, int32_t id, CallbackBase* callback, AsyncTask::PriorityType priorityType = AsyncTask::PriorityType::DEFAULT);
 
   /**
    * Destructor.
@@ -72,6 +73,15 @@ public:
   bool HasSucceeded() const;
 
   /**
+   * Get the id of task.
+   * @return The Id of task what we added from constructor.
+   */
+  int32_t GetId() const
+  {
+    return mId;
+  }
+
+  /**
    * @brief Get the task's imageRenderer
    * @return VectorImageRenderer
    */
@@ -92,6 +102,7 @@ private:
 
 protected:
   VectorImageRenderer mVectorRenderer;
+  const int32_t       mId;
   bool                mHasSucceeded;
 };
 
@@ -101,12 +112,13 @@ public:
   /**
    * Constructor
    * @param[in] vectorRenderer The vector rasterizer.
+   * @param[in] id The id of this task.
    * @param[in] url The URL to svg resource to use.
    * @param[in] encodedImageBuffer The resource buffer if required.
    * @param[in] dpi The DPI of the screen.
    * @param[in] callback The callback that is called when the operation is completed.
    */
-  SvgLoadingTask(VectorImageRenderer vectorRenderer, const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, float dpi, CallbackBase* callback);
+  SvgLoadingTask(VectorImageRenderer vectorRenderer, int32_t id, const VisualUrl& url, EncodedImageBuffer encodedImageBuffer, float dpi, CallbackBase* callback);
 
   /**
    * Destructor.
@@ -151,11 +163,12 @@ public:
   /**
    * Constructor
    * @param[in] vectorRenderer The vector rasterizer.
+   * @param[in] id The id of this task.
    * @param[in] width The rasterization width.
    * @param[in] height The rasterization height.
    * @param[in] callback The callback that is called when the operation is completed.
    */
-  SvgRasterizingTask(VectorImageRenderer vectorRenderer, uint32_t width, uint32_t height, CallbackBase* callback);
+  SvgRasterizingTask(VectorImageRenderer vectorRenderer, int32_t id, uint32_t width, uint32_t height, CallbackBase* callback);
 
   /**
    * Destructor.
index 49d62ab..7728733 100644 (file)
 #include <dali-toolkit/internal/visuals/svg/svg-visual.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
-#include <dali-toolkit/internal/visuals/svg/svg-task.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-feature-builder.h>
+#include <dali-toolkit/internal/visuals/svg/svg-loader.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/common/stage.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/rendering/decorated-visual-renderer.h>
 
@@ -42,8 +43,7 @@ namespace
 {
 const int CUSTOM_PROPERTY_COUNT(1); // atlas
 
-// property name
-const Dali::Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+constexpr Dali::Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
 } // namespace
 
@@ -63,11 +63,14 @@ SvgVisualPtr SvgVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderF
 }
 
 SvgVisual::SvgVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, ImageDimensions size)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::SVG),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::SVG),
   mImageVisualShaderFactory(shaderFactory),
+  mSvgLoader(factoryCache.GetSvgLoader()),
+  mSvgLoadId(SvgLoader::INVALID_SVG_LOAD_ID),
+  mSvgRasterizeId(SvgLoader::INVALID_SVG_RASTERIZE_ID),
   mAtlasRect(FULL_TEXTURE_RECT),
+  mAtlasRectIndex(Property::INVALID_INDEX),
   mImageUrl(imageUrl),
-  mVectorRenderer(VectorImageRenderer::New()),
   mDefaultWidth(0),
   mDefaultHeight(0),
   mPlacementActor(),
@@ -82,21 +85,24 @@ SvgVisual::SvgVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory&
 
 SvgVisual::~SvgVisual()
 {
-  if(Stage::IsInstalled())
+  if(DALI_LIKELY(Dali::Adaptor::IsAvailable()))
   {
-    if(mLoadingTask)
+    if(mSvgLoadId != SvgLoader::INVALID_SVG_LOAD_ID)
     {
-      Dali::AsyncTaskManager::Get().RemoveTask(mLoadingTask);
+      mSvgLoader.RequestLoadRemove(mSvgLoadId, this);
+      mSvgLoadId = SvgLoader::INVALID_SVG_LOAD_ID;
     }
-    if(mRasterizingTask)
+    if(mSvgRasterizeId != SvgLoader::INVALID_SVG_LOAD_ID)
     {
-      Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
+      // We don't need to remove task synchronously.
+      mSvgLoader.RequestRasterizeRemove(mSvgRasterizeId, this, false);
+      mSvgRasterizeId = SvgLoader::INVALID_SVG_RASTERIZE_ID;
     }
 
     if(mImageUrl.IsBufferResource())
     {
       TextureManager& textureManager = mFactoryCache.GetTextureManager();
-      textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
+      textureManager.RemoveEncodedImageBuffer(mImageUrl);
     }
   }
 }
@@ -111,34 +117,10 @@ void SvgVisual::OnInitialize()
   Vector2 dpi     = Stage::GetCurrent().GetDpi();
   float   meanDpi = (dpi.height + dpi.width) * 0.5f;
 
-  EncodedImageBuffer encodedImageBuffer;
+  const bool synchronousLoading = IsSynchronousLoadingRequired() && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource());
 
-  if(mImageUrl.IsBufferResource())
-  {
-    // Increase reference count of External Resources :
-    // EncodedImageBuffer.
-    // Reference count will be decreased at destructor of the visual.
-    TextureManager& textureManager = mFactoryCache.GetTextureManager();
-    textureManager.UseExternalResource(mImageUrl.GetUrl());
-
-    encodedImageBuffer = textureManager.GetEncodedImageBuffer(mImageUrl.GetUrl());
-  }
-
-  mLoadingTask = new SvgLoadingTask(mVectorRenderer, mImageUrl, encodedImageBuffer, meanDpi, MakeCallback(this, &SvgVisual::ApplyRasterizedImage));
-
-  if(IsSynchronousLoadingRequired() && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()))
-  {
-    mLoadingTask->Process();
-    if(!mLoadingTask->HasSucceeded())
-    {
-      mLoadFailed = true;
-    }
-    mLoadingTask.Reset(); // We don't need it anymore.
-  }
-  else
-  {
-    Dali::AsyncTaskManager::Get().AddTask(mLoadingTask);
-  }
+  // It will call SvgVisual::LoadComplete() synchronously if it required, or we already loaded same svg before.
+  mSvgLoadId = mSvgLoader.Load(mImageUrl, meanDpi, this, synchronousLoading);
 }
 
 void SvgVisual::DoSetProperties(const Property::Map& propertyMap)
@@ -176,7 +158,11 @@ void SvgVisual::DoSetProperty(Property::Index index, const Property::Value& valu
   {
     case Toolkit::ImageVisual::Property::ATLASING:
     {
-      value.Get(mAttemptAtlasing);
+      bool atlasing = false;
+      if(value.Get(atlasing))
+      {
+        mAttemptAtlasing = atlasing;
+      }
       break;
     }
     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
@@ -255,17 +241,18 @@ void SvgVisual::DoSetOnScene(Actor& actor)
 void SvgVisual::DoSetOffScene(Actor& actor)
 {
   // Remove rasterizing task
-  if(mRasterizingTask)
+  if(mSvgRasterizeId != SvgLoader::INVALID_SVG_LOAD_ID)
   {
-    Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
-    mRasterizingTask.Reset();
+    // We don't need to remove task synchronously.
+    mSvgLoader.RequestRasterizeRemove(mSvgRasterizeId, this, false);
+    mSvgRasterizeId = SvgLoader::INVALID_SVG_RASTERIZE_ID;
   }
 
   actor.RemoveRenderer(mImpl->mRenderer);
   mPlacementActor.Reset();
 
-  // Reset the visual size to zero so that when adding the actor back to stage the SVG rasterization is forced
-  mRasterizedSize = Vector2::ZERO;
+  // Reset the visual size so that when adding the actor back to stage the SVG rasterization is forced
+  mRasterizedSize = -Vector2::ONE; ///< Let we don't use zero since visual size could be zero after trasnform
 }
 
 void SvgVisual::GetNaturalSize(Vector2& naturalSize)
@@ -325,115 +312,139 @@ void SvgVisual::EnablePreMultipliedAlpha(bool preMultiplied)
   }
 }
 
+bool SvgVisual::AttemptAtlasing() const
+{
+  return (!mImpl->mCustomShader && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()) && mAttemptAtlasing);
+}
+
 void SvgVisual::AddRasterizationTask(const Vector2& size)
 {
   if(mImpl->mRenderer)
   {
     // Remove previous task
-    if(mRasterizingTask)
+    if(mSvgRasterizeId != SvgLoader::INVALID_SVG_LOAD_ID)
     {
-      Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
-      mRasterizingTask.Reset();
+      mSvgLoader.RequestRasterizeRemove(mSvgRasterizeId, this, true);
+      mSvgRasterizeId = SvgLoader::INVALID_SVG_RASTERIZE_ID;
     }
 
-    unsigned int width  = static_cast<unsigned int>(size.width);
-    unsigned int height = static_cast<unsigned int>(size.height);
+    uint32_t width  = static_cast<uint32_t>(roundf(size.width));
+    uint32_t height = static_cast<uint32_t>(roundf(size.height));
+
+    const bool synchronousRasterize = IsSynchronousLoadingRequired() && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource());
+    const bool attemtAtlasing       = AttemptAtlasing();
 
-    mRasterizingTask = new SvgRasterizingTask(mVectorRenderer, width, height, MakeCallback(this, &SvgVisual::ApplyRasterizedImage));
+    mSvgRasterizeId = mSvgLoader.Rasterize(mSvgLoadId, width, height, attemtAtlasing, this, synchronousRasterize);
+  }
+}
 
-#ifdef TRACE_ENABLED
-    reinterpret_cast<SvgRasterizingTask*>(mRasterizingTask.Get())->SetUrl(mImageUrl);
-#endif
+/// Called when SvgLoader::Load is completed.
+void SvgVisual::LoadComplete(int32_t loadId, Dali::VectorImageRenderer vectorImageRenderer)
+{
+  // mSvgLoadId might not be updated if svg file is cached. Update now.
+  mSvgLoadId = loadId;
 
-    if(IsSynchronousLoadingRequired() && mImageUrl.IsLocalResource())
+  if(DALI_LIKELY(vectorImageRenderer))
+  {
+    vectorImageRenderer.GetDefaultSize(mDefaultWidth, mDefaultHeight);
+    if(mImpl->mEventObserver && mImpl->mFittingMode != DevelVisual::FittingMode::DONT_CARE)
     {
-      mRasterizingTask->Process();
-      ApplyRasterizedImage(mRasterizingTask);
-      mRasterizingTask.Reset(); // We don't need it anymore.
+      // Need teo call ApplyFittingMode once again, after load completed.
+      mImpl->mEventObserver->RelayoutRequest(*this);
     }
-    else
+  }
+  else if(!mLoadFailed)
+  {
+    SvgVisualPtr self = this; // Keep reference until this API finished
+
+    mLoadFailed = true;
+
+    // Remove rasterizing task if we requested before.
+    if(mSvgRasterizeId != SvgLoader::INVALID_SVG_LOAD_ID)
     {
-      Dali::AsyncTaskManager::Get().AddTask(mRasterizingTask);
+      mSvgLoader.RequestRasterizeRemove(mSvgRasterizeId, this, true);
+      mSvgRasterizeId = SvgLoader::INVALID_SVG_RASTERIZE_ID;
+    }
+
+    if(IsOnScene())
+    {
+      Actor actor = mPlacementActor.GetHandle();
+      if(actor && mImpl->mRenderer)
+      {
+        Vector2 imageSize = Vector2::ZERO;
+        imageSize         = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
+        mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+        actor.AddRenderer(mImpl->mRenderer);
+        // reset the weak handle so that the renderer only get added to actor once
+        mPlacementActor.Reset();
+      }
+
+      // Emit failed signal only if this visual is scene-on
+      ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
     }
   }
 }
 
-void SvgVisual::ApplyRasterizedImage(SvgTaskPtr task)
+/// Called when SvgLoader::Rasterize is completed.
+void SvgVisual::RasterizeComplete(int32_t rasterizeId, Dali::TextureSet textureSet, Vector4 atlasRect)
 {
-  SvgVisualPtr self = this; // Keep reference until this API finished
+  // rasterize id might not be updated if rasterize is cached.
+  mSvgRasterizeId = rasterizeId;
 
-  if(task->HasSucceeded())
+  if(DALI_LIKELY(textureSet))
   {
-    PixelData rasterizedPixelData = task->GetPixelData();
-    if(mDefaultWidth == 0 || mDefaultHeight == 0)
-    {
-      task->GetRenderer().GetDefaultSize(mDefaultWidth, mDefaultHeight);
-    }
+    bool updateShader = false;
 
-    // We don't need to keep tasks anymore. reset now.
-    if(task == mLoadingTask)
+    if(AttemptAtlasing() && atlasRect != FULL_TEXTURE_RECT)
     {
-      mLoadingTask.Reset();
+      mAtlasRect = atlasRect;
+      if(DALI_UNLIKELY(!(mImpl->mFlags & Impl::IS_ATLASING_APPLIED)))
+      {
+        updateShader = true;
+      }
+      mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
     }
-    if(task == mRasterizingTask)
+    else
     {
-      mRasterizingTask.Reset();
+      mAtlasRect = FULL_TEXTURE_RECT;
+      if(DALI_UNLIKELY(mImpl->mFlags & Impl::IS_ATLASING_APPLIED))
+      {
+        updateShader = true;
+      }
+      mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
     }
 
-    // Rasterization success
-    if(rasterizedPixelData && IsOnScene())
+    if(DALI_LIKELY(mImpl->mRenderer))
     {
-      mRasterizedSize.x = static_cast<float>(rasterizedPixelData.GetWidth());
-      mRasterizedSize.y = static_cast<float>(rasterizedPixelData.GetHeight());
-
       TextureSet currentTextureSet = mImpl->mRenderer.GetTextures();
-      if(mImpl->mFlags & Impl::IS_ATLASING_APPLIED)
-      {
-        mFactoryCache.GetAtlasManager()->Remove(currentTextureSet, mAtlasRect);
-      }
-
-      TextureSet textureSet;
 
-      if(mAttemptAtlasing && !mImpl->mCustomShader)
+      if(mAtlasRectIndex == Property::INVALID_INDEX)
       {
-        Vector4 atlasRect;
-        textureSet = mFactoryCache.GetAtlasManager()->Add(atlasRect, rasterizedPixelData);
-        if(textureSet) // atlasing
+        if(DALI_UNLIKELY(mAtlasRect != FULL_TEXTURE_RECT))
         {
-          if(textureSet != currentTextureSet)
-          {
-            mImpl->mRenderer.SetTextures(textureSet);
-          }
-          mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, atlasRect);
-          mAtlasRect = atlasRect;
-          mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
+          // Register atlas rect property only if it's not full texture rect.
+          mAtlasRectIndex = mImpl->mRenderer.RegisterUniqueProperty(mAtlasRectIndex, ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
         }
       }
-
-      if(!textureSet) // no atlasing - mAttemptAtlasing is false or adding to atlas is failed
+      else
       {
-        Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888, rasterizedPixelData.GetWidth(), rasterizedPixelData.GetHeight());
-        texture.Upload(rasterizedPixelData);
-        mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
-
-        if(mAtlasRect == FULL_TEXTURE_RECT)
-        {
-          textureSet = currentTextureSet;
-        }
-        else
-        {
-          textureSet = TextureSet::New();
-          mImpl->mRenderer.SetTextures(textureSet);
+        mImpl->mRenderer.SetProperty(mAtlasRectIndex, mAtlasRect);
+      }
 
-          mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT);
-          mAtlasRect = FULL_TEXTURE_RECT;
-        }
+      if(textureSet != currentTextureSet)
+      {
+        mImpl->mRenderer.SetTextures(textureSet);
+      }
 
-        if(textureSet)
-        {
-          textureSet.SetTexture(0, texture);
-        }
+      if(DALI_UNLIKELY(updateShader))
+      {
+        UpdateShader();
       }
+    }
+
+    if(IsOnScene())
+    {
+      SvgVisualPtr self = this; // Keep reference until this API finished
 
       // Rasterized pixels are uploaded to texture. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
       Actor actor = mPlacementActor.GetHandle();
@@ -450,36 +461,37 @@ void SvgVisual::ApplyRasterizedImage(SvgTaskPtr task)
   }
   else if(!mLoadFailed)
   {
+    SvgVisualPtr self = this; // Keep reference until this API finished
+
     mLoadFailed = true;
 
-    // Remove rasterizing task if we requested before.
-    if(mRasterizingTask)
+    if(IsOnScene())
     {
-      Dali::AsyncTaskManager::Get().RemoveTask(mRasterizingTask);
-      mRasterizingTask.Reset();
-    }
+      Actor actor = mPlacementActor.GetHandle();
+      if(actor && mImpl->mRenderer)
+      {
+        Vector2 imageSize = Vector2::ZERO;
+        imageSize         = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
+        mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+        actor.AddRenderer(mImpl->mRenderer);
 
-    // We don't need to keep tasks anymore. reset now.
-    if(task == mLoadingTask)
-    {
-      mLoadingTask.Reset();
-    }
+        // reset the weak handle so that the renderer only get added to actor once
+        mPlacementActor.Reset();
+      }
 
-    Actor actor = mPlacementActor.GetHandle();
-    if(actor)
-    {
-      Vector2 imageSize = Vector2::ZERO;
-      imageSize         = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
-      mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
-      actor.AddRenderer(mImpl->mRenderer);
+      // Emit failed signal only if this visual is scene-on
+      ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
     }
-
-    ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
   }
 }
 
 void SvgVisual::OnSetTransform()
 {
+  if(mImpl->mRenderer)
+  {
+    mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
+  }
+
   if(IsOnScene() && !mLoadFailed)
   {
     Vector2 size;
@@ -494,17 +506,16 @@ void SvgVisual::OnSetTransform()
       size = mImpl->mTransform.GetVisualSize(mImpl->mControlSize);
     }
 
-    if(size != mRasterizedSize || mDefaultWidth == 0 || mDefaultHeight == 0)
+    // roundf and change as integer scale.
+    size.width  = static_cast<uint32_t>(roundf(size.width));
+    size.height = static_cast<uint32_t>(roundf(size.height));
+
+    if(size != mRasterizedSize)
     {
       mRasterizedSize = size;
       AddRasterizationTask(size);
     }
   }
-
-  if(mImpl->mRenderer)
-  {
-    mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
-  }
 }
 
 void SvgVisual::UpdateShader()
@@ -523,8 +534,8 @@ Shader SvgVisual::GenerateShader() const
   {
     shader = mImageVisualShaderFactory.GetShader(
       mFactoryCache,
-      ImageVisualShaderFeatureBuilder()
-        .EnableTextureAtlas(mAttemptAtlasing)
+      ImageVisualShaderFeature::FeatureBuilder()
+        .EnableTextureAtlas(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED)
         .EnableRoundedCorner(IsRoundedCornerRequired())
         .EnableBorderline(IsBorderlineRequired()));
   }
index faf8a22..29e10e9 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_SVG_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/public-api/object/weak-handle.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/svg/svg-loader-observer.h>
+#include <dali-toolkit/internal/visuals/svg/svg-loader.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
-#include <dali-toolkit/internal/visuals/svg/svg-task.h>
 
 namespace Dali
 {
@@ -47,7 +48,8 @@ typedef IntrusivePtr<SvgVisual> SvgVisualPtr;
  * | url                      | STRING           |
  *
  */
-class SvgVisual : public Visual::Base
+class SvgVisual : public Visual::Base,
+                  public SvgLoaderObserver
 {
 public:
   /**
@@ -150,13 +152,16 @@ protected:
    */
   Shader GenerateShader() const override;
 
-public:
+protected: // Implementation of  SvgLoaderObserver
   /**
-   * @bried Apply the rasterized image to the visual.
-   *
-   * @param[in] task SvgTaskPtr
+   * @copydoc Dali::Toolkit::Internal::SvgLoaderObserver::LoadComplete
+   */
+  void LoadComplete(int32_t loadId, Dali::VectorImageRenderer vectorImageRenderer) override;
+
+  /**
+   * @copydoc Dali::Toolkit::Internal::SvgLoaderObserver::RasterizeComplete
    */
-  void ApplyRasterizedImage(SvgTaskPtr task);
+  void RasterizeComplete(int32_t rasterizeId, Dali::TextureSet textureSet, Vector4 atlasRect) override;
 
 private:
   /**
@@ -173,6 +178,12 @@ private:
    */
   void DoSetProperty(Property::Index index, const Property::Value& value);
 
+  /**
+   * @brief Checks if atlasing should be attempted
+   * @return bool returns true if atlasing can be attempted.
+   */
+  bool AttemptAtlasing() const;
+
   // Undefined
   SvgVisual(const SvgVisual& svgRenderer);
 
@@ -181,18 +192,21 @@ private:
 
 private:
   ImageVisualShaderFactory& mImageVisualShaderFactory;
-  Vector4                   mAtlasRect;
-  VisualUrl                 mImageUrl;
-  VectorImageRenderer       mVectorRenderer;
-  uint32_t                  mDefaultWidth;
-  uint32_t                  mDefaultHeight;
-  WeakHandle<Actor>         mPlacementActor;
-  Vector2                   mRasterizedSize;
-  Dali::ImageDimensions     mDesiredSize{};
-  SvgTaskPtr                mLoadingTask;
-  SvgTaskPtr                mRasterizingTask;
-  bool                      mLoadFailed;
-  bool                      mAttemptAtlasing; ///< If true will attempt atlasing, otherwise create unique texture
+  SvgLoader&                mSvgLoader; ///< reference to Svg loader for fast access
+
+  SvgLoader::SvgLoadId      mSvgLoadId;
+  SvgLoader::SvgRasterizeId mSvgRasterizeId;
+
+  Vector4               mAtlasRect;
+  Property::Index       mAtlasRectIndex;
+  VisualUrl             mImageUrl;
+  uint32_t              mDefaultWidth;
+  uint32_t              mDefaultHeight;
+  WeakHandle<Actor>     mPlacementActor;
+  Vector2               mRasterizedSize;
+  Dali::ImageDimensions mDesiredSize{};
+  bool                  mLoadFailed : 1;
+  bool                  mAttemptAtlasing : 1; ///< If true will attempt atlasing, otherwise create unique texture
 };
 
 } // namespace Internal
diff --git a/dali-toolkit/internal/visuals/text-visual-shader-factory.cpp b/dali-toolkit/internal/visuals/text-visual-shader-factory.cpp
deleted file mode 100644 (file)
index 365de2f..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// CLASS HEADER
-#include <dali-toolkit/internal/visuals/text-visual-shader-factory.h>
-
-// INTERNAL INCLUDES
-#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
-#include <dali-toolkit/internal/visuals/visual-string-constants.h>
-#include <dali/integration-api/debug.h>
-
-namespace Dali
-{
-namespace Toolkit
-{
-namespace Internal
-{
-namespace
-{
-// enum of required list when we select shader
-enum class TextVisualRequireFlag : uint32_t
-{
-  DEFAULT     = 0,
-  STYLES      = 1 << 0,
-  OVERLAY     = 1 << 1,
-  EMOJI       = 1 << 2,
-  MULTI_COLOR = 1 << 3,
-};
-
-const VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[] =
-  {
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_OVERLAY,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_OVERLAY,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_OVERLAY_AND_EMOJI,
-    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_OVERLAY_AND_EMOJI,
-    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT,
-    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE,
-    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_OVERLAY,
-    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE_AND_OVERLAY,
-};
-
-static constexpr auto      SHADER_TYPE_COUNT = 1u;
-constexpr std::string_view VertexPredefines[SHADER_TYPE_COUNT]{
-  "", // VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT
-};
-constexpr std::string_view FragmentPredefines[SHADER_TYPE_COUNT]{
-  "", // VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT
-};
-constexpr VisualFactoryCache::ShaderType ShaderTypePredefines[SHADER_TYPE_COUNT]{
-  VisualFactoryCache::ShaderType::TEXT_SHADER_SINGLE_COLOR_TEXT,
-};
-
-} // unnamed namespace
-
-namespace TextVisualShaderFeature
-{
-FeatureBuilder& FeatureBuilder::EnableMultiColor(bool enableMultiColor)
-{
-  mTextMultiColor = enableMultiColor ? TextMultiColor::MULTI_COLOR_TEXT : TextMultiColor::SINGLE_COLOR_TEXT;
-  return *this;
-}
-FeatureBuilder& FeatureBuilder::EnableEmoji(bool enableEmoji)
-{
-  mTextEmoji = enableEmoji ? TextEmoji::HAS_EMOJI : TextEmoji::NO_EMOJI;
-  return *this;
-}
-FeatureBuilder& FeatureBuilder::EnableStyle(bool enableStyle)
-{
-  mTextStyle = enableStyle ? TextStyle::HAS_STYLES : TextStyle::NO_STYLES;
-  return *this;
-}
-FeatureBuilder& FeatureBuilder::EnableOverlay(bool enableOverlay)
-{
-  mTextOverlay = enableOverlay ? TextOverlay::HAS_OVERLAY : TextOverlay::NO_OVERLAY;
-  return *this;
-}
-} // namespace TextVisualShaderFeature
-
-TextVisualShaderFactory::TextVisualShaderFactory()
-{
-}
-
-TextVisualShaderFactory::~TextVisualShaderFactory()
-{
-}
-
-Shader TextVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder)
-{
-  Shader                         shader;
-  uint32_t                       shaderTypeFlag = static_cast<uint32_t>(TextVisualRequireFlag::DEFAULT);
-  VisualFactoryCache::ShaderType shaderType     = VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT;
-
-  const auto& multiColor = featureBuilder.mTextMultiColor;
-  const auto& emoji      = featureBuilder.mTextEmoji;
-  const auto& style      = featureBuilder.mTextStyle;
-  const auto& overlay    = featureBuilder.mTextOverlay;
-
-  if(style == TextVisualShaderFeature::TextStyle::HAS_STYLES)
-  {
-    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::STYLES);
-  }
-  if(overlay == TextVisualShaderFeature::TextOverlay::HAS_OVERLAY)
-  {
-    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::OVERLAY);
-  }
-  // multi color can also render emoji. If multi color text, dont consider emoji
-  if(multiColor != TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT && emoji == TextVisualShaderFeature::TextEmoji::HAS_EMOJI)
-  {
-    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::EMOJI);
-  }
-  if(multiColor == TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT)
-  {
-    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::MULTI_COLOR);
-  }
-
-  shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
-  shader     = factoryCache.GetShader(shaderType);
-
-  if(!shader)
-  {
-    std::string vertexShaderPrefixList;
-    std::string fragmentShaderPrefixList;
-
-    if(style == TextVisualShaderFeature::TextStyle::HAS_STYLES)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_STYLE\n";
-    }
-    if(overlay == TextVisualShaderFeature::TextOverlay::HAS_OVERLAY)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_OVERLAY\n";
-    }
-    // multi color can also render emoji. If multi color text, dont consider emoji
-    if(multiColor != TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT && emoji == TextVisualShaderFeature::TextEmoji::HAS_EMOJI)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_EMOJI\n";
-    }
-    if(multiColor == TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT)
-    {
-      fragmentShaderPrefixList += "#define IS_REQUIRED_MULTI_COLOR\n";
-    }
-
-    std::string vertexShader   = std::string(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_TEXT_VISUAL_SHADER_VERT.data());
-    std::string fragmentShader = std::string(Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_TEXT_VISUAL_SHADER_FRAG.data());
-
-    shader = factoryCache.GenerateAndSaveShader(shaderType, vertexShader, fragmentShader);
-  }
-  return shader;
-}
-
-void TextVisualShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
-{
-  std::vector<std::string_view> vertexPrefix;
-  std::vector<std::string_view> fragmentPrefix;
-  std::vector<std::string_view> shaderName;
-  int                           shaderCount = 0;
-  for(uint32_t i = 0; i < SHADER_TYPE_COUNT; ++i)
-  {
-    vertexPrefix.push_back(VertexPredefines[i]);
-    fragmentPrefix.push_back(FragmentPredefines[i]);
-    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
-    shaderCount++;
-  }
-
-  shaders.vertexPrefix   = vertexPrefix;
-  shaders.fragmentPrefix = fragmentPrefix;
-  shaders.shaderName     = shaderName;
-  shaders.vertexShader   = SHADER_TEXT_VISUAL_SHADER_VERT;
-  shaders.fragmentShader = SHADER_TEXT_VISUAL_SHADER_FRAG;
-  shaders.shaderCount    = shaderCount;
-}
-
-} // namespace Internal
-
-} // namespace Toolkit
-
-} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/text/text-visual-shader-factory.cpp b/dali-toolkit/internal/visuals/text/text-visual-shader-factory.cpp
new file mode 100644 (file)
index 0000000..269754e
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/text/text-visual-shader-factory.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+// enum of required list when we select shader
+enum class TextVisualRequireFlag : uint32_t
+{
+  DEFAULT     = 0,
+  STYLES      = 1 << 0,
+  OVERLAY     = 1 << 1,
+  EMOJI       = 1 << 2,
+  MULTI_COLOR = 1 << 3,
+};
+
+const VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[] =
+  {
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_OVERLAY,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_OVERLAY,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_EMOJI,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_EMOJI,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_OVERLAY_AND_EMOJI,
+    VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT_WITH_STYLE_AND_OVERLAY_AND_EMOJI,
+    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT,
+    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE,
+    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_OVERLAY,
+    VisualFactoryCache::TEXT_SHADER_MULTI_COLOR_TEXT_WITH_STYLE_AND_OVERLAY,
+};
+
+static constexpr auto      PREDEFINED_SHADER_TYPE_COUNT = 1u;
+constexpr std::string_view VertexPredefines[PREDEFINED_SHADER_TYPE_COUNT]{
+  "", // VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT
+};
+constexpr std::string_view FragmentPredefines[PREDEFINED_SHADER_TYPE_COUNT]{
+  "", // VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT
+};
+constexpr VisualFactoryCache::ShaderType ShaderTypePredefines[PREDEFINED_SHADER_TYPE_COUNT]{
+  VisualFactoryCache::ShaderType::TEXT_SHADER_SINGLE_COLOR_TEXT,
+};
+
+} // unnamed namespace
+
+namespace TextVisualShaderFeature
+{
+FeatureBuilder::FeatureBuilder()
+: mTextMultiColor(TextMultiColor::SINGLE_COLOR_TEXT),
+  mTextEmoji(TextEmoji::NO_EMOJI),
+  mTextStyle(TextStyle::NO_STYLES),
+  mTextOverlay(TextOverlay::NO_OVERLAY)
+{
+}
+
+FeatureBuilder& FeatureBuilder::EnableMultiColor(bool enableMultiColor)
+{
+  mTextMultiColor = enableMultiColor ? TextMultiColor::MULTI_COLOR_TEXT : TextMultiColor::SINGLE_COLOR_TEXT;
+  return *this;
+}
+FeatureBuilder& FeatureBuilder::EnableEmoji(bool enableEmoji)
+{
+  mTextEmoji = enableEmoji ? TextEmoji::HAS_EMOJI : TextEmoji::NO_EMOJI;
+  return *this;
+}
+FeatureBuilder& FeatureBuilder::EnableStyle(bool enableStyle)
+{
+  mTextStyle = enableStyle ? TextStyle::HAS_STYLES : TextStyle::NO_STYLES;
+  return *this;
+}
+FeatureBuilder& FeatureBuilder::EnableOverlay(bool enableOverlay)
+{
+  mTextOverlay = enableOverlay ? TextOverlay::HAS_OVERLAY : TextOverlay::NO_OVERLAY;
+  return *this;
+}
+
+VisualFactoryCache::ShaderType FeatureBuilder::GetShaderType() const
+{
+  uint32_t                       shaderTypeFlag = static_cast<uint32_t>(TextVisualRequireFlag::DEFAULT);
+  VisualFactoryCache::ShaderType shaderType     = VisualFactoryCache::TEXT_SHADER_SINGLE_COLOR_TEXT;
+
+  if(mTextStyle == TextVisualShaderFeature::TextStyle::HAS_STYLES)
+  {
+    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::STYLES);
+  }
+  if(mTextOverlay == TextVisualShaderFeature::TextOverlay::HAS_OVERLAY)
+  {
+    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::OVERLAY);
+  }
+  // multi color can also render emoji. If multi color text, dont consider emoji
+  if(mTextMultiColor != TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT && mTextEmoji == TextVisualShaderFeature::TextEmoji::HAS_EMOJI)
+  {
+    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::EMOJI);
+  }
+  if(mTextMultiColor == TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT)
+  {
+    shaderTypeFlag |= static_cast<uint32_t>(TextVisualRequireFlag::MULTI_COLOR);
+  }
+
+  shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
+  return shaderType;
+}
+
+void FeatureBuilder::GetVertexShaderPrefixList(std::string& vertexShaderPrefixList) const
+{
+  // Do nothing
+}
+
+void FeatureBuilder::GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const
+{
+  if(mTextStyle == TextVisualShaderFeature::TextStyle::HAS_STYLES)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_STYLE\n";
+  }
+  if(mTextOverlay == TextVisualShaderFeature::TextOverlay::HAS_OVERLAY)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_OVERLAY\n";
+  }
+  // multi color can also render emoji. If multi color text, dont consider emoji
+  if(mTextMultiColor != TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT && mTextEmoji == TextVisualShaderFeature::TextEmoji::HAS_EMOJI)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_EMOJI\n";
+  }
+  if(mTextMultiColor == TextVisualShaderFeature::TextMultiColor::MULTI_COLOR_TEXT)
+  {
+    fragmentShaderPrefixList += "#define IS_REQUIRED_MULTI_COLOR\n";
+  }
+}
+
+} // namespace TextVisualShaderFeature
+
+TextVisualShaderFactory::TextVisualShaderFactory()
+{
+}
+
+TextVisualShaderFactory::~TextVisualShaderFactory()
+{
+}
+
+Shader TextVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder)
+{
+  Shader                         shader;
+  VisualFactoryCache::ShaderType  shaderType = featureBuilder.GetShaderType();
+  shader     = factoryCache.GetShader(shaderType);
+
+  if(!shader)
+  {
+    std::string vertexShaderPrefixList;
+    std::string fragmentShaderPrefixList;
+    featureBuilder.GetVertexShaderPrefixList(vertexShaderPrefixList);
+    featureBuilder.GetFragmentShaderPrefixList(fragmentShaderPrefixList);
+
+    std::string vertexShader   = std::string(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_TEXT_VISUAL_SHADER_VERT.data());
+    std::string fragmentShader = std::string(Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_TEXT_VISUAL_SHADER_FRAG.data());
+
+    shader = factoryCache.GenerateAndSaveShader(shaderType, vertexShader, fragmentShader);
+  }
+  return shader;
+}
+
+bool TextVisualShaderFactory::AddPrecompiledShader(PrecompileShaderOption& option)
+{
+  ShaderFlagList shaderOption = option.GetShaderOptions();
+
+  auto featureBuilder = TextVisualShaderFeature::FeatureBuilder();
+  std::string vertexPrefixList;
+  std::string fragmentPrefixList;
+  CreatePrecompileShader(featureBuilder, shaderOption);
+
+  VisualFactoryCache::ShaderType type = featureBuilder.GetShaderType();
+  featureBuilder.GetVertexShaderPrefixList(vertexPrefixList);
+  featureBuilder.GetFragmentShaderPrefixList(fragmentPrefixList);
+  return SavePrecompileShader(type, vertexPrefixList, fragmentPrefixList );
+}
+
+
+void TextVisualShaderFactory::GetPreCompiledShader(RawShaderData& shaders)
+{
+  std::vector<std::string_view> vertexPrefix;
+  std::vector<std::string_view> fragmentPrefix;
+  std::vector<std::string_view> shaderName;
+  int                           shaderCount = 0;
+
+  // precompile requested shader first
+  for(uint32_t i = 0u; i < mRequestedPrecompileShader.size(); i++ )
+  {
+    vertexPrefix.push_back(mRequestedPrecompileShader[i].vertexPrefix);
+    fragmentPrefix.push_back(mRequestedPrecompileShader[i].fragmentPrefix);
+    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+    shaderCount++;
+  }
+
+  for(uint32_t i = 0u; i < PREDEFINED_SHADER_TYPE_COUNT; ++i)
+  {
+    vertexPrefix.push_back(VertexPredefines[i]);
+    fragmentPrefix.push_back(FragmentPredefines[i]);
+    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+    shaderCount++;
+  }
+
+  shaders.vertexPrefix   = std::move(vertexPrefix);
+  shaders.fragmentPrefix = std::move(fragmentPrefix);
+  shaders.shaderName     = std::move(shaderName);
+  shaders.vertexShader   = SHADER_TEXT_VISUAL_SHADER_VERT;
+  shaders.fragmentShader = SHADER_TEXT_VISUAL_SHADER_FRAG;
+  shaders.shaderCount    = shaderCount;
+  shaders.custom = false;
+}
+
+void TextVisualShaderFactory::CreatePrecompileShader(TextVisualShaderFeature::FeatureBuilder& builder, const ShaderFlagList& option)
+{
+  for(uint32_t i = 0; i < option.size(); ++i)
+  {
+    if(option[i] == PrecompileShaderOption::Flag::STYLES)
+    {
+      builder.EnableStyle(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::OVERLAY)
+    {
+      builder.EnableOverlay(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::EMOJI)
+    {
+      builder.EnableEmoji(true);
+    }
+    else if(option[i] == PrecompileShaderOption::Flag::MULTI_COLOR)
+    {
+      builder.EnableMultiColor(true);
+    }
+    else
+    {
+      DALI_LOG_WARNING("Unknown option[%d]. maybe this type can't use this flag \n", option[i]);
+    }
+  }
+}
+
+bool TextVisualShaderFactory::SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix)
+{
+  for(uint32_t i = 0u; i< PREDEFINED_SHADER_TYPE_COUNT; i++)
+  {
+    if(ShaderTypePredefines[i] == shader)
+    {
+      DALI_LOG_WARNING("This shader already added list(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  for(uint32_t i = 0u; i< mRequestedPrecompileShader.size(); i++)
+  {
+    if(mRequestedPrecompileShader[i].type == shader)
+    {
+      DALI_LOG_WARNING("This shader already requsted(%s).", Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(mRequestedPrecompileShader[i].type, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+      return false;
+    }
+  }
+
+  RequestShaderInfo info;
+  info.type = shader;
+  info.vertexPrefix = vertexPrefix;
+  info.fragmentPrefix = fragmentPrefix;
+  mRequestedPrecompileShader.push_back(info);
+  DALI_LOG_RELEASE_INFO("Add precompile shader success!!(%s)",Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(shader, VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
+  return true;
+}
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXT_VISUAL_SHADER_FACTORY_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 // EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali-toolkit/internal/visuals/visual-shader-factory-interface.h>
 #include <string_view>
 
 namespace Dali
@@ -85,21 +87,19 @@ enum Type
 /**
  * @brief Collection of current text visual feature.
  */
-struct FeatureBuilder
+class FeatureBuilder
 {
-  FeatureBuilder()
-  : mTextMultiColor(TextMultiColor::SINGLE_COLOR_TEXT),
-    mTextEmoji(TextEmoji::NO_EMOJI),
-    mTextStyle(TextStyle::NO_STYLES),
-    mTextOverlay(TextOverlay::NO_OVERLAY)
-  {
-  }
-
+public:
+  FeatureBuilder();
   FeatureBuilder& EnableMultiColor(bool enableMultiColor);
   FeatureBuilder& EnableEmoji(bool enableEmoji);
   FeatureBuilder& EnableStyle(bool enableStyle);
   FeatureBuilder& EnableOverlay(bool enableOverlay);
 
+  VisualFactoryCache::ShaderType GetShaderType() const;
+  void GetVertexShaderPrefixList(std::string& vertexShaderPrefixList) const;
+  void GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const;
+
   bool IsEnabledMultiColor() const
   {
     return mTextMultiColor == TextMultiColor::MULTI_COLOR_TEXT;
@@ -117,6 +117,7 @@ struct FeatureBuilder
     return mTextOverlay == TextOverlay::HAS_OVERLAY;
   }
 
+private:
   TextMultiColor::Type mTextMultiColor : 2; ///< Whether text has multiple color, or not. default as TextMultiColor::SINGLE_COLOR_TEXT
   TextEmoji::Type      mTextEmoji : 2;      ///< Whether text has emoji, or not. default as TextEmoji::NO_EMOJI
   TextStyle::Type      mTextStyle : 2;      ///< Whether text has style, or not. default as TextStyle::NO_STYLES
@@ -128,7 +129,7 @@ struct FeatureBuilder
 /**
  * TextVisualShaderFactory is an object that provides and shares shaders for text visuals
  */
-class TextVisualShaderFactory
+class TextVisualShaderFactory : public VisualShaderFactoryInterface
 {
 public:
   /**
@@ -149,11 +150,27 @@ public:
    */
   Shader GetShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder);
 
+public: // Implementation of VisualShaderFactoryInterface
+  /**
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::AddPrecompiledShader
+   */
+  bool AddPrecompiledShader(PrecompileShaderOption& option) override;
+
   /**
-   * @brief Get the default shader source.
-   * @param[in] shaders shaderList for precompile
+   * @copydoc Dali::Toolkit::VisualShaderFactoryInterface::GetPreCompiledShader
    */
-  void GetPreCompiledShader(RawShaderData& shaders);
+  void GetPreCompiledShader(RawShaderData& shaders) override;
+
+private:
+  /**
+   * @brief Create pre-compiled shader for image with builder and option.
+   */
+  void CreatePrecompileShader(TextVisualShaderFeature::FeatureBuilder& builder, const ShaderFlagList& option);
+
+  /**
+   * @brief Check if cached hash value is valid or not.
+   */
+  bool SavePrecompileShader(VisualFactoryCache::ShaderType shader, std::string& vertexPrefix, std::string& fragmentPrefix);
 
 protected:
   /**
@@ -165,8 +182,6 @@ protected:
    * Undefined assignment operator.
    */
   TextVisualShaderFactory& operator=(const TextVisualShaderFactory& rhs);
-
-private:
 };
 
 } // namespace Internal
index 5d2de15..aab56ea 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/devel-api/rendering/texture-devel.h>
 #include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/pixel-data-integ.h>
 #include <dali/integration-api/trace.h>
 #include <string.h>
 
@@ -36,7 +37,7 @@
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
@@ -52,9 +53,28 @@ namespace Internal
 namespace
 {
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
+DALI_INIT_TRACE_FILTER(gTraceFilter2, DALI_TRACE_TEXT_ASYNC, false);
 
 const int CUSTOM_PROPERTY_COUNT(3); // uTextColorAnimatable, uHasMultipleTextColors, requireRender
 
+const float VERTICAL_ALIGNMENT_TABLE[Text::VerticalAlignment::BOTTOM + 1] =
+  {
+    0.0f, // VerticalAlignment::TOP
+    0.5f, // VerticalAlignment::CENTER
+    1.0f  // VerticalAlignment::BOTTOM
+};
+
+#ifdef TRACE_ENABLED
+const char* GetRequestTypeName(Text::Async::RequestType type)
+{
+  if(type < Text::Async::RENDER_FIXED_SIZE || type > Text::Async::COMPUTE_HEIGHT_FOR_WIDTH)
+  {
+    return "INVALID_REQUEST_TYPE";
+  }
+  return Text::Async::RequestTypeName[type];
+}
+#endif
+
 /**
  * Return Property index for the given string key
  * param[in] stringKey the string index key
@@ -251,9 +271,10 @@ void TextVisual::EnablePreMultipliedAlpha(bool preMultiplied)
 }
 
 TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory& shaderFactory)
-: Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::TEXT),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::TEXT),
   mController(Text::Controller::New()),
   mTypesetter(Text::Typesetter::New(mController->GetTextModel())),
+  mAsyncTextInterface(nullptr),
   mTextVisualShaderFactory(shaderFactory),
   mTextShaderFeatureCache(),
   mHasMultipleTextColorsIndex(Property::INVALID_INDEX),
@@ -261,7 +282,13 @@ TextVisual::TextVisual(VisualFactoryCache& factoryCache, TextVisualShaderFactory
   mTextColorAnimatableIndex(Property::INVALID_INDEX),
   mTextRequireRenderPropertyIndex(Property::INVALID_INDEX),
   mRendererUpdateNeeded(false),
-  mTextRequireRender(false)
+  mTextRequireRender(false),
+  mTextLoadingTaskId(0u),
+  mNaturalSizeTaskId(0u),
+  mHeightForWidthTaskId(0u),
+  mIsTextLoadingTaskRunning(false),
+  mIsNaturalSizeTaskRunning(false),
+  mIsHeightForWidthTaskRunning(false)
 {
   // Enable the pre-multiplied alpha to improve the text quality
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
@@ -273,13 +300,14 @@ TextVisual::~TextVisual()
 
 void TextVisual::OnInitialize()
 {
-  Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
-  Shader   shader   = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder());
+  Geometry geometry       = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
+  auto     featureBuilder = TextVisualShaderFeature::FeatureBuilder();
+  Shader   shader         = GetTextShader(mFactoryCache, featureBuilder);
 
   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
   mTextRequireRenderPropertyIndex = mImpl->mRenderer.RegisterUniqueProperty("requireRender", mTextRequireRender);
-  mHasMultipleTextColorsIndex = mImpl->mRenderer.RegisterUniqueProperty("uHasMultipleTextColors", static_cast<float>(false));
+  mHasMultipleTextColorsIndex     = mImpl->mRenderer.RegisterUniqueProperty("uHasMultipleTextColors", static_cast<float>(false));
 }
 
 void TextVisual::DoSetProperties(const Property::Map& propertyMap)
@@ -372,6 +400,11 @@ void TextVisual::RemoveRenderer(Actor& actor, bool removeDefaultRenderer)
 
 void TextVisual::DoSetOffScene(Actor& actor)
 {
+  if(mController->GetRenderMode() != DevelTextLabel::Render::SYNC && mIsTextLoadingTaskRunning)
+  {
+    Text::AsyncTextManager::Get().RequestCancel(mTextLoadingTaskId);
+    mIsTextLoadingTaskRunning = false;
+  }
   if(mColorConstraint)
   {
     mColorConstraint.Remove();
@@ -494,6 +527,11 @@ void TextVisual::DoSetProperty(Dali::Property::Index index, const Dali::Property
 
 void TextVisual::UpdateRenderer()
 {
+  if(mController->GetRenderMode() != DevelTextLabel::Render::SYNC)
+  {
+    return;
+  }
+
   Actor control = mControl.GetHandle();
   if(!control)
   {
@@ -507,7 +545,7 @@ void TextVisual::UpdateRenderer()
   const bool isWidthRelative  = fabsf(mImpl->mTransform.mOffsetSizeMode.z) < Math::MACHINE_EPSILON_1000;
   const bool isHeightRelative = fabsf(mImpl->mTransform.mOffsetSizeMode.w) < Math::MACHINE_EPSILON_1000;
 
-  const float controlWidth = mImpl->mControlSize.width;
+  const float controlWidth  = mImpl->mControlSize.width;
   const float controlHeight = mImpl->mControlSize.height;
 
   // Round the size and offset to avoid pixel alignement issues.
@@ -573,26 +611,36 @@ void TextVisual::UpdateRenderer()
         shadowEnabled = true;
       }
 
-      const bool outlineEnabled               = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
-      const bool backgroundEnabled            = mController->GetTextModel()->IsBackgroundEnabled();
-      const bool markupOrSpannedText          = mController->IsMarkupProcessorEnabled() || mController->GetTextModel()->IsSpannedTextPlaced();
-      const bool markupUnderlineEnabled       = markupOrSpannedText && mController->GetTextModel()->IsMarkupUnderlineSet();
-      const bool markupStrikethroughEnabled   = markupOrSpannedText && mController->GetTextModel()->IsMarkupStrikethroughSet();
-      const bool underlineEnabled             = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
-      const bool strikethroughEnabled         = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
-      const bool backgroundMarkupSet          = mController->GetTextModel()->IsMarkupBackgroundColorSet();
-      const bool cutoutEnabled                = mController->IsTextCutout();
-      const bool backgroundWithCutoutEnabled  = mController->GetTextModel()->IsBackgroundWithCutoutEnabled();
-      const bool styleEnabled                 = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet || cutoutEnabled || backgroundWithCutoutEnabled);
-      const bool isOverlayStyle               = underlineEnabled || strikethroughEnabled;
+      const bool outlineEnabled              = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
+      const bool backgroundEnabled           = mController->GetTextModel()->IsBackgroundEnabled();
+      const bool markupOrSpannedText         = mController->IsMarkupProcessorEnabled() || mController->GetTextModel()->IsSpannedTextPlaced();
+      const bool markupUnderlineEnabled      = markupOrSpannedText && mController->GetTextModel()->IsMarkupUnderlineSet();
+      const bool markupStrikethroughEnabled  = markupOrSpannedText && mController->GetTextModel()->IsMarkupStrikethroughSet();
+      const bool underlineEnabled            = mController->GetTextModel()->IsUnderlineEnabled() || markupUnderlineEnabled;
+      const bool strikethroughEnabled        = mController->GetTextModel()->IsStrikethroughEnabled() || markupStrikethroughEnabled;
+      const bool backgroundMarkupSet         = mController->GetTextModel()->IsMarkupBackgroundColorSet();
+      const bool cutoutEnabled               = mController->IsTextCutout();
+      const bool backgroundWithCutoutEnabled = mController->GetTextModel()->IsBackgroundWithCutoutEnabled();
+      const bool styleEnabled                = (shadowEnabled || outlineEnabled || backgroundEnabled || markupOrSpannedText || backgroundMarkupSet || cutoutEnabled || backgroundWithCutoutEnabled);
+      const bool isOverlayStyle              = underlineEnabled || strikethroughEnabled;
 
       // if background with cutout is enabled, This text visual must render the entire control size.
 
       if(cutoutEnabled)
       {
-        relayoutSize = Vector2(controlWidth, controlHeight);
-        mImpl->mTransform.mSize.width = controlWidth;
+        // mTransform stores the size and offset of the current visual.
+        // padding and alignment information is stored in mOffset.
+        // When Cutout Enabled, the current visual must draw the entire control.
+        // so set the size to controlSize and offset to 0.
+
+        relayoutSize                   = Vector2(controlWidth, controlHeight);
+        mImpl->mTransform.mSize.width  = controlWidth;
         mImpl->mTransform.mSize.height = controlHeight;
+
+        // Relayout to the original size has been completed, so save only the offset information and use it in typesetter.
+
+        Vector2 originOffset = Vector2(mImpl->mTransform.mOffset.x, mImpl->mTransform.mOffset.y);
+        mController->SetOffsetWithCutout(originOffset);
         mImpl->mTransform.mOffset.x = 0;
         mImpl->mTransform.mOffset.y = 0;
       }
@@ -620,7 +668,7 @@ void TextVisual::AddTexture(TextureSet& textureSet, PixelData& data, Sampler& sa
 void TextVisual::AddTilingTexture(TextureSet& textureSet, TilingInfo& tilingInfo, PixelData& data, Sampler& sampler, unsigned int textureSetIndex)
 {
   Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
-                                 tilingInfo.textPixelFormat,
+                                 data.GetPixelFormat(),
                                  tilingInfo.width,
                                  tilingInfo.height);
   DevelTexture::UploadSubPixelData(texture, data, 0u, tilingInfo.offsetHeight, tilingInfo.width, tilingInfo.height);
@@ -662,15 +710,14 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
 
   renderer.SetTextures(textureSet);
 
-  //Register transform properties
+  // Register transform properties
   mImpl->mTransform.SetUniforms(renderer, Direction::LEFT_TO_RIGHT);
 
   // Enable the pre-multiplied alpha to improve the text quality
   renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
-  renderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, true);
 
   // Set size and offset for the tiling.
-  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(info.width, info.height));
+  renderer.SetProperty(VisualRenderer::Property::TRANSFORM_SIZE, Vector2(static_cast<float>(info.width), static_cast<float>(info.height)));
   renderer.SetProperty(VisualRenderer::Property::TRANSFORM_OFFSET, info.transformOffset);
   renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
   renderer.RegisterProperty("uHasMultipleTextColors", static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
@@ -678,6 +725,430 @@ void TextVisual::CreateTextureSet(TilingInfo& info, VisualRenderer& renderer, Sa
   mRendererList.push_back(renderer);
 }
 
+// From async text manager
+void TextVisual::LoadComplete(bool loadingSuccess, const TextInformation& textInformation)
+{
+  Text::AsyncTextParameters parameters = textInformation.parameters;
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter2 && gTraceFilter2->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("LoadComplete, success:%d, type:%s\n", loadingSuccess, GetRequestTypeName(parameters.requestType));
+  }
+#endif
+
+  switch(parameters.requestType)
+  {
+    case Text::Async::RENDER_FIXED_SIZE:
+    case Text::Async::RENDER_FIXED_WIDTH:
+    case Text::Async::RENDER_CONSTRAINT:
+    {
+      mIsTextLoadingTaskRunning = false;
+      break;
+    }
+    case Text::Async::COMPUTE_NATURAL_SIZE:
+    {
+      mIsNaturalSizeTaskRunning = false;
+      break;
+    }
+    case Text::Async::COMPUTE_HEIGHT_FOR_WIDTH:
+    {
+      mIsHeightForWidthTaskRunning = false;
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unexpected request type : %d\n", parameters.requestType);
+      break;
+    }
+  }
+
+  Toolkit::Visual::ResourceStatus resourceStatus;
+
+  if(loadingSuccess)
+  {
+    resourceStatus = Toolkit::Visual::ResourceStatus::READY;
+
+    Text::AsyncTextRenderInfo renderInfo = textInformation.renderInfo;
+
+    if(parameters.requestType == Text::Async::COMPUTE_NATURAL_SIZE || parameters.requestType == Text::Async::COMPUTE_HEIGHT_FOR_WIDTH)
+    {
+      if(mAsyncTextInterface)
+      {
+        mAsyncTextInterface->AsyncSizeComputed(renderInfo);
+        return;
+      }
+    }
+
+    Actor control = mControl.GetHandle();
+    if(!control)
+    {
+      // Nothing to do.
+      ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+      return;
+    }
+
+    // Calculate the size of the visual that can fit the text.
+    // The size of the text after it has been laid-out, size of pixel data buffer.
+    Size layoutSize(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
+
+    // Calculate the offset for vertical alignment only, as the layout engine will do the horizontal alignment.
+    Vector2 alignmentOffset;
+    alignmentOffset.x = 0.0f;
+    alignmentOffset.y = (parameters.textHeight - layoutSize.y) * VERTICAL_ALIGNMENT_TABLE[parameters.verticalAlignment];
+
+    // Size of the text control including padding.
+    Vector2 textControlSize(parameters.textWidth + (parameters.padding.start + parameters.padding.end), parameters.textHeight + (parameters.padding.top + parameters.padding.bottom));
+
+    if(parameters.isAutoScrollEnabled)
+    {
+      // In case of auto scroll, the layout width (renderInfo's width) is the natural size of the text.
+      // Since the layout size is the size of the visual transform, it should be reset to the text area excluding padding.
+      layoutSize.width = parameters.textWidth;
+    }
+
+    Vector2 visualTransformOffset;
+    if(renderInfo.isCutout)
+    {
+      // When Cutout Enabled, the current visual must draw the entire control.
+      // so set the size to controlSize and offset to 0.
+      visualTransformOffset.x = 0.0f;
+      visualTransformOffset.y = 0.0f;
+
+      // The layout size is set to the text control size including padding.
+      layoutSize = textControlSize;
+    }
+    else
+    {
+      // This affects font rendering quality.
+      // It need to be integerized.
+      visualTransformOffset.x = roundf(parameters.padding.start + alignmentOffset.x);
+      visualTransformOffset.y = roundf(parameters.padding.top + alignmentOffset.y);
+    }
+
+    SetRequireRender(renderInfo.isCutout);
+
+    // Transform offset is used for subpixel data upload in text tiling.
+    // We should set the transform before creating a tiling texture.
+    Property::Map visualTransform;
+    visualTransform.Add(Toolkit::Visual::Transform::Property::SIZE, layoutSize)
+      .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
+      .Add(Toolkit::Visual::Transform::Property::OFFSET, visualTransformOffset)
+      .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
+      .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
+      .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN);
+    SetTransformAndSize(visualTransform, textControlSize);
+
+    Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(renderInfo.hasMultipleTextColors).EnableEmoji(renderInfo.containsColorGlyph).EnableStyle(renderInfo.styleEnabled).EnableOverlay(renderInfo.isOverlayStyle));
+    mImpl->mRenderer.SetShader(shader);
+
+    // Remove the texture set and any renderer previously set.
+    RemoveRenderer(control, false);
+
+    // Get the maximum texture size.
+    const int maxTextureSize = Dali::GetMaxTextureSize();
+
+    // No tiling required. Use the default renderer.
+    if(renderInfo.height < static_cast<uint32_t>(maxTextureSize))
+    {
+      // Filter mode needs to be set to linear to produce better quality while scaling.
+      Sampler sampler = Sampler::New();
+      sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
+
+      TextureSet textureSet = TextureSet::New();
+
+      uint32_t textureSetIndex = 0u;
+      AddTexture(textureSet, renderInfo.textPixelData, sampler, textureSetIndex);
+      ++textureSetIndex;
+
+      if(mTextShaderFeatureCache.IsEnabledStyle())
+      {
+        // Create RGBA texture for all the text styles that render in the background (without the text itself)
+        AddTexture(textureSet, renderInfo.stylePixelData, sampler, textureSetIndex);
+        ++textureSetIndex;
+      }
+      if(mTextShaderFeatureCache.IsEnabledOverlay())
+      {
+        // Create RGBA texture for overlay styles such as underline and strikethrough (without the text itself)
+        AddTexture(textureSet, renderInfo.overlayStylePixelData, sampler, textureSetIndex);
+        ++textureSetIndex;
+      }
+
+      if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
+      {
+        // Create a L8 texture as a mask to avoid color glyphs (e.g. emojis) to be affected by text color animation
+        AddTexture(textureSet, renderInfo.maskPixelData, sampler, textureSetIndex);
+      }
+
+      mImpl->mRenderer.SetTextures(textureSet);
+      // Register transform properties
+      mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
+      mImpl->mRenderer.SetProperty(mHasMultipleTextColorsIndex, static_cast<float>(mTextShaderFeatureCache.IsEnabledMultiColor()));
+      mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+      mRendererList.push_back(mImpl->mRenderer);
+    }
+    else
+    {
+      // Filter mode needs to be set to linear to produce better quality while scaling.
+      Sampler sampler = Sampler::New();
+      sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
+
+      int verifiedWidth  = static_cast<int>(renderInfo.width);
+      int verifiedHeight = static_cast<int>(renderInfo.height);
+
+      // Set information for creating textures.
+      TilingInfo info(verifiedWidth, maxTextureSize);
+
+      // Get the pixel data of text.
+      info.textPixelData = renderInfo.textPixelData;
+
+      if(mTextShaderFeatureCache.IsEnabledStyle())
+      {
+        info.stylePixelData = renderInfo.stylePixelData;
+      }
+
+      if(mTextShaderFeatureCache.IsEnabledOverlay())
+      {
+        info.overlayStylePixelData = renderInfo.overlayStylePixelData;
+      }
+
+      if(mTextShaderFeatureCache.IsEnabledEmoji() && !mTextShaderFeatureCache.IsEnabledMultiColor())
+      {
+        info.maskPixelData = renderInfo.maskPixelData;
+      }
+
+      // Get the current offset for recalculate the offset when tiling.
+      Property::Map retMap;
+      mImpl->mTransform.GetPropertyMap(retMap);
+      Property::Value* offsetValue = retMap.Find(Dali::Toolkit::Visual::Transform::Property::OFFSET);
+      if(offsetValue)
+      {
+        offsetValue->Get(info.transformOffset);
+      }
+
+      // Create a textureset in the default renderer.
+      CreateTextureSet(info, mImpl->mRenderer, sampler);
+
+      verifiedHeight -= maxTextureSize;
+
+      Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
+
+      // Create a renderer by cutting maxTextureSize.
+      while(verifiedHeight > 0)
+      {
+        VisualRenderer tilingRenderer = VisualRenderer::New(geometry, shader);
+        tilingRenderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Toolkit::DepthIndex::CONTENT);
+        // New offset position of buffer for tiling.
+        info.offsetHeight += static_cast<uint32_t>(maxTextureSize);
+        // New height for tiling.
+        info.height = (verifiedHeight - maxTextureSize) > 0 ? maxTextureSize : verifiedHeight;
+        // New offset for tiling.
+        info.transformOffset.y += static_cast<float>(maxTextureSize);
+
+        // Create a textureset int the new tiling renderer.
+        CreateTextureSet(info, tilingRenderer, sampler);
+
+        verifiedHeight -= maxTextureSize;
+      }
+    }
+
+    mImpl->mFlags &= ~Visual::Base::Impl::IS_ATLASING_APPLIED;
+
+    const Vector4& defaultColor = parameters.textColor;
+
+    for(RendererContainer::iterator iter = mRendererList.begin(); iter != mRendererList.end(); ++iter)
+    {
+      Renderer renderer = (*iter);
+      if(renderer)
+      {
+        control.AddRenderer(renderer);
+
+        if(renderer != mImpl->mRenderer)
+        {
+          // Set constraint for text label's color for non-default renderers.
+          if(mAnimatableTextColorPropertyIndex != Property::INVALID_INDEX)
+          {
+            // Register unique property, or get property for default renderer.
+            Property::Index index = renderer.RegisterUniqueProperty("uTextColorAnimatable", defaultColor);
+
+            // Create constraint for the animatable text's color Property with uTextColorAnimatable in the renderer.
+            if(index != Property::INVALID_INDEX)
+            {
+              Constraint colorConstraint = Constraint::New<Vector4>(renderer, index, TextColorConstraint);
+              colorConstraint.AddSource(Source(control, mAnimatableTextColorPropertyIndex));
+              colorConstraint.Apply();
+            }
+
+            // Make zero if the alpha value of text color is zero to skip rendering text
+            // VisualRenderer::Property::OPACITY uses same animatable property internally.
+            Constraint opacityConstraint = Constraint::New<float>(renderer, Dali::DevelRenderer::Property::OPACITY, OpacityConstraint);
+            opacityConstraint.AddSource(Source(control, mAnimatableTextColorPropertyIndex));
+            opacityConstraint.AddSource(Source(mImpl->mRenderer, mTextRequireRenderPropertyIndex));
+            opacityConstraint.Apply();
+          }
+        }
+      }
+    }
+
+    if(mAsyncTextInterface && parameters.isAutoScrollEnabled)
+    {
+      mAsyncTextInterface->AsyncSetupAutoScroll(renderInfo);
+    }
+
+    if(mAsyncTextInterface && parameters.isTextFitEnabled)
+    {
+      mAsyncTextInterface->AsyncTextFitChanged(parameters.fontSize);
+    }
+
+    if(mAsyncTextInterface)
+    {
+      mAsyncTextInterface->AsyncLoadComplete(renderInfo);
+    }
+
+    // Ignore current result when user re-request async load during load complete callback.
+    if(mIsTextLoadingTaskRunning)
+    {
+      // Remove the texture set and any renderer previously set.
+      RemoveRenderer(control, true);
+      return;
+    }
+  }
+  else
+  {
+    resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
+  }
+
+  // Signal to observers ( control ) that resources are ready. Must be all resources.
+  ResourceReady(resourceStatus);
+}
+
+void TextVisual::SetAsyncTextInterface(Text::AsyncTextInterface* asyncTextInterface)
+{
+  mAsyncTextInterface = asyncTextInterface;
+}
+
+void TextVisual::RequestAsyncSizeComputation(Text::AsyncTextParameters& parameters)
+{
+#ifdef TRACE_ENABLED
+  if(gTraceFilter2 && gTraceFilter2->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("Request size computation, type:%s\n", GetRequestTypeName(parameters.requestType));
+  }
+#endif
+
+  switch(parameters.requestType)
+  {
+    case Text::Async::COMPUTE_NATURAL_SIZE:
+    {
+      if(mIsNaturalSizeTaskRunning)
+      {
+        Text::AsyncTextManager::Get().RequestCancel(mNaturalSizeTaskId);
+      }
+      mIsNaturalSizeTaskRunning = true;
+
+      TextLoadObserver* textLoadObserver = this;
+      mNaturalSizeTaskId                 = Text::AsyncTextManager::Get().RequestLoad(parameters, textLoadObserver);
+      break;
+    }
+    case Text::Async::COMPUTE_HEIGHT_FOR_WIDTH:
+    {
+      if(mIsHeightForWidthTaskRunning)
+      {
+        Text::AsyncTextManager::Get().RequestCancel(mHeightForWidthTaskId);
+      }
+      mIsHeightForWidthTaskRunning = true;
+
+      TextLoadObserver* textLoadObserver = this;
+      mHeightForWidthTaskId              = Text::AsyncTextManager::Get().RequestLoad(parameters, textLoadObserver);
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unexpected request type : %d\n", parameters.requestType);
+      break;
+    }
+  }
+}
+
+bool TextVisual::UpdateAsyncRenderer(Text::AsyncTextParameters& parameters)
+{
+  Actor control = mControl.GetHandle();
+  if(!control)
+  {
+    // Nothing to do.
+    ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+    return false;
+  }
+
+  if((fabsf(parameters.textWidth) < Math::MACHINE_EPSILON_1000) || (fabsf(parameters.textHeight) < Math::MACHINE_EPSILON_1000) ||
+     parameters.text.empty())
+  {
+    if(mIsTextLoadingTaskRunning)
+    {
+      Text::AsyncTextManager::Get().RequestCancel(mTextLoadingTaskId);
+      mIsTextLoadingTaskRunning = false;
+    }
+
+    // Remove the texture set and any renderer previously set.
+    RemoveRenderer(control, true);
+
+    // Nothing else to do if the relayout size is zero.
+    ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+
+    if(mAsyncTextInterface)
+    {
+      Text::AsyncTextRenderInfo renderInfo;
+      if(parameters.requestType == Text::Async::RENDER_FIXED_SIZE)
+      {
+        renderInfo.renderedSize = Size(parameters.textWidth, parameters.textHeight);
+      }
+      else if(parameters.requestType == Text::Async::RENDER_FIXED_WIDTH)
+      {
+        renderInfo.renderedSize = Size(parameters.textWidth, 0.0f);
+      }
+      else
+      {
+        renderInfo.renderedSize = Size::ZERO;
+      }
+
+      renderInfo.manualRendered = parameters.manualRender;
+      mAsyncTextInterface->AsyncLoadComplete(renderInfo);
+    }
+
+    return true;
+  }
+
+  // Get the maximum texture size.
+  const int maxTextureSize = Dali::GetMaxTextureSize();
+
+  if(parameters.textWidth > maxTextureSize)
+  {
+    DALI_LOG_WARNING("layoutSize(%f) > maxTextureSize(%d): To guarantee the behavior of Texture::New, layoutSize must not be bigger than maxTextureSize\n", parameters.textWidth, maxTextureSize);
+    parameters.textWidth = maxTextureSize;
+  }
+
+  // This does not mean whether task is actually running or waiting.
+  // It is whether text visual received a completion callback after requesting a task.
+  if(mIsTextLoadingTaskRunning)
+  {
+    Text::AsyncTextManager::Get().RequestCancel(mTextLoadingTaskId);
+  }
+
+#ifdef TRACE_ENABLED
+  if(gTraceFilter2 && gTraceFilter2->IsTraceEnabled())
+  {
+    DALI_LOG_RELEASE_INFO("Request render, type:%s\n", GetRequestTypeName(parameters.requestType));
+  }
+#endif
+
+  mIsTextLoadingTaskRunning          = true;
+  TextLoadObserver* textLoadObserver = this;
+  mTextLoadingTaskId                 = Text::AsyncTextManager::Get().RequestLoad(parameters, textLoadObserver);
+
+  return true;
+}
+
 void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultipleTextColors, bool containsColorGlyph, bool styleEnabled, bool isOverlayStyle)
 {
   Shader shader = GetTextShader(mFactoryCache, TextVisualShaderFeature::FeatureBuilder().EnableMultiColor(hasMultipleTextColors).EnableEmoji(containsColorGlyph).EnableStyle(styleEnabled).EnableOverlay(isOverlayStyle));
@@ -694,7 +1165,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     TextureSet textureSet = GetTextTexture(size);
 
     mImpl->mRenderer.SetTextures(textureSet);
-    //Register transform properties
+    // Register transform properties
     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
     mImpl->mRenderer.SetProperty(mHasMultipleTextColorsIndex, static_cast<float>(hasMultipleTextColors));
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
@@ -721,7 +1192,7 @@ void TextVisual::AddRenderer(Actor& actor, const Vector2& size, bool hasMultiple
     int verifiedHeight = data.GetHeight();
 
     // Set information for creating textures.
-    TilingInfo info(verifiedWidth, maxTextureSize, textPixelFormat);
+    TilingInfo info(verifiedWidth, maxTextureSize);
 
     // Get the pixel data of text.
     info.textPixelData = data;
@@ -832,20 +1303,20 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size)
   Pixel::Format textPixelFormat = (mTextShaderFeatureCache.IsEnabledEmoji() || mTextShaderFeatureCache.IsEnabledMultiColor() || cutoutEnabled) ? Pixel::RGBA8888 : Pixel::L8;
 
   // Check the text direction
-  Toolkit::DevelText::TextDirection::Type textDirection = mController->GetTextDirection();
-  uint32_t textureSetIndex = 0u;
+  Toolkit::DevelText::TextDirection::Type textDirection   = mController->GetTextDirection();
+  uint32_t                                textureSetIndex = 0u;
   // Create a texture for the text without any styles
 
   Devel::PixelBuffer cutoutData;
-  float cutoutAlpha = mController->GetTextModel()->GetDefaultColor().a;
+  float              cutoutAlpha = mController->GetTextModel()->GetDefaultColor().a;
   if(cutoutEnabled)
   {
     cutoutData = mTypesetter->RenderWithPixelBuffer(size, textDirection, Text::Typesetter::RENDER_NO_STYLES, false, textPixelFormat);
 
     // Make transparent buffer.
     // If the cutout is enabled, a separate texture is not used for the text.
-    Devel::PixelBuffer buffer = mTypesetter->CreateFullBackgroundBuffer(1, 1, Vector4(0.f, 0.f ,0.f ,0.f));
-    PixelData data = Devel::PixelBuffer::Convert(buffer);
+    Devel::PixelBuffer buffer = mTypesetter->CreateFullBackgroundBuffer(1, 1, Vector4(0.f, 0.f, 0.f, 0.f));
+    PixelData          data   = Devel::PixelBuffer::Convert(buffer);
     AddTexture(textureSet, data, sampler, textureSetIndex);
     ++textureSetIndex;
   }
@@ -856,7 +1327,6 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size)
     ++textureSetIndex;
   }
 
-
   if(mTextShaderFeatureCache.IsEnabledStyle())
   {
     // Create RGBA texture for all the text styles that render in the background (without the text itself)
@@ -894,7 +1364,7 @@ TextureSet TextVisual::GetTextTexture(const Vector2& size)
   return textureSet;
 }
 
-Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder)
+Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, TextVisualShaderFeature::FeatureBuilder& featureBuilder)
 {
   // Cache feature builder informations.
   mTextShaderFeatureCache = featureBuilder;
@@ -905,10 +1375,14 @@ Shader TextVisual::GetTextShader(VisualFactoryCache& factoryCache, const TextVis
 
 void TextVisual::SetRequireRender(bool requireRender)
 {
-  mTextRequireRender = requireRender;
-  if(mImpl->mRenderer)
+  // Avoid function calls if there is no change.
+  if(mTextRequireRender != requireRender)
   {
-    mImpl->mRenderer.SetProperty(mTextRequireRenderPropertyIndex, mTextRequireRender);
+    mTextRequireRender = requireRender;
+    if(mImpl->mRenderer)
+    {
+      mImpl->mRenderer.SetProperty(mTextRequireRenderPropertyIndex, mTextRequireRender);
+    }
   }
 }
 
index 0d5e5f8..6961148 100644 (file)
 #include <dali/public-api/rendering/visual-renderer.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/async-text/async-text-interface.h>
+#include <dali-toolkit/internal/text/async-text/async-text-manager.h>
 #include <dali-toolkit/internal/text/controller/text-controller.h>
 #include <dali-toolkit/internal/text/rendering/text-typesetter.h>
-#include <dali-toolkit/internal/visuals/text-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/text/text-visual-shader-factory.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 
 namespace Dali
@@ -66,7 +68,7 @@ typedef IntrusivePtr<TextVisual> TextVisualPtr;
  * | outline             | STRING  |
  *
  */
-class TextVisual : public Visual::Base
+class TextVisual : public Visual::Base, public TextLoadObserver
 {
 public:
   /**
@@ -134,6 +136,37 @@ public:
     GetVisualObject(visual).UpdateRenderer();
   };
 
+  /**
+   * @brief Instantly updates the async renderer
+   * @param[in] visual The text visual.
+   * @param[in] parameters The async text parameters.
+   * @return true if the async text render request was successful, false otherwise.
+   */
+  static bool UpdateAsyncRenderer(Toolkit::Visual::Base visual, Text::AsyncTextParameters& parameters)
+  {
+    return GetVisualObject(visual).UpdateAsyncRenderer(parameters);
+  };
+
+  /**
+   * @brief Instantly requests the async size computation.
+   * @param[in] visual The text visual.
+   * @param[in] parameters The async text parameters.
+   */
+  static void RequestAsyncSizeComputation(Toolkit::Visual::Base visual, Text::AsyncTextParameters& parameters)
+  {
+    GetVisualObject(visual).RequestAsyncSizeComputation(parameters);
+  };
+
+  /**
+   * @brief Set the control's async text interface.
+   * @param[in] visual The text visual.
+   * @param[in] asyncTextInterface The async text interface.
+   */
+  static void SetAsyncTextInterface(Toolkit::Visual::Base visual, Text::AsyncTextInterface* asyncTextInterface)
+  {
+    GetVisualObject(visual).SetAsyncTextInterface(asyncTextInterface);
+  };
+
 public: // from Visual::Base
   /**
    * @copydoc Visual::Base::GetHeightForWidth()
@@ -204,24 +237,22 @@ protected:
 private:
   struct TilingInfo
   {
-    PixelData     textPixelData;
-    PixelData     stylePixelData;
-    PixelData     overlayStylePixelData;
-    PixelData     maskPixelData;
-    int32_t       width;
-    int32_t       height;
-    Pixel::Format textPixelFormat;
-    uint32_t      offsetHeight;
-    Vector2       transformOffset;
-
-    TilingInfo(int32_t width, int32_t height, Pixel::Format textPixelFormat)
+    PixelData textPixelData;
+    PixelData stylePixelData;
+    PixelData overlayStylePixelData;
+    PixelData maskPixelData;
+    int32_t   width;
+    int32_t   height;
+    uint32_t  offsetHeight;
+    Vector2   transformOffset;
+
+    TilingInfo(int32_t width, int32_t height)
     : textPixelData(),
       stylePixelData(),
       overlayStylePixelData(),
       maskPixelData(),
       width(width),
       height(height),
-      textPixelFormat(textPixelFormat),
       offsetHeight(0u),
       transformOffset(0.f, 0.f)
     {
@@ -247,6 +278,25 @@ private:
   void UpdateRenderer();
 
   /**
+   * @brief Updates the text's async renderer.
+   * @param[in] parameters The async text parameters.
+   * @return true if the async text render request was successful, false otherwise.
+   */
+  bool UpdateAsyncRenderer(Text::AsyncTextParameters& parameters);
+
+  /**
+   * @brief Requests the async size computation.
+   * @param[in] parameters The async text parameters.
+   */
+  void RequestAsyncSizeComputation(Text::AsyncTextParameters& parameters);
+
+  /**
+   * @brief Set the control's async text interface.
+   * @param[in] asyncTextInterface The async text interface.
+   */
+  void SetAsyncTextInterface(Text::AsyncTextInterface* asyncTextInterface);
+
+  /**
    * @brief Removes the text's renderer.
    */
   void RemoveRenderer(Actor& actor, bool removeDefaultRenderer);
@@ -300,7 +350,7 @@ private:
    * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
    * @param[in] featureBuilder Collection of current text shader's features. It will be cached as text visual.
    */
-  Shader GetTextShader(VisualFactoryCache& factoryCache, const TextVisualShaderFeature::FeatureBuilder& featureBuilder);
+  Shader GetTextShader(VisualFactoryCache& factoryCache, TextVisualShaderFeature::FeatureBuilder& featureBuilder);
 
   /**
    * @brief Set the text to be always rendered
@@ -318,12 +368,23 @@ private:
     return static_cast<TextVisual&>(Toolkit::GetImplementation(visual).GetVisualObject());
   };
 
+  /**
+   * @copydoc TextLoadObserver::LoadComplete
+   *
+   * Called when the TextLoadingTask's work is complete.
+   *
+   * @param[in] success True if the load was successful, false otherwise.
+   * @param[in] textInformation The text information including render info and parameters.
+   */
+  void LoadComplete(bool success, const TextInformation& textInformation) override;
+
 private:
   typedef std::vector<Renderer> RendererContainer;
 
 private:
-  Text::ControllerPtr mController; ///< The text's controller.
-  Text::TypesetterPtr mTypesetter; ///< The text's typesetter.
+  Text::ControllerPtr       mController;         ///< The text's controller.
+  Text::TypesetterPtr       mTypesetter;         ///< The text's typesetter.
+  Text::AsyncTextInterface* mAsyncTextInterface; ///< The text's async interface.
 
   TextVisualShaderFactory&                mTextVisualShaderFactory; ///< The shader factory for text visual.
   TextVisualShaderFeature::FeatureBuilder mTextShaderFeatureCache;  ///< The cached shader feature for text visual.
@@ -338,6 +399,13 @@ private:
   bool              mRendererUpdateNeeded : 1;         ///< The flag to indicate whether the renderer needs to be updated.
   bool              mTextRequireRender : 1;            ///< The flag to indicate whether the text needs to be rendered.
   RendererContainer mRendererList;
+
+  uint32_t mTextLoadingTaskId;               ///< The currently requested text loading(render) task Id.
+  uint32_t mNaturalSizeTaskId;               ///< The currently requested natural size task Id.
+  uint32_t mHeightForWidthTaskId;            ///< The currently requested height for width task Id.
+  bool     mIsTextLoadingTaskRunning : 1;    ///< Whether the requested text loading task is running or not.
+  bool     mIsNaturalSizeTaskRunning : 1;    ///< Whether the requested natural size task is running or not.
+  bool     mIsHeightForWidthTaskRunning : 1; ///< Whether the requested height for width task is running or not.
 };
 
 } // namespace Internal
index aee602d..029a4cc 100644 (file)
@@ -128,7 +128,10 @@ Internal::Visual::Base::Impl::Impl(FittingMode fittingMode, Toolkit::Visual::Typ
   mResourceStatus(Toolkit::Visual::ResourceStatus::PREPARING),
   mType(type),
   mAlwaysUsingBorderline(false),
-  mAlwaysUsingCornerRadius(false)
+  mAlwaysUsingCornerRadius(false),
+  mIgnoreFittingMode(false),
+  mPixelAreaSetByFittingMode(false),
+  mTransformMapSetForFittingMode(false)
 {
 }
 
index e54c3ae..d8e7f34 100644 (file)
@@ -254,8 +254,11 @@ struct Base::Impl
   int                             mFlags;
   Toolkit::Visual::ResourceStatus mResourceStatus;
   const Toolkit::Visual::Type     mType;
-  bool                            mAlwaysUsingBorderline : 1;     ///< Whether we need the borderline in shader always.
-  bool                            mAlwaysUsingCornerRadius : 1;   ///< Whether we need the corner radius in shader always.
+  bool                            mAlwaysUsingBorderline : 1;        ///< Whether we need the borderline in shader always.
+  bool                            mAlwaysUsingCornerRadius : 1;      ///< Whether we need the corner radius in shader always.
+  bool                            mIgnoreFittingMode : 1;            ///< Whether we need to ignore fitting mode.
+  bool                            mPixelAreaSetByFittingMode : 1;    ///< Whether the pixel area is set for fitting mode.
+  bool                            mTransformMapSetForFittingMode :1; ///< Whether the transformMap is set for fitting mode.
 };
 
 } // namespace Visual
index ed6e00c..e830fa9 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL HEADER
 #include <dali-toolkit/public-api/dali-toolkit-common.h>
+#include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
 #include <dali/devel-api/scripting/enum-helper.h>
 #include <dali/integration-api/debug.h>
@@ -56,6 +57,8 @@ namespace Toolkit
 {
 namespace Internal
 {
+const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+
 namespace
 {
 DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_FITTING_MODE)
@@ -65,6 +68,7 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_FITTING_MODE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, CENTER)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_HEIGHT)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_WIDTH)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, DONT_CARE)
 DALI_ENUM_TO_STRING_TABLE_END(VISUAL_FITTING_MODE)
 
 /**
@@ -160,10 +164,18 @@ Visual::Base::Base(VisualFactoryCache& factoryCache, FittingMode fittingMode, To
 : mImpl(new Impl(fittingMode, type)),
   mFactoryCache(factoryCache)
 {
+  if(DALI_UNLIKELY(!Dali::Stage::IsCoreThread()))
+  {
+    DALI_LOG_ERROR("Visual::Base[%p] called from non-UI thread! something unknown issue will be happened!\n", this);
+  }
 }
 
 Visual::Base::~Base()
 {
+  if(DALI_UNLIKELY(!Dali::Stage::IsCoreThread()))
+  {
+    DALI_LOG_ERROR("Visual::~Base[%p] called from non-UI thread! something unknown issue will be happened!\n", this);
+  }
   delete mImpl;
 }
 
@@ -494,7 +506,7 @@ void Visual::Base::GetNaturalSize(Vector2& naturalSize)
   naturalSize = Vector2::ZERO;
 }
 
-void Visual::Base::DoAction(const Property::Index actionId, const Property::Value attributes)
+void Visual::Base::DoAction(const Property::Index actionId, const Property::Value& attributes)
 {
   OnDoAction(actionId, attributes);
 
@@ -513,7 +525,7 @@ void Visual::Base::DoAction(const Property::Index actionId, const Property::Valu
   }
 }
 
-void Visual::Base::DoActionExtension(const Dali::Property::Index actionId, const Dali::Any attributes)
+void Visual::Base::DoActionExtension(const Dali::Property::Index actionId, const Dali::Any& attributes)
 {
   OnDoActionExtension(actionId, attributes);
 }
@@ -577,8 +589,7 @@ void Visual::Base::CreatePropertyMap(Property::Map& map) const
   if(mImpl->mRenderer)
   {
     // Update values from Renderer
-    mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(VisualRenderer::Property::VISUAL_MIX_COLOR);
-    mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
+    mImpl->mMixColor = mImpl->mRenderer.GetProperty<Vector4>(Renderer::Property::MIX_COLOR);
 
     mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_OFFSET);
     mImpl->mTransform.mSize   = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_SIZE);
@@ -656,7 +667,6 @@ void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
   if(mImpl->mRenderer)
   {
     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
-    mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultiplied);
   }
 }
 
@@ -728,7 +738,7 @@ void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Va
   // May be overriden by derived class
 }
 
-void Visual::Base::OnDoActionExtension(const Property::Index actionId, const Dali::Any attributes)
+void Visual::Base::OnDoActionExtension(const Property::Index actionId, const Dali::Any& attributes)
 {
   // May be overriden by derived class
 }
@@ -738,15 +748,9 @@ void Visual::Base::RegisterMixColor()
   if(mImpl->mRenderer)
   {
     // All visual renderers now use same mix color / opacity properties.
-    mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(mImpl->mMixColor));
-    mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
+    mImpl->mRenderer.SetProperty(Renderer::Property::MIX_COLOR, mImpl->mMixColor);
 
-    float preMultipliedAlpha = 0.0f;
-    if(IsPreMultipliedAlphaEnabled())
-    {
-      preMultipliedAlpha = 1.0f;
-    }
-    mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultipliedAlpha);
+    mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
   }
 }
 
@@ -782,8 +786,7 @@ void Visual::Base::SetMixColor(const Vector4& color)
 
   if(mImpl->mRenderer)
   {
-    mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(color));
-    mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
+    mImpl->mRenderer.SetProperty(Renderer::Property::MIX_COLOR, color);
   }
 }
 
@@ -795,7 +798,9 @@ void Visual::Base::SetMixColor(const Vector3& color)
 
   if(mImpl->mRenderer)
   {
-    mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, color);
+    mImpl->mRenderer.SetProperty(Renderer::Property::MIX_COLOR_RED, color.r);
+    mImpl->mRenderer.SetProperty(Renderer::Property::MIX_COLOR_GREEN, color.g);
+    mImpl->mRenderer.SetProperty(Renderer::Property::MIX_COLOR_BLUE, color.b);
   }
 }
 
@@ -849,6 +854,41 @@ Visual::FittingMode Visual::Base::GetFittingMode() const
   return mImpl->mFittingMode;
 }
 
+void Visual::Base::SetFittingMode(Visual::FittingMode fittingMode)
+{
+  mImpl->mFittingMode = fittingMode;
+}
+
+bool Visual::Base::IsIgnoreFittingMode() const
+{
+  return mImpl->mIgnoreFittingMode;
+}
+
+bool Visual::Base::IsPixelAreaSetForFittingMode() const
+{
+  return mImpl->mPixelAreaSetByFittingMode;
+}
+
+void Visual::Base::SetPixelAreaForFittingMode(const Vector4& pixelArea)
+{
+  if(mImpl->mRenderer)
+  {
+    mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, pixelArea);
+  }
+
+  mImpl->mPixelAreaSetByFittingMode = (pixelArea != FULL_TEXTURE_RECT) ? true : false;
+}
+
+bool Visual::Base::IsTransformMapSetForFittingMode() const
+{
+  return mImpl->mTransformMapSetForFittingMode;
+}
+
+void Visual::Base::SetTransformMapUsageForFittingMode(bool used)
+{
+  mImpl->mTransformMapSetForFittingMode = used;
+}
+
 Visual::Base& Visual::Base::GetVisualObject()
 {
   return *this;
@@ -964,15 +1004,15 @@ Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
     }
     case Dali::Toolkit::Visual::Property::MIX_COLOR:
     {
-      return VisualRenderer::Property::VISUAL_MIX_COLOR;
+      return Renderer::Property::MIX_COLOR;
     }
     case Dali::Toolkit::Visual::Property::OPACITY:
     {
-      return DevelRenderer::Property::OPACITY;
+      return Renderer::Property::OPACITY;
     }
     case Dali::Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
     {
-      return VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA;
+      return Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA;
     }
     case Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS:
     {
@@ -1028,8 +1068,8 @@ void Visual::Base::SetupTransition(
   Dali::Animation&                    transition,
   Internal::TransitionData::Animator& animator,
   Property::Index                     index,
-  Property::Value&                    initialValue,
-  Property::Value&                    targetValue)
+  const Property::Value&              initialValue,
+  const Property::Value&              targetValue)
 {
   if(index != Property::INVALID_INDEX)
   {
@@ -1059,7 +1099,7 @@ void Visual::Base::SetupTransition(
           Dali::KeyFrames keyFrames = Dali::KeyFrames::New();
           keyFrames.Add(0.0f, animator.initialValue);
           keyFrames.Add(1.0f, animator.targetValue);
-          transition.AnimateBetween(Property(mImpl->mRenderer, index),keyFrames, TimePeriod(animator.timePeriodDelay, animator.timePeriodDuration));
+          transition.AnimateBetween(Property(mImpl->mRenderer, index), keyFrames, TimePeriod(animator.timePeriodDelay, animator.timePeriodDuration));
         }
         else if(animator.animationType == TransitionData::AnimationType::BY)
         {
@@ -1069,10 +1109,10 @@ void Visual::Base::SetupTransition(
         else
         {
           transition.AnimateTo(Property(mImpl->mRenderer, index),
-                      targetValue,
-                      animator.alphaFunction,
-                      TimePeriod(animator.timePeriodDelay,
-                                animator.timePeriodDuration));
+                               targetValue,
+                               animator.alphaFunction,
+                               TimePeriod(animator.timePeriodDelay,
+                                          animator.timePeriodDuration));
         }
       }
     }
@@ -1122,7 +1162,7 @@ void Visual::Base::AnimateOpacityProperty(
     mImpl->mMixColor.a = targetOpacity;
   }
 
-  SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
+  SetupTransition(transition, animator, Renderer::Property::OPACITY, animator.initialValue, animator.targetValue);
 }
 
 void Visual::Base::AnimateRendererProperty(
@@ -1148,6 +1188,8 @@ void Visual::Base::AnimateRendererProperty(
         map.Add(animator.propertyKey.stringKey, animator.targetValue);
       }
 
+      // Set flag to ignore fitting mode when we set the transform property map
+      mImpl->mIgnoreFittingMode = true;
       mImpl->mTransform.UpdatePropertyMap(map);
     }
     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
@@ -1160,45 +1202,41 @@ void Visual::Base::AnimateMixColorProperty(
 {
   bool animateOpacity = false;
 
-  Property::Value initialOpacity;
-  Property::Value targetOpacity;
-  Property::Value initialMixColor;
-  Property::Value targetMixColor;
-
-  Vector4 initialColor;
-  if(animator.initialValue.Get(initialColor))
+  Vector4 initialMixColor;
+  Vector4 targetMixColor;
+  if(animator.initialValue.Get(initialMixColor))
   {
-    if(animator.initialValue.GetType() == Property::VECTOR4)
+    if(animator.initialValue.GetType() != Property::VECTOR4)
     {
-      // if there is an initial color specifying alpha, test it
-      initialOpacity = initialColor.a;
+      // if there is non initial color specifying alpha, get from cached mix color
+      initialMixColor.a = mImpl->mMixColor.a;
     }
-    initialMixColor = Vector3(initialColor);
   }
 
   // Set target value into data store
   if(animator.targetValue.GetType() != Property::NONE)
   {
-    Vector4 mixColor;
-    animator.targetValue.Get(mixColor);
+    animator.targetValue.Get(targetMixColor);
     if(animator.targetValue.GetType() == Property::VECTOR4)
     {
-      mImpl->mMixColor.a = mixColor.a;
-      targetOpacity      = mixColor.a;
+      mImpl->mMixColor.a = targetMixColor.a;
       animateOpacity     = true;
     }
 
-    mImpl->mMixColor.r = mixColor.r;
-    mImpl->mMixColor.g = mixColor.g;
-    mImpl->mMixColor.b = mixColor.b;
-    targetMixColor     = Vector3(mixColor);
+    mImpl->mMixColor.r = targetMixColor.r;
+    mImpl->mMixColor.g = targetMixColor.g;
+    mImpl->mMixColor.b = targetMixColor.b;
   }
 
-  SetupTransition(transition, animator, VisualRenderer::Property::VISUAL_MIX_COLOR, initialMixColor, targetMixColor);
-
   if(animateOpacity)
   {
-    SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
+    SetupTransition(transition, animator, Renderer::Property::MIX_COLOR, initialMixColor, targetMixColor);
+  }
+  else
+  {
+    SetupTransition(transition, animator, Renderer::Property::MIX_COLOR_RED, initialMixColor.r, targetMixColor.r);
+    SetupTransition(transition, animator, Renderer::Property::MIX_COLOR_GREEN, initialMixColor.g, targetMixColor.g);
+    SetupTransition(transition, animator, Renderer::Property::MIX_COLOR_BLUE, initialMixColor.b, targetMixColor.b);
   }
 }
 
@@ -1215,11 +1253,11 @@ Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
     // Default animatable properties from VisualRenderer
     case Toolkit::Visual::Property::MIX_COLOR:
     {
-      return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
+      return Dali::Property(mImpl->mRenderer, Renderer::Property::MIX_COLOR);
     }
     case Toolkit::Visual::Property::OPACITY:
     {
-      return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
+      return Dali::Property(mImpl->mRenderer, Renderer::Property::OPACITY);
     }
     case Toolkit::Visual::Transform::Property::OFFSET:
     {
@@ -1294,7 +1332,7 @@ Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
          ((mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) ||
           (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR)))
       {
-        return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
+        return Dali::Property(mImpl->mRenderer, Renderer::Property::MIX_COLOR);
       }
 
       // Special case for BLUR_RADIUS
index 7ebf239..b5c002e 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_VISUAL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -92,22 +92,6 @@ public:
   void SetTransformAndSize(const Property::Map& transform, Size controlSize);
 
   /**
-   * @brief Performs an action on the visual with the given action id and attributes.
-   *
-   * @param[in] actionId The id of the action to perform this API only takes an Index
-   * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
-   */
-  void DoAction(const Dali::Property::Index actionId, const Dali::Property::Value attributes);
-
-  /**
-   * @brief Performs an action on the visual with the given action id and attributes.
-   *
-   * @param[in] actionId The id of the action to perform this API only takes an Index
-   * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
-   */
-  void DoActionExtension(const Dali::Property::Index actionId, const Dali::Any attributes);
-
-  /**
    * @copydoc Toolkit::Visual::Base::GetHeightForWidth
    */
   virtual float GetHeightForWidth(float width);
@@ -149,6 +133,16 @@ public:
   void CreatePropertyMap(Property::Map& map) const;
 
   /**
+   * @copydoc Toolkit::Visual::Base::DoAction
+   */
+  void DoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes);
+
+  /**
+   * @copydoc Toolkit::Visual::Base::DoActionExtension
+   */
+  void DoActionExtension(const Dali::Property::Index actionId, const Dali::Any& attributes);
+
+  /**
    * @brief Create a property map containing per-instance visual properties.
    *
    * This will enable creation of new visuals on control state change with
@@ -264,6 +258,42 @@ public:
   FittingMode GetFittingMode() const;
 
   /**
+   * @brief Set the fitting mode for the visual
+   *
+   * @param[in] fittingMode The fitting mode for the visual
+   */
+  void SetFittingMode(FittingMode fittingMode);
+
+  /**
+   * @brief Query whether the fittingMode is ignored.
+   *
+   * @return Returns true if the fittingMode is ignored, false otherwise.
+   */
+  bool IsIgnoreFittingMode() const;
+
+  /**
+   * @brief Query whether the pixel area is set by fitting mode.
+   */
+  bool IsPixelAreaSetForFittingMode() const;
+
+  /**
+   * @brief Set the pixel area of the visual.
+   *
+   * @param[in] pixelArea The pixel area of the visual.
+   */
+  void SetPixelAreaForFittingMode(const Vector4& pixelArea);
+
+  /**
+   * @brief Checks whether the trasformMap is being used in the FittingMode situation.
+   */
+  bool IsTransformMapSetForFittingMode() const;
+
+  /**
+   * @brief Set the flag to use transformMap in the FittingMode.
+   */
+  void SetTransformMapUsageForFittingMode(bool used);
+
+  /**
    * @brief Get the actual Visual Object.
    * @return The actual visual object
    * @note Should be overridden by deriving controls if they are acting as a proxy to other visual objects.
@@ -376,7 +406,7 @@ protected:
    * @param[in] actionId The action to perform
    * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
    */
-  virtual void OnDoActionExtension(const Property::Index actionId, Dali::Any attributes);
+  virtual void OnDoActionExtension(const Property::Index actionId, const Dali::Any& attributes);
 
   /**
    * @brief Update the shader when some properties are changed.
@@ -464,8 +494,8 @@ private:
   void SetupTransition(Dali::Animation&                    transition,
                        Internal::TransitionData::Animator& animator,
                        Property::Index                     index,
-                       Property::Value&                    initialValue,
-                       Property::Value&                    targetValue);
+                       const Property::Value&              initialValue,
+                       const Property::Value&              targetValue);
 
   /**
    * Animate the opacity property - Special handling to
index e8b5dbf..edf965c 100644 (file)
@@ -31,7 +31,7 @@
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h>
 #include <dali-toolkit/internal/visuals/color/color-visual.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/internal/visuals/image/image-atlas-manager.h>
 #include <dali-toolkit/internal/visuals/svg/svg-visual.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 
@@ -65,6 +65,7 @@ VisualFactoryCache::VisualFactoryCache(bool preMultiplyOnLoad)
   mDefaultBrokenImageUrl(""),
   mUseDefaultBrokenImageOnly(true)
 {
+  mSvgLoader.SetVisualFactoryCache(*this);
 }
 
 VisualFactoryCache::~VisualFactoryCache()
@@ -147,6 +148,11 @@ NPatchLoader& VisualFactoryCache::GetNPatchLoader()
   return mNPatchLoader;
 }
 
+SvgLoader& VisualFactoryCache::GetSvgLoader()
+{
+  return mSvgLoader;
+}
+
 VectorAnimationManager& VisualFactoryCache::GetVectorAnimationManager()
 {
   if(!mVectorAnimationManager)
@@ -156,6 +162,14 @@ VectorAnimationManager& VisualFactoryCache::GetVectorAnimationManager()
   return *mVectorAnimationManager;
 }
 
+void VisualFactoryCache::FinalizeVectorAnimationManager()
+{
+  if(mVectorAnimationManager)
+  {
+    mVectorAnimationManager->Finalize();
+  }
+}
+
 Geometry VisualFactoryCache::CreateGridGeometry(Uint16Pair gridSize)
 {
   uint16_t gridWidth  = gridSize.GetWidth();
@@ -361,7 +375,7 @@ void VisualFactoryCache::ApplyTextureAndUniforms(Renderer& renderer, int index)
   }
 }
 
-void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size, const bool& rendererIsImage)
+void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size, const bool rendererIsImage)
 {
   bool useDefaultBrokenImage = false;
   if(mBrokenImageInfoContainer.size() == 0)
index 1b3f098..cc59ea7 100644 (file)
@@ -18,8 +18,6 @@
  */
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/common/owner-container.h>
-#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
 #include <dali/public-api/math/uint-16-pair.h>
 #include <dali/public-api/object/ref-object.h>
 #include <dali/public-api/rendering/geometry.h>
@@ -27,7 +25,8 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
-#include <dali-toolkit/internal/visuals/npatch-loader.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-loader.h>
+#include <dali-toolkit/internal/visuals/svg/svg-loader.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
 
 namespace Dali
@@ -40,6 +39,7 @@ namespace Internal
 {
 class ImageAtlasManager;
 class NPatchLoader;
+class SvgLoader;
 class TextureManager;
 class VectorAnimationManager;
 
@@ -62,6 +62,12 @@ public:
     COLOR_SHADER_ROUNDED_BORDERLINE,
     COLOR_SHADER_BLUR_EDGE,
     COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE,
+    COLOR_SHADER_CUTOUT,
+    COLOR_SHADER_CUTOUT_ROUNDED_CORNER,
+    COLOR_SHADER_CUTOUT_BORDERLINE,
+    COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE,
+    COLOR_SHADER_CUTOUT_BLUR_EDGE,
+    COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE,
     BORDER_SHADER,
     BORDER_SHADER_ANTI_ALIASING,
     GRADIENT_SHADER_LINEAR_BOUNDING_BOX,
@@ -137,7 +143,7 @@ public:
     WIREFRAME_SHADER,
     ARC_BUTT_CAP_SHADER,
     ARC_ROUND_CAP_SHADER,
-    SHADER_TYPE_MAX = ARC_ROUND_CAP_SHADER
+    SHADER_TYPE_MAX
   };
 
   /**
@@ -150,7 +156,7 @@ public:
     NINE_PATCH_GEOMETRY,
     NINE_PATCH_BORDER_GEOMETRY,
     WIREFRAME_GEOMETRY,
-    GEOMETRY_TYPE_MAX = WIREFRAME_GEOMETRY
+    GEOMETRY_TYPE_MAX
   };
 
 public:
@@ -231,7 +237,7 @@ public:
    *                            If true, we don't need to create new renderer when broken image is single image.
    *                            Most of user experience use normal images. So It can reduce runtime.
    */
-  void UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size, const bool& rendererIsImage = true);
+  void UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size, const bool rendererIsImage = true);
 
   /**
    * @brief Get whether we support YUV Planes load or not.
@@ -260,11 +266,23 @@ public:
   NPatchLoader& GetNPatchLoader();
 
   /**
+   * Get the Svg texture cache.
+   * @return A reference to the Svg loader
+   */
+  SvgLoader& GetSvgLoader();
+
+  /**
    * Get the vector animation manager.
    * @return A reference to the vector animation manager.
    */
   VectorAnimationManager& GetVectorAnimationManager();
 
+  /**
+   * @brief Finalize vector animation manager.
+   * It will be called when application is terminated.
+   */
+  void FinalizeVectorAnimationManager();
+
 protected:
   /**
    * Undefined copy constructor.
@@ -350,14 +368,15 @@ private:
     uint32_t                 height;
   };
 
-  Geometry mGeometry[GEOMETRY_TYPE_MAX + 1];
-  Shader   mShader[SHADER_TYPE_MAX + 1];
+  Geometry mGeometry[GEOMETRY_TYPE_MAX];
+  Shader   mShader[SHADER_TYPE_MAX];
 
   bool mLoadYuvPlanes; ///< A global flag to specify if the image should be loaded as yuv planes
 
   ImageAtlasManagerPtr mAtlasManager;
   TextureManager       mTextureManager;
   NPatchLoader         mNPatchLoader;
+  SvgLoader            mSvgLoader;
 
   std::unique_ptr<VectorAnimationManager> mVectorAnimationManager;
   bool                                    mPreMultiplyOnLoad;
index 91584eb..92b2fa8 100644 (file)
@@ -18,6 +18,7 @@
 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/lifecycle-controller.h>
 #include <dali/devel-api/scripting/scripting.h>
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h>
 #include <dali-toolkit/internal/visuals/arc/arc-visual.h>
 #include <dali-toolkit/internal/visuals/border/border-visual.h>
+#include <dali-toolkit/internal/visuals/color/color-visual-shader-factory.h>
 #include <dali-toolkit/internal/visuals/color/color-visual.h>
+#include <dali-toolkit/internal/visuals/custom-shader-factory.h>
 #include <dali-toolkit/internal/visuals/gradient/gradient-visual.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/image/image-visual-shader-factory.h>
 #include <dali-toolkit/internal/visuals/image/image-visual.h>
 #include <dali-toolkit/internal/visuals/mesh/mesh-visual.h>
+#include <dali-toolkit/internal/visuals/npatch-shader-factory.h>
 #include <dali-toolkit/internal/visuals/npatch/npatch-visual.h>
 #include <dali-toolkit/internal/visuals/primitive/primitive-visual.h>
 #include <dali-toolkit/internal/visuals/svg/svg-visual.h>
-#include <dali-toolkit/internal/visuals/text-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/text/text-visual-shader-factory.h>
 #include <dali-toolkit/internal/visuals/text/text-visual.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
@@ -76,42 +80,43 @@ DALI_TYPE_REGISTRATION_BEGIN_CREATE(Toolkit::VisualFactory, Dali::BaseHandle, Cr
 DALI_TYPE_REGISTRATION_END()
 const char* const BROKEN_IMAGE_FILE_NAME = "broken.png"; ///< The file name of the broken image.
 
-static constexpr auto SHADER_TYPE_COUNT = 2u;
-
-constexpr std::string_view VertexPredefines[SHADER_TYPE_COUNT]{
-  "",                                     //VisualFactoryCache::COLOR_SHADER
-  "#define IS_REQUIRED_ROUNDED_CORNER\n", //VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER
-};
-constexpr std::string_view FragmentPredefines[SHADER_TYPE_COUNT]{
-  "",                                     //VisualFactoryCache::COLOR_SHADER
-  "#define IS_REQUIRED_ROUNDED_CORNER\n", //VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER
-};
-constexpr VisualFactoryCache::ShaderType ShaderTypePredefines[SHADER_TYPE_COUNT]{
-  VisualFactoryCache::ShaderType::COLOR_SHADER,
-  VisualFactoryCache::ShaderType::COLOR_SHADER_ROUNDED_CORNER,
-};
-
 } // namespace
 
 VisualFactory::VisualFactory(bool debugEnabled)
 : mFactoryCache(),
   mImageVisualShaderFactory(),
   mTextVisualShaderFactory(),
+  mColorVisualShaderFactory(),
   mSlotDelegate(this),
   mIdleCallback(nullptr),
+  mDefaultCreationOptions(Toolkit::VisualFactory::CreationOptions::NONE),
   mDebugEnabled(debugEnabled),
   mPreMultiplyOnLoad(true),
   mPrecompiledShaderRequested(false)
 {
+  Dali::LifecycleController lifecycleController = Dali::LifecycleController::Get();
+  if(DALI_LIKELY(lifecycleController))
+  {
+    lifecycleController.TerminateSignal().Connect(this, &VisualFactory::OnApplicationTerminated);
+  }
 }
 
 VisualFactory::~VisualFactory()
 {
-  if(mIdleCallback && Adaptor::IsAvailable())
+  if(Adaptor::IsAvailable())
   {
-    // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
-    Adaptor::Get().RemoveIdle(mIdleCallback);
-    mIdleCallback = nullptr;
+    Dali::LifecycleController lifecycleController = Dali::LifecycleController::Get();
+    if(DALI_LIKELY(lifecycleController))
+    {
+      lifecycleController.TerminateSignal().Disconnect(this, &VisualFactory::OnApplicationTerminated);
+    }
+
+    if(mIdleCallback)
+    {
+      // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
+      Adaptor::Get().RemoveIdle(mIdleCallback);
+      mIdleCallback = nullptr;
+    }
   }
 }
 
@@ -130,6 +135,11 @@ void VisualFactory::OnBrokenImageChangedSignal(Toolkit::StyleManager styleManage
 
 Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyMap)
 {
+  return CreateVisual(propertyMap, mDefaultCreationOptions);
+}
+
+Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyMap, Toolkit::VisualFactory::CreationOptions creationOptions)
+{
   Visual::BasePtr visualPtr;
 
   Property::Value*           typeValue  = propertyMap.Find(Toolkit::Visual::Property::TYPE, VISUAL_TYPE);
@@ -149,7 +159,7 @@ Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyM
 
     case Toolkit::Visual::COLOR:
     {
-      visualPtr = ColorVisual::New(GetFactoryCache(), propertyMap);
+      visualPtr = ColorVisual::New(GetFactoryCache(), GetColorVisualShaderFactory(), propertyMap);
       break;
     }
 
@@ -160,6 +170,7 @@ Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyM
     }
 
     case Toolkit::Visual::IMAGE:
+    case Toolkit::Visual::ANIMATED_IMAGE:
     {
       Property::Value* imageURLValue = propertyMap.Find(Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME);
       std::string      imageUrl;
@@ -184,17 +195,21 @@ Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyM
                 visualPtr = SvgVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, propertyMap);
                 break;
               }
-              case VisualUrl::GIF:
-              case VisualUrl::WEBP:
-              {
-                visualPtr = AnimatedImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, propertyMap);
-                break;
-              }
               case VisualUrl::JSON:
               {
                 visualPtr = AnimatedVectorImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), imageUrl, propertyMap);
                 break;
               }
+              case VisualUrl::GIF:
+              case VisualUrl::WEBP:
+              {
+                if(visualType == Toolkit::DevelVisual::ANIMATED_IMAGE || !(creationOptions & Toolkit::VisualFactory::CreationOptions::IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY))
+                {
+                  visualPtr = AnimatedImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, propertyMap);
+                  break;
+                }
+                DALI_FALLTHROUGH;
+              }
               case VisualUrl::REGULAR_IMAGE:
               {
                 visualPtr = ImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, propertyMap);
@@ -267,31 +282,6 @@ Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyM
       break;
     }
 
-    case Toolkit::Visual::ANIMATED_IMAGE:
-    {
-      Property::Value* imageURLValue = propertyMap.Find(Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME);
-      std::string      imageUrl;
-      if(imageURLValue)
-      {
-        if(imageURLValue->Get(imageUrl))
-        {
-          if(!imageUrl.empty())
-          {
-            visualPtr = AnimatedImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), imageUrl, propertyMap);
-          }
-        }
-        else
-        {
-          Property::Array* array = imageURLValue->GetArray();
-          if(array && array->Count() > 0)
-          {
-            visualPtr = AnimatedImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), *array, propertyMap);
-          }
-        }
-      }
-      break;
-    }
-
     case Toolkit::DevelVisual::ANIMATED_GRADIENT:
     {
       visualPtr = AnimatedGradientVisual::New(GetFactoryCache(), propertyMap);
@@ -342,6 +332,11 @@ Toolkit::Visual::Base VisualFactory::CreateVisual(const Property::Map& propertyM
 
 Toolkit::Visual::Base VisualFactory::CreateVisual(const std::string& url, ImageDimensions size)
 {
+  return CreateVisual(url, size, mDefaultCreationOptions);
+}
+
+Toolkit::Visual::Base VisualFactory::CreateVisual(const std::string& url, ImageDimensions size, Toolkit::VisualFactory::CreationOptions creationOptions)
+{
   Visual::BasePtr visualPtr;
 
   if(!url.empty())
@@ -361,17 +356,21 @@ Toolkit::Visual::Base VisualFactory::CreateVisual(const std::string& url, ImageD
         visualPtr = SvgVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, size);
         break;
       }
-      case VisualUrl::GIF:
-      case VisualUrl::WEBP:
-      {
-        visualPtr = AnimatedImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, size);
-        break;
-      }
       case VisualUrl::JSON:
       {
         visualPtr = AnimatedVectorImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, size);
         break;
       }
+      case VisualUrl::GIF:
+      case VisualUrl::WEBP:
+      {
+        if(!(creationOptions & Toolkit::VisualFactory::CreationOptions::IMAGE_VISUAL_LOAD_STATIC_IMAGES_ONLY))
+        {
+          visualPtr = AnimatedImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, size);
+          break;
+        }
+        DALI_FALLTHROUGH;
+      }
       case VisualUrl::REGULAR_IMAGE:
       {
         visualPtr = ImageVisual::New(GetFactoryCache(), GetImageVisualShaderFactory(), visualUrl, size);
@@ -403,6 +402,16 @@ bool VisualFactory::GetPreMultiplyOnLoad() const
   return mPreMultiplyOnLoad;
 }
 
+void VisualFactory::SetDefaultCreationOptions(Toolkit::VisualFactory::CreationOptions creationOptions)
+{
+  mDefaultCreationOptions = creationOptions;
+}
+
+Toolkit::VisualFactory::CreationOptions VisualFactory::GetDefaultCreationOptions() const
+{
+  return mDefaultCreationOptions;
+}
+
 void VisualFactory::DiscardVisual(Toolkit::Visual::Base visual)
 {
   mDiscardedVisuals.emplace_back(visual);
@@ -410,6 +419,19 @@ void VisualFactory::DiscardVisual(Toolkit::Visual::Base visual)
   RegisterDiscardCallback();
 }
 
+bool VisualFactory::AddPrecompileShader(const Property::Map& map)
+{
+  PrecompileShaderOption shaderOption(map);
+  auto                   type = shaderOption.GetShaderType();
+  if(type == PrecompileShaderOption::ShaderType::UNKNOWN)
+  {
+    DALI_LOG_ERROR("AddPrecompileShader is failed. we can't find shader type");
+    return false;
+  }
+
+  return AddPrecompileShader(shaderOption);
+}
+
 void VisualFactory::UsePreCompiledShader()
 {
   if(mPrecompiledShaderRequested)
@@ -433,9 +455,19 @@ void VisualFactory::UsePreCompiledShader()
 
   // Get color shader
   RawShaderData colorShaderData;
-  GetPreCompiledShader(colorShaderData);
+  GetColorVisualShaderFactory().GetPreCompiledShader(colorShaderData);
   rawShaderList.push_back(colorShaderData);
 
+  RawShaderData npatchShaderData;
+  GetNpatchShaderFactory().GetPreCompiledShader(npatchShaderData);
+  rawShaderList.push_back(npatchShaderData);
+
+  // Get 3D shader
+  // Get Custom shader
+  RawShaderData customShaderData;
+  GetCustomShaderFactory().GetPreCompiledShader(customShaderData);
+  rawShaderList.push_back(customShaderData);
+
   // Save all shader
   ShaderPreCompiler::Get().SavePreCompileShaderList(rawShaderList);
 }
@@ -445,6 +477,11 @@ Internal::TextureManager& VisualFactory::GetTextureManager()
   return GetFactoryCache().GetTextureManager();
 }
 
+Internal::SvgLoader& VisualFactory::GetSvgLoader()
+{
+  return GetFactoryCache().GetSvgLoader();
+}
+
 void VisualFactory::SetBrokenImageUrl(Toolkit::StyleManager& styleManager)
 {
   const std::string        imageDirPath   = AssetManager::GetDaliImagePath();
@@ -465,29 +502,6 @@ void VisualFactory::SetBrokenImageUrl(Toolkit::StyleManager& styleManager)
   mFactoryCache->SetBrokenImageUrl(brokenImageUrl, customBrokenImageUrlList);
 }
 
-void VisualFactory::GetPreCompiledShader(RawShaderData& shaders)
-{
-  std::vector<std::string_view> vertexPrefix;
-  std::vector<std::string_view> fragmentPrefix;
-  std::vector<std::string_view> shaderName;
-  int                           shaderCount = 0;
-  shaders.shaderCount                       = 0;
-  for(uint32_t i = 0u; i < SHADER_TYPE_COUNT; ++i)
-  {
-    vertexPrefix.push_back(VertexPredefines[i]);
-    fragmentPrefix.push_back(FragmentPredefines[i]);
-    shaderName.push_back(Scripting::GetLinearEnumerationName<VisualFactoryCache::ShaderType>(ShaderTypePredefines[i], VISUAL_SHADER_TYPE_TABLE, VISUAL_SHADER_TYPE_TABLE_COUNT));
-    shaderCount++;
-  }
-
-  shaders.vertexPrefix   = vertexPrefix;
-  shaders.fragmentPrefix = fragmentPrefix;
-  shaders.shaderName     = shaderName;
-  shaders.vertexShader   = SHADER_COLOR_VISUAL_SHADER_VERT;
-  shaders.fragmentShader = SHADER_COLOR_VISUAL_SHADER_FRAG;
-  shaders.shaderCount    = shaderCount;
-}
-
 Internal::VisualFactoryCache& VisualFactory::GetFactoryCache()
 {
   if(!mFactoryCache)
@@ -523,6 +537,79 @@ TextVisualShaderFactory& VisualFactory::GetTextVisualShaderFactory()
   return *mTextVisualShaderFactory;
 }
 
+ColorVisualShaderFactory& VisualFactory::GetColorVisualShaderFactory()
+{
+  if(!mColorVisualShaderFactory)
+  {
+    mColorVisualShaderFactory = std::unique_ptr<ColorVisualShaderFactory>(new ColorVisualShaderFactory());
+  }
+  return *mColorVisualShaderFactory;
+}
+
+NpatchShaderFactory& VisualFactory::GetNpatchShaderFactory()
+{
+  if(!mNpatchShaderFactory)
+  {
+    mNpatchShaderFactory = std::unique_ptr<NpatchShaderFactory>(new NpatchShaderFactory());
+  }
+  return *mNpatchShaderFactory;
+}
+
+CustomShaderFactory& VisualFactory::GetCustomShaderFactory()
+{
+  if(!mCustomShaderFactory)
+  {
+    mCustomShaderFactory = std::unique_ptr<CustomShaderFactory>(new CustomShaderFactory());
+  }
+  return *mCustomShaderFactory;
+}
+
+bool VisualFactory::AddPrecompileShader(PrecompileShaderOption& option)
+{
+  auto type = option.GetShaderType();
+  bool ret  = false;
+  switch(type)
+  {
+    case PrecompileShaderOption::ShaderType::COLOR:
+    {
+      ret = GetColorVisualShaderFactory().AddPrecompiledShader(option);
+      break;
+    }
+    case PrecompileShaderOption::ShaderType::IMAGE:
+    {
+      ret = GetImageVisualShaderFactory().AddPrecompiledShader(option);
+      break;
+    }
+    case PrecompileShaderOption::ShaderType::TEXT:
+    {
+      ret = GetTextVisualShaderFactory().AddPrecompiledShader(option);
+      break;
+    }
+    case PrecompileShaderOption::ShaderType::NPATCH:
+    {
+      ret = GetNpatchShaderFactory().AddPrecompiledShader(option);
+      break;
+    }
+    case PrecompileShaderOption::ShaderType::MODEL_3D:
+    {
+      // TODO
+      break;
+    }
+    case PrecompileShaderOption::ShaderType::CUSTOM:
+    {
+      ret = GetCustomShaderFactory().AddPrecompiledShader(option);
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("AddPrecompileShader is failed. we can't find shader factory type:%d", type);
+      break;
+    }
+  }
+
+  return ret;
+}
+
 void VisualFactory::OnDiscardCallback()
 {
   mIdleCallback = nullptr;
@@ -531,6 +618,19 @@ void VisualFactory::OnDiscardCallback()
   mDiscardedVisuals.clear();
 }
 
+void VisualFactory::OnApplicationTerminated()
+{
+  if(DALI_UNLIKELY(mIdleCallback))
+  {
+    OnDiscardCallback();
+  }
+
+  if(mFactoryCache)
+  {
+    mFactoryCache->FinalizeVectorAnimationManager();
+  }
+}
+
 void VisualFactory::RegisterDiscardCallback()
 {
   if(!mIdleCallback && Adaptor::IsAvailable())
index 76bacf9..9aec117 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VISUAL_FACTORY_IMPL_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 // EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/base-object.h>
-#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/visual-factory/visual-base.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
+#include <dali-toolkit/devel-api/visual-factory/precompile-shader-option.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/public-api/styling/style-manager.h>
 
@@ -37,11 +38,14 @@ namespace Internal
 class VisualFactoryCache;
 class ImageVisualShaderFactory;
 class TextVisualShaderFactory;
+class ColorVisualShaderFactory;
+class NpatchShaderFactory;
+class CustomShaderFactory;
 
 /**
  * @copydoc Toolkit::VisualFactory
  */
-class VisualFactory : public BaseObject
+class VisualFactory : public BaseObject, public ConnectionTracker
 {
 public:
   /**
@@ -72,11 +76,21 @@ public:
   Toolkit::Visual::Base CreateVisual(const Property::Map& propertyMap);
 
   /**
+   * @copydoc Toolkit::VisualFactory::CreateVisual( const Property::Map&, Toolkit::VisualFactory::CreationOptions )
+   */
+  Toolkit::Visual::Base CreateVisual(const Property::Map& propertyMap, Toolkit::VisualFactory::CreationOptions creationOptions);
+
+  /**
    * @copydoc Toolkit::VisualFactory::CreateVisual( const std::string&, ImageDimensions )
    */
   Toolkit::Visual::Base CreateVisual(const std::string& image, ImageDimensions size);
 
   /**
+   * @copydoc Toolkit::VisualFactory::CreateVisual( const std::string&, ImageDimensions, Toolkit::VisualFactory::CreationOptions )
+   */
+  Toolkit::Visual::Base CreateVisual(const std::string& image, ImageDimensions size, Toolkit::VisualFactory::CreationOptions creationOptions);
+
+  /**
    * @copydoc Toolkit::VisualFactory::SetPreMultiplyOnLoad()
    */
   void SetPreMultiplyOnLoad(bool preMultiply);
@@ -87,11 +101,26 @@ public:
   bool GetPreMultiplyOnLoad() const;
 
   /**
+   * @copydoc Toolkit::VisualFactory::SetDefaultCreationOptions( Toolkit::VisualFactory::CreationOptions )
+   */
+  void SetDefaultCreationOptions(Toolkit::VisualFactory::CreationOptions creationOptions);
+
+  /**
+   * @copydoc Toolkit::VisualFactory::GetDefaultCreationOptions()
+   */
+  Toolkit::VisualFactory::CreationOptions GetDefaultCreationOptions() const;
+
+  /**
    * @copydoc Toolkit::VisualFactory::DiscardVisual()
    */
   void DiscardVisual(Toolkit::Visual::Base visual);
 
   /**
+   * @copydoc Toolkit::VisualFactory::AddPrecompileShader()
+   */
+  bool AddPrecompileShader(const Property::Map& map);
+
+  /**
    * @copydoc Toolkit::VisualFactory::UsePreCompiledShader()
    */
   void UsePreCompiledShader();
@@ -101,6 +130,11 @@ public:
    */
   Internal::TextureManager& GetTextureManager();
 
+  /**
+   * @return the reference to svg loader
+   */
+  Internal::SvgLoader& GetSvgLoader();
+
 protected:
   /**
    * A reference counted object may only be deleted by calling Unreference()
@@ -115,12 +149,6 @@ private:
   void SetBrokenImageUrl(Toolkit::StyleManager& styleManager);
 
   /**
-   * @brief Get the default shader source.
-   * @param[in] shaders shaderList for precompile
-   */
-  void GetPreCompiledShader(RawShaderData& shaders);
-
-  /**
    * Get the factory cache, creating it if necessary.
    */
   Internal::VisualFactoryCache& GetFactoryCache();
@@ -136,6 +164,29 @@ private:
   TextVisualShaderFactory& GetTextVisualShaderFactory();
 
   /**
+   * Get the color visual shader factory, creating it if necessary.
+   */
+  ColorVisualShaderFactory& GetColorVisualShaderFactory();
+
+  /**
+   * Get the npatch shader factory, creating it if necessary.
+   */
+  NpatchShaderFactory& GetNpatchShaderFactory();
+
+  /**
+   * Get the custom shader factory, creating it if necessary.
+   */
+  CustomShaderFactory& GetCustomShaderFactory();
+
+  /**
+   * @brief Add precompiled shader
+   * @param[in] shaderOption The option of precompiled shader
+   *
+   * @return True if add success, false otherwise.
+   */
+  bool AddPrecompileShader(PrecompileShaderOption& shaderOption);
+
+  /**
    * @brief Callbacks called for clear discarded visuals.
    */
   void OnDiscardCallback();
@@ -145,6 +196,11 @@ private:
    */
   void RegisterDiscardCallback();
 
+  /**
+   * @brief Callbacks called when application is terminated.
+   */
+  void OnApplicationTerminated();
+
   VisualFactory(const VisualFactory&) = delete;
 
   VisualFactory& operator=(const VisualFactory& rhs) = delete;
@@ -153,13 +209,19 @@ private:
   std::unique_ptr<VisualFactoryCache>       mFactoryCache;
   std::unique_ptr<ImageVisualShaderFactory> mImageVisualShaderFactory;
   std::unique_ptr<TextVisualShaderFactory>  mTextVisualShaderFactory;
+  std::unique_ptr<ColorVisualShaderFactory> mColorVisualShaderFactory;
+  std::unique_ptr<NpatchShaderFactory>      mNpatchShaderFactory;
+  std::unique_ptr<CustomShaderFactory>      mCustomShaderFactory;
   SlotDelegate<VisualFactory>               mSlotDelegate;
   CallbackBase*                             mIdleCallback;
   using DiscardedVisualContainer = std::vector<Toolkit::Visual::Base>;
   DiscardedVisualContainer mDiscardedVisuals{};
-  bool                                      mDebugEnabled : 1;
-  bool                                      mPreMultiplyOnLoad : 1; ///< Local store for this flag
-  bool                                      mPrecompiledShaderRequested : 1;
+
+  Toolkit::VisualFactory::CreationOptions mDefaultCreationOptions : 2;
+
+  bool mDebugEnabled : 1;
+  bool mPreMultiplyOnLoad : 1; ///< Local store for this flag
+  bool mPrecompiledShaderRequested : 1;
 };
 
 /**
diff --git a/dali-toolkit/internal/visuals/visual-shader-factory-interface.h b/dali-toolkit/internal/visuals/visual-shader-factory-interface.h
new file mode 100644 (file)
index 0000000..704be91
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef DALI_TOOLKIT_VISUAL_SHADER_FACTORY_INTERFACE_H
+#define DALI_TOOLKIT_VISUAL_SHADER_FACTORY_INTERFACE_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/shader-precompiler.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
+#include <dali-toolkit/devel-api/visual-factory/precompile-shader-option.h>
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+
+using HashType = uint64_t;
+using ShaderFlagList = std::vector<PrecompileShaderOption::Flag>;
+
+namespace Internal
+{
+/**
+ * @brief The VisualShaderFactoryInterface class provides a factory interface for creating visual shader
+ */
+class VisualShaderFactoryInterface
+{
+public:
+
+  /**
+   * @brief Structure to request shader info from visual shader factory.
+   */
+  struct RequestShaderInfo
+  {
+    VisualFactoryCache::ShaderType type;
+    std::string name;
+    std::string vertexPrefix;
+    std::string fragmentPrefix;
+  };
+
+  VisualShaderFactoryInterface() = default;
+  virtual ~VisualShaderFactoryInterface() = default;
+
+  /**
+   * @brief Add precompiled shader for precompile
+   * @param[in] option shaderList for precompile
+   */
+  virtual bool AddPrecompiledShader(PrecompileShaderOption& option) = 0;
+
+  /**
+   * @brief Get precompiled shader for precompile
+   * @param[out] shaders shaderList for precompile
+   */
+  virtual void GetPreCompiledShader(RawShaderData& shaders) = 0;
+
+protected:
+  std::vector<RequestShaderInfo> mRequestedPrecompileShader;
+};
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_VISUAL_SHADER_FACTORY_INTERFACE_H
index ae39540..ad0c406 100644 (file)
@@ -53,6 +53,12 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_SHADER_TYPE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_ROUNDED_BORDERLINE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_ROUNDED_CORNER)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_BORDERLINE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, BORDER_SHADER)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, BORDER_SHADER_ANTI_ALIASING)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, GRADIENT_SHADER_LINEAR_BOUNDING_BOX)
@@ -172,6 +178,7 @@ const char* const CORNER_RADIUS_POLICY("cornerRadiusPolicy");
 
 // Color visual
 const char* const BLUR_RADIUS_NAME("blurRadius");
+const char* const CUTOUT_POLICY_NAME("cutoutPolicy");
 
 // Image visual
 const char* const IMAGE_URL_NAME("url");
@@ -213,6 +220,8 @@ const char* const FAST_TRACK_UPLOADING_NAME("fastTrackUploading");
 const char* const ENABLE_BROKEN_IMAGE("enableBrokenImage");
 const char* const ENABLE_FRAME_CACHE("enableFrameCache");
 const char* const NOTIFY_AFTER_RASTERIZATION("notifyAfterRasterization");
+const char* const SYNCHRONOUS_SIZING("synchronousSizing");
+const char* const FRAME_SPEED_FACTOR("frameSpeedFactor");
 
 // Text visual
 const char* const TEXT_PROPERTY("text");
index 9835299..6aa50b8 100644 (file)
@@ -74,6 +74,7 @@ extern const char* const CORNER_RADIUS_POLICY;
 
 // Color visual
 extern const char* const BLUR_RADIUS_NAME;
+extern const char* const CUTOUT_POLICY_NAME;
 
 // Image visual
 extern const char* const IMAGE_URL_NAME;
@@ -115,6 +116,8 @@ extern const char* const FAST_TRACK_UPLOADING_NAME;
 extern const char* const ENABLE_BROKEN_IMAGE;
 extern const char* const ENABLE_FRAME_CACHE;
 extern const char* const NOTIFY_AFTER_RASTERIZATION;
+extern const char* const SYNCHRONOUS_SIZING;
+extern const char* const FRAME_SPEED_FACTOR;
 
 // Text visual
 extern const char* const TEXT_PROPERTY;
index 7611dd6..ae76d87 100644 (file)
@@ -68,7 +68,7 @@ WireframeVisualPtr WireframeVisual::New(VisualFactoryCache& factoryCache, Visual
 }
 
 WireframeVisual::WireframeVisual(VisualFactoryCache& factoryCache, Visual::BasePtr actualVisual)
-: Visual::Base(factoryCache, Visual::FittingMode::FILL, actualVisual ? actualVisual->GetType() : Toolkit::Visual::WIREFRAME),
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, actualVisual ? actualVisual->GetType() : Toolkit::Visual::WIREFRAME),
   mActualVisual(actualVisual)
 {
 }
index 40f34a0..068fb03 100644 (file)
 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/visuals/color/color-visual.h>
+#include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali-toolkit/public-api/align-enumerations.h>
 #include <dali-toolkit/public-api/controls/control.h>
@@ -165,13 +167,45 @@ void Control::ClearBackground()
   RelayoutRequest();
 }
 
+void Control::SetRenderEffect(Toolkit::RenderEffect effect)
+{
+  if(mImpl->mRenderEffect != effect)
+  {
+    ClearRenderEffect();
+    mImpl->mRenderEffect = effect;
+
+    if(effect)
+    {
+      Toolkit::Internal::RenderEffectImpl* object = dynamic_cast<Toolkit::Internal::RenderEffectImpl*>(mImpl->mRenderEffect.GetObjectPtr());
+      DALI_ASSERT_ALWAYS(object && "Not a valid RenderEffect set.");
+
+      Dali::Toolkit::Control ownerControl(GetOwner());
+      object->SetOwnerControl(ownerControl);
+    }
+  }
+}
+
+void Control::ClearRenderEffect()
+{
+  if(mImpl->mRenderEffect)
+  {
+    Toolkit::Internal::RenderEffectImpl* object = dynamic_cast<Toolkit::Internal::RenderEffectImpl*>(mImpl->mRenderEffect.GetObjectPtr());
+
+    if(object)
+    {
+      object->ClearOwnerControl();
+    }
+    mImpl->mRenderEffect.Reset();
+  }
+}
+
 void Control::SetResourceReady()
 {
   Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
   controlDataImpl.ResourceReady();
 }
 
-Toolkit::DevelControl::ControlAccessible* Control::GetAccessibleObject()
+std::shared_ptr<Toolkit::DevelControl::ControlAccessible> Control::GetAccessibleObject()
 {
   return mImpl->GetAccessibleObject();
 }
@@ -405,6 +439,9 @@ Control::Control(ControlBehaviour behaviourFlags)
 
 Control::~Control()
 {
+  // Deactivate render effect before destroying the control impl
+  ClearRenderEffect();
+
   delete mImpl;
 }
 
@@ -497,7 +534,7 @@ void Control::EmitKeyInputFocusSignal(bool focusGained)
       auto parent = accessible->GetParent();
       if(parent && !accessible->GetStates()[Dali::Accessibility::State::MANAGES_DESCENDANTS])
       {
-        parent->EmitActiveDescendantChanged(accessible);
+        parent->EmitActiveDescendantChanged(accessible.get());
       }
     }
   }
@@ -580,7 +617,7 @@ void Control::OnPropertySet(Property::Index index, const Property::Value& proper
     }
     case Actor::Property::VISIBLE:
     {
-      auto* accessible = GetAccessibleObject();
+      auto accessible = GetAccessibleObject();
       if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
       {
         accessible->EmitVisible(Self().GetProperty<bool>(Actor::Property::VISIBLE));
@@ -601,12 +638,16 @@ void Control::OnPropertySet(Property::Index index, const Property::Value& proper
 
 void Control::OnSizeSet(const Vector3& targetSize)
 {
+  Vector2               size(targetSize);
   Toolkit::Visual::Base visual = mImpl->GetVisual(Toolkit::Control::Property::BACKGROUND);
   if(visual)
   {
-    Vector2 size(targetSize);
     visual.SetTransformAndSize(Property::Map(), size); // Send an empty map as we do not want to modify the visual's set transform
   }
+
+  // Apply FittingMode here
+  mImpl->mSize = size;
+  mImpl->RegisterProcessorOnce();
 }
 
 void Control::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
@@ -654,11 +695,8 @@ void Control::OnRelayout(const Vector2& size, RelayoutContainer& container)
     container.Add(child, newChildSize);
   }
 
-  Toolkit::Visual::Base visual = mImpl->GetVisual(Toolkit::Control::Property::BACKGROUND);
-  if(visual)
-  {
-    visual.SetTransformAndSize(Property::Map(), size); // Send an empty map as we do not want to modify the visual's set transform
-  }
+  // Apply FittingMode here
+  mImpl->ApplyFittingMode(size);
 }
 
 void Control::OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
@@ -774,16 +812,10 @@ void Control::MakeVisualTransition(Dali::Property::Map& sourcePropertyMap, Dali:
   float   destinationBorderlineOffset = findValueFloat(destinationMap, Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, defaultBorderlineOffset);
 
   // If the value of the source Control and that of destination Control is different, the property should be transitioned.
-  if(Vector3(sourceMixColor) != Vector3(destinationMixColor))
-  {
-    sourcePropertyMap.Add(Dali::Toolkit::Visual::Property::MIX_COLOR, Vector3(sourceMixColor));
-    destinationPropertyMap.Add(Dali::Toolkit::Visual::Property::MIX_COLOR, Vector3(destinationMixColor));
-  }
-
-  if(std::abs(sourceMixColor.a - destinationMixColor.a) > Math::MACHINE_EPSILON_1)
+  if(sourceMixColor != destinationMixColor)
   {
-    sourcePropertyMap.Add(Dali::Toolkit::Visual::Property::OPACITY, sourceMixColor.a);
-    destinationPropertyMap.Add(Dali::Toolkit::Visual::Property::OPACITY, destinationMixColor.a);
+    sourcePropertyMap.Add(Dali::Toolkit::Visual::Property::MIX_COLOR, sourceMixColor);
+    destinationPropertyMap.Add(Dali::Toolkit::Visual::Property::MIX_COLOR, destinationMixColor);
   }
 
   if(sourceCornerRadius != destinationCornerRadius)
index 9e2915e..76991aa 100644 (file)
@@ -115,6 +115,16 @@ public:
   void ClearBackground();
 
   /**
+   * @copydoc Dali::Toolkit::Control::SetRenderEffect
+   */
+  void SetRenderEffect(Toolkit::RenderEffect effect);
+
+  /**
+   * @copydoc Dali::Toolkit::Control::ClearRenderEffect
+   */
+  void ClearRenderEffect();
+
+  /**
    * @brief Called when resources of control are ready. this api does not request relayout.
    */
   void SetResourceReady();
@@ -130,7 +140,7 @@ public:
    *
    * @see CreateAccessibleObject()
    */
-  Toolkit::DevelControl::ControlAccessible* GetAccessibleObject();
+  std::shared_ptr<Toolkit::DevelControl::ControlAccessible> GetAccessibleObject();
 
   // Gesture Detection
 
index 2e918f0..c163378 100644 (file)
@@ -115,6 +115,16 @@ void Control::ClearBackground()
   Internal::GetImplementation(*this).ClearBackground();
 }
 
+void Control::SetRenderEffect(Toolkit::RenderEffect effect)
+{
+  Internal::GetImplementation(*this).SetRenderEffect(effect);
+}
+
+void Control::ClearRenderEffect()
+{
+  Internal::GetImplementation(*this).ClearRenderEffect();
+}
+
 bool Control::IsResourceReady() const
 {
   return Internal::GetImplementation(*this).IsResourceReady();
index e4e90a2..7da13e8 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_CONTROL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ namespace Dali
 {
 namespace Toolkit
 {
+class RenderEffect;
 //Forward declarations.
 
 namespace Internal
@@ -147,16 +148,16 @@ public:
      */
     enum Direction
     {
-      LEFT,      ///< Move keyboard focus towards the left direction @SINCE_1_0.0
-      RIGHT,     ///< Move keyboard focus towards the right direction @SINCE_1_0.0
-      UP,        ///< Move keyboard focus towards the up direction @SINCE_1_0.0
-      DOWN,      ///< Move keyboard focus towards the down direction @SINCE_1_0.0
-      PAGE_UP,   ///< Move keyboard focus towards the previous page direction @SINCE_1_2.14
-      PAGE_DOWN, ///< Move keyboard focus towards the next page direction @SINCE_1_2.14
-      FORWARD,   ///< Move keyboard focus towards the forward direction @SINCE_2_1.10
-      BACKWARD,  ///< Move keyboard focus towards the backward direction @SINCE_2_1.10
-      CLOCKWISE,  ///< Move keyboard focus towards the clockwise direction @SINCE_2_1.14
-      COUNTER_CLOCKWISE,  ///< Move keyboard focus towards the counter clockwise direction @SINCE_2_1.14
+      LEFT,              ///< Move keyboard focus towards the left direction @SINCE_1_0.0
+      RIGHT,             ///< Move keyboard focus towards the right direction @SINCE_1_0.0
+      UP,                ///< Move keyboard focus towards the up direction @SINCE_1_0.0
+      DOWN,              ///< Move keyboard focus towards the down direction @SINCE_1_0.0
+      PAGE_UP,           ///< Move keyboard focus towards the previous page direction @SINCE_1_2.14
+      PAGE_DOWN,         ///< Move keyboard focus towards the next page direction @SINCE_1_2.14
+      FORWARD,           ///< Move keyboard focus towards the forward direction @SINCE_2_1.10
+      BACKWARD,          ///< Move keyboard focus towards the backward direction @SINCE_2_1.10
+      CLOCKWISE,         ///< Move keyboard focus towards the clockwise direction @SINCE_2_1.14
+      COUNTER_CLOCKWISE, ///< Move keyboard focus towards the counter clockwise direction @SINCE_2_1.14
     };
   };
 
@@ -172,7 +173,6 @@ public:
   typedef Signal<void(Control)> ResourceReadySignalType;
 
 public: // Creation & Destruction
-
   /**
    * @brief Additional control behaviour flags for the control constructor.
    * @note TODO : Currunt code is hard-coded. We Should sync type values as
@@ -382,6 +382,22 @@ public:
    */
   void ClearBackground();
 
+  /**
+   * @brief Sets RenderEffect to this control.
+   *
+   * @SINCE_2_3.25
+   * @param[in] effect RenderEffect to add.
+   *
+   * @note Every effect inherits RenderEffect.
+   */
+  void SetRenderEffect(Toolkit::RenderEffect effect);
+
+  /**
+   * @brief Clears RenderEffect of this control, if exists.
+   * @SINCE_2_3.25
+   */
+  void ClearRenderEffect();
+
   // Resources
 
   /**
diff --git a/dali-toolkit/public-api/controls/render-effects/background-blur-effect.cpp b/dali-toolkit/public-api/controls/render-effects/background-blur-effect.cpp
new file mode 100644 (file)
index 0000000..f02f276
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/blur-effect-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+BackgroundBlurEffect::BackgroundBlurEffect() = default;
+
+BackgroundBlurEffect::BackgroundBlurEffect(const BackgroundBlurEffect& handle)
+: RenderEffect(handle)
+{
+}
+
+BackgroundBlurEffect::BackgroundBlurEffect(Internal::BlurEffectImpl* blurEffectImpl)
+: RenderEffect(blurEffectImpl)
+{
+}
+
+BackgroundBlurEffect::~BackgroundBlurEffect() = default;
+
+BackgroundBlurEffect BackgroundBlurEffect::New()
+{
+  Internal::BlurEffectImplPtr internal = Internal::BlurEffectImpl::New(true);
+  return BackgroundBlurEffect(internal.Get());
+}
+
+BackgroundBlurEffect BackgroundBlurEffect::New(float downscaleFactor, uint32_t blurRadius)
+{
+  Internal::BlurEffectImplPtr internal = Internal::BlurEffectImpl::New(downscaleFactor, blurRadius, true);
+  return BackgroundBlurEffect(internal.Get());
+}
+
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/public-api/controls/render-effects/background-blur-effect.h b/dali-toolkit/public-api/controls/render-effects/background-blur-effect.h
new file mode 100644 (file)
index 0000000..414d4fa
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef DALI_TOOLKIT_BACKGROUND_BLUR_EFFECT_H
+#define DALI_TOOLKIT_BACKGROUND_BLUR_EFFECT_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal DALI_INTERNAL
+{
+class BlurEffectImpl;
+} // namespace DALI_INTERNAL
+
+/**
+ * @brief BackgroundBlurEffect is a visual effect that blurs owner control's background.
+ * This class is a concrete class from RenderEffect interface.
+ * Add this effect to a control, clear manually to deactivate.
+ *
+ * Toolkit::Control control = Toolkit::Control::New();
+ * parent.Add(control);
+ * control.SetRenderEffect(BackgroundBlurEffect::New()); // Activate
+ * ...
+ * control.ClearRenderEffect(); // Deactivate
+ *
+ * Note that tree hierarchy matters for BackgroundBlurEffect. You should determine "what is the background".
+ * Add() should preceed SetRenderEffect(), and the effect cannot have multiple owner controls.
+ *
+ * @SINCE_2_3.28
+ */
+class DALI_TOOLKIT_API BackgroundBlurEffect : public RenderEffect
+{
+public:
+  /**
+   * @brief Creates an initialized BackgroundBlurEffect, using default settings. The default settings are:
+   *
+   * downscaleFactor = 0.4f
+   * pixelRadius = 5u
+   *
+   * @SINCE_2_3.28
+   * @return A handle to a newly allocated Dali resource
+   */
+  static BackgroundBlurEffect New();
+
+  /**
+   * @brief Creates an initialized BackgroundBlurEffect.
+   * @param[in] downscaleFactor This value should reside in the range [0.0, 1.0].
+   * @param[in] blurRadius The radius of Gaussian kernel.
+   * @SINCE_2_3.28
+   * @return A handle to a newly allocated Dali resource
+   */
+  static BackgroundBlurEffect New(float downscaleFactor, uint32_t blurRadius);
+
+  /**
+   * @brief Creates an uninitialized blur effect.
+   * @SINCE_2_3.28
+   */
+  BackgroundBlurEffect();
+
+  /**
+   * @brief Copy constructor.
+   * @SINCE_2_3.28
+   */
+  BackgroundBlurEffect(const BackgroundBlurEffect& handle);
+
+  /**
+  * @brief Destructor
+  * @SINCE_2_3.28
+  */
+  ~BackgroundBlurEffect();
+
+public: // Not intended for use by Application developers
+  ///@cond internal
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   * @SINCE_2_3.28
+   * @param[in]  blurEffectImpl The UI Control implementation.
+   */
+  explicit DALI_INTERNAL BackgroundBlurEffect(Internal::BlurEffectImpl* blurEffectImpl);
+  ///@endcond
+};
+
+} // namespace Toolkit
+} // namespace Dali
+
+#endif //DALI_TOOLKIT_BACKGROUND_BLUR_EFFECT_H
diff --git a/dali-toolkit/public-api/controls/render-effects/render-effect.cpp b/dali-toolkit/public-api/controls/render-effects/render-effect.cpp
new file mode 100644 (file)
index 0000000..9964e13
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+RenderEffect::RenderEffect(const RenderEffect& handle)
+: BaseHandle(handle)
+{
+}
+
+RenderEffect::RenderEffect(Internal::RenderEffectImpl* renderEffectImpl)
+: BaseHandle(renderEffectImpl)
+{
+}
+
+RenderEffect::~RenderEffect() = default;
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/public-api/controls/render-effects/render-effect.h b/dali-toolkit/public-api/controls/render-effects/render-effect.h
new file mode 100644 (file)
index 0000000..085fa88
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef DALI_TOOLKIT_RENDER_EFFECT_H
+#define DALI_TOOLKIT_RENDER_EFFECT_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/render-tasks/render-task.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+class Control;
+namespace Internal DALI_INTERNAL
+{
+class Control;
+class RenderEffectImpl;
+} // namespace Internal DALI_INTERNAL
+
+/**
+ * @brief
+ * RenderEffect is an interface for visual effects.
+ *
+ * Each effect has a single owner Control.
+ *
+ * Used internal at:
+ * Toolkit::Control::SetRenderEffect(Toolkit::RenderEffect effect);
+ * Toolkit::Control::ClearRenderEffect();
+ * @note RenderEffect is interface class without constructor. Create resource by subclass.
+ * @SINCE_2_3.28
+ */
+class DALI_TOOLKIT_API RenderEffect : public BaseHandle
+{
+public:
+  /**
+   * @brief Creates an uninitialized effect.
+   * @SINCE_2_3.28
+   */
+  RenderEffect() = default;
+
+  /**
+   * @brief Copy constructor. Creates another handle that points to the same real object.
+   * @SINCE_2_3.28
+   */
+  RenderEffect(const RenderEffect& handle);
+
+  /**
+   * @brief Destructor.
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   * @SINCE_2_3.28
+   */
+  ~RenderEffect();
+
+public: // Not intended for Application developers
+  ///@cond internal
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   * @SINCE_2_3.28
+   * @param[in]  renderEffectImpl The UI Control implementation.
+   */
+  explicit DALI_INTERNAL RenderEffect(Internal::RenderEffectImpl* renderEffectImpl);
+  ///@endcond
+};
+
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_RENDER_EFFECT_H
index 978a3a5..ab3f861 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 3;
-const unsigned int TOOLKIT_MICRO_VERSION = 24;
+const unsigned int TOOLKIT_MICRO_VERSION = 42;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 833bfbf..0a69c83 100644 (file)
@@ -13,6 +13,8 @@ SET( public_api_src_files
   ${public_api_src_dir}/controls/image-view/image-view.cpp
   ${public_api_src_dir}/controls/model3d-view/model3d-view.cpp
   ${public_api_src_dir}/controls/progress-bar/progress-bar.cpp
+  ${public_api_src_dir}/controls/render-effects/render-effect.cpp
+  ${public_api_src_dir}/controls/render-effects/background-blur-effect.cpp
   ${public_api_src_dir}/controls/scrollable/item-view/default-item-layout.cpp
   ${public_api_src_dir}/controls/scrollable/item-view/item-layout.cpp
   ${public_api_src_dir}/controls/scrollable/item-view/item-view.cpp
@@ -27,7 +29,6 @@ SET( public_api_src_files
   ${public_api_src_dir}/controls/text-controls/text-field.cpp
   ${public_api_src_dir}/controls/video-view/video-view.cpp
   ${public_api_src_dir}/controls/camera-view/camera-view.cpp
-  ${public_api_src_dir}/controls/gl-view/gl-view.cpp
   ${public_api_src_dir}/image-loader/image.cpp
   ${public_api_src_dir}/image-loader/image-url.cpp
   ${public_api_src_dir}/image-loader/async-image-loader.cpp
@@ -51,6 +52,10 @@ SET( public_api_src_files
   ${public_api_src_dir}/enums.cpp
 )
 
+SET(public_api_egl_src_files
+  ${public_api_src_dir}/controls/gl-view/gl-view.cpp
+)
+
 # Add public header files here
 SET( public_api_header_files
   ${public_api_src_dir}/dali-toolkit-version.h
@@ -104,6 +109,11 @@ SET( public_api_progress_bar_header_files
   ${public_api_src_dir}/controls/progress-bar/progress-bar.h
 )
 
+SET( public_api_render_effects_header_files
+  ${public_api_src_dir}/controls/render-effects/render-effect.h
+  ${public_api_src_dir}/controls/render-effects/background-blur-effect.h
+)
+
 SET( public_api_scrollable_header_files
   ${public_api_src_dir}/controls/scrollable/scrollable.h
 )
@@ -198,6 +208,7 @@ SET( PUBLIC_API_HEADERS ${PUBLIC_API_HEADERS}
   ${public_api_item_view_header_files}
   ${public_api_image_loader_header_files}
   ${public_api_progress_bar_header_files}
+  ${public_api_render_effects_header_files}
   ${public_api_scrollable_header_files}
   ${public_api_scroll_view_header_files}
   ${public_api_slider_header_files}
index 6f19efc..61866f0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VISUAL_PROPERTIES_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ enum
    * @details Name "mixColor", type Property::VECTOR3 or Property::VECTOR4, animatable
    * @SINCE_1_2.60
    * @note Optional
-   * @note To animate an opacity, OPACITY property should be used.
+   * @note Animate support for Property::VECTOR4, which OPACITY property included.
    */
   MIX_COLOR,
 
index 2bc8bdd..b3df4c4 100644 (file)
@@ -1,6 +1,6 @@
 # DALi Introduction
 
-### Introduction
+## Introduction
  + [What is DALi?](@ref dali-introduction)
  + [Features](@ref dali-features)
  + [High Level Design](@ref dali-hld)
   + [Actions](@ref actions)
  + [Tutorial: Hello World](@ref hello-world)
 
-### Getting Started
+## Getting Started
  + [How to build DALi on Ubuntu Desktop](@ref build-ubuntu)
 
-### Programming Guide
+## Programming Guide
  + [Programming Languages:](@ref programming-languages)
   + [C++](@ref c-plus-plus)
   + [JSON](@ref json-support)
   + [Multi-threading Notes](@ref animation-multi-threading-notes)
  + [Styling](@ref styling)
 
-### Resources
+## Resources
  + [Resource Image](@ref resource-image)
  + [9 Patch Image](@ref resource-9-patch)
  + [Buffer Image](@ref resource-buffer)
 
-### Control Base Class & Visuals
+## Control Base Class & Visuals
  + [Background Feature](@ref background)
  + Keyboard Focus
  + [Accessibility](@ref accessibility)
  + [Visuals](@ref visuals)
 
-### UI Components
+## UI Components
  + Buttons
  + [FlexContainer](@ref flex-container)
  + [ItemView](@ref item-view)
  + [Text Field](@ref text-field)
  + [Text Label](@ref text-label)
 
-### RenderTasks
+## RenderTasks
 
-### Shader Effects
+## Shader Effects
  + [Overview](@ref shader-intro)
 
-### Scripting
+## Scripting
  + [JSON Overview](@ref scriptoverview)
  + [JSON Syntax](@ref script-json-specification)
  + [Scripting Hello World](@ref script-hello)
 
-### Tools
+## Tools
  + Environment Variables
  + [Resource Tracking](@ref resourcetracking)
  + Logging
  + [Visual Debug Rendering](@ref debugrendering)
  + [Stagehand - DALi Visual Debugger](@ref stagehand)
 
-### Extending DALi
+## Extending DALi
  + [How to write Custom UI Components](@ref creating-custom-controls)
   + [Size Negotiation for Controls](@ref size-negotiation-controls)
   + [Type Registration](@ref type-registration)
  + [Automated Tests](@ref auto_testing)
  + [Programming Guide](@ref documentationguide)
 
-### Application Optimization Guide
+## Application Optimization Guide
  + [Rescaling Images](@ref resourceimagescaling)
  + Performance & Debugging
  + [Performance Tips](@ref performancetips)
index d3f6302..1405669 100644 (file)
@@ -19,7 +19,7 @@ Namespaces are important
   + MyUIControl
   + Internal::MyUIControl
  
-### General Guidelines:
+## General Guidelines:
 + Try to avoid adding C++ APIs as they become difficult to maintain.
   + Use **properties** as much as possible as Controls should be data driven.
   + These controls will be used through JSON files so need to be compatible.
index 8227fc1..cbe5a4e 100644 (file)
@@ -12,7 +12,7 @@ For documentation, please follow these guidelines:
  - For DOXYGEN to link to the mark down it currently needs a reference {hash myfile}
   
 
-#### Images
+## Images
  Doxygen copies all images in to the same folder as the HTML generated pages so you can just reference it as follows:
 
  ~~~
index 29e4387..4b318a6 100644 (file)
@@ -62,7 +62,7 @@ label.SetProperty( TextLabel::Property::POINT_SIZE,  12.0f );
 
 However the text control will fall-back to using the default font, if the requested font does not support the required scripts.
 
-### Font Styles
+## Font Styles
 
 Setting a font size programmatically is not ideal for applications which support multiple
 screen resolutions and platforms which support multiple logical font sizes.  Also, any
index 188654f..4de6e38 100644 (file)
@@ -36,7 +36,7 @@ rootLayer.add( myActor );  // adds an actor to the root layer
 Example To create two new layers on top of the root layer.
   
 
-### Layer clipping
+## Layer clipping
 
 Clips the contents of the layer to a rectangle.
 
@@ -50,7 +50,7 @@ layer1.SetClippingBox( 20, 20, 100, 100 ); // X, Y, Width, Height
 
 ~~~
 
-### Re-ordering layers
+## Re-ordering layers
 
 The following functions can be used to change the draw order of the layers.
 
@@ -67,21 +67,21 @@ The following functions can be used to change the draw order of the layers.
 Note:
  - The root layer can be moved above and below other layers. However, stage.add( actor ), will always use the root layer object.
 
-### Rendering order of actors inside of a layer
+## Rendering order of actors inside of a layer
 
 Layers have two behaviour modes:
 
  - LAYER_2D ( Default )
  - LAYER_3D
 
-### Layer_2D
+## Layer_2D
 
 ~~~{.cpp}
 // C++
 layer.SetBehavior( Layer::LAYER_2D );
 ~~~
 
-#### Background
+### Background
 
  - Graphics are drawn in DALi using renderers
  - Actors can have zero or many renderers
@@ -144,7 +144,7 @@ For example if we want the render draw order to be 8, 7, 6, with 6 being drawn l
 
 ~~~
 
-### Layer_3D
+## Layer_3D
 
 ~~~{.cpp}
 // C++
@@ -172,7 +172,7 @@ Note:
  - When 2 transparent renderers are the same distance from the camera, you can use depth index to adjust which renderer is drawn first.
 
   
-### Actor drawMode OVERLAY_2D
+## Actor drawMode OVERLAY_2D
 
 Inside a layer it is possible to force a tree actors to be drawn on top everything else in the layer.
   
@@ -180,7 +180,7 @@ The draw order of the actors inside the tree marked OVERLAY_2D, the draw order i
 Depth testing is not used.
   
 
-### Layer Actor Specific Properties
+## Layer Actor Specific Properties
 
 | Name                   |    Type    | Writable     | Animatable|
 |------------------------|------------|--------------|-----------|
index 3bc08c3..5668715 100644 (file)
@@ -8,7 +8,7 @@ Touch events are received via signals.
 
 For C++ API see Dali::Actor::TouchedSignal() and Dali::Actor::HoveredSignal() for more details.
 
-### Hit Testing Rules Summary:
+# Hit Testing Rules Summary:
 
  - An actor is only hittable if the actor's touch signal has a connection.
  - An actor is only hittable when it is between the camera's near and far planes.
@@ -17,7 +17,7 @@ For C++ API see Dali::Actor::TouchedSignal() and Dali::Actor::HoveredSignal() fo
  - To be hittable, an actor must have a non-zero size.
  - If an actor's world color is fully transparent, then it is not hittable; see Dali::Actor::Property::WORLD_COLOR
 
-### Hit Test Algorithm:
+# Hit Test Algorithm:
 
  - RenderTasks
    - Hit testing is dependent on the camera used, which is specific to each RenderTask.
index 8e8c597..d7eafb8 100644 (file)
@@ -74,7 +74,7 @@ The application may request dimensions through `ResourceImage::New()`:
   - `Not specifying either dimension`: IE. Width and Height set to 0 - The target dimensions become the same as the source.
 
   - `Just one dimension specified, Width OR Height (the other dimension set to 0)`:
-    The unspecified dimension will be derived from the specified one whilst maintaining the aspect of the source image. The specified and calculated dimensions become the target dimensions. See more on this case [below](#resourceimagescalingzerodimensions).
+    The unspecified dimension will be derived from the specified one whilst maintaining the aspect of the source image. The specified and calculated dimensions become the target dimensions. See more on this case [below](#resourceimagescaling-zerodimensions).
      
   - `Width AND Height both specified` The requested dimensions pass straight through to become the target for fitting.
   
@@ -109,7 +109,7 @@ The operation of each of these modes is as follows:
 <sub> **Fitting modes**: *The top row shows the effect of each mode when a tall target rectangle is applied to a square image. The middle row applies a wide target to a square raw image. The bottom row uses a target with the same aspect ratio as the raw image. This example shows that `SCALE_TO_FILL` is the only option for which the dimensions of the fitted image result fill all the area of the target. Others would be letterboxed with borders. `SHRINK_TO_FIT` is always equal to one of `FIT_WIDTH` or `FIT_HEIGHT`: in each case it is the minimum of them. As a special case, where the aspect ratio of raw image and target match, all fitting modes generate an exact match final image and are equivalent to each other.* </sub>
   
 
-Note: The image is scaled to the same aspect and shrunk to fit depending on fitting mode. It is not upscaled. See: [Upscaling](#resourceimagescalingupscaling).
+Note: The image is scaled to the same aspect and shrunk to fit depending on fitting mode. It is not upscaled. See: [Upscaling](#resourceimagescaling-upscaling).
   
   
 
@@ -209,7 +209,7 @@ ResourceImage image = ResourceImage::New( "flower.png", ImageDimensions( 32, 24
 ~~~
   
 
-### Passing a Zero Dimension {#resourceimagescalingzerodimensions}
+### Passing a Zero Dimension {#resourceimagescaling-zerodimensions}
   
 Passing in a single zero dimension is equivalent to specifying `FIT_WIDTH` or `FIT_HEIGHT` `FittingMode`s. When a non-zero width and zero height are specified, the fitting done will be identical to the result using `FittingMode` `FIT_WIDTH`. When passing a zero width and non-zero height, the effect of applying the chosen `FittingMode` to the calculated target dimensions is always identical to applying the `FIT_HEIGHT` mode.
   
@@ -233,7 +233,7 @@ ResourceImage image = ResourceImage::New("flower.png", ImageDimensions(0, y));
   
 Note:
 - If both values are specified as 0, both dimensions are taken from the source image.
-- If both dimensions are not 0, this value becomes the 'natural size' even if it differs from the actual pixel dimensions loaded. [This requires some care in rendering to avoid distortion](#resourceimagescaling-samplingmodesrendernaturalsize).
+- If both dimensions are not 0, this value becomes the 'natural size' even if it differs from the actual pixel dimensions loaded. [This requires some care in rendering to avoid distortion](#resourceimagescaling-naturalsizecompensation).
   
 
 ### Code Examples for Sampling Modes  {#resourceimagescaling-codeexamplessamplingmodes}
@@ -316,7 +316,7 @@ Compressed textures cannot be scaled at load time as their formats are designed
 ### Compensation for Natural Size != Pixel Width / Height {#resourceimagescaling-naturalsizecompensation}
   
 Because the *natural size* of an image is
-[taken from the requested dimensions](#resourceimagescaling-samplingmodesdimensionflow)
+[taken from the requested dimensions](#resourceimagescaling-workflow)
 passed to `ResourceImage::New()` rather than passing through the same calculations that result in the eventual pixel width and height loaded,
 the *natural size* and pixel dimensions of an image will differ when loaded with scaling.
 It is inherent in the definition of fitting modes other than `SCALE_TO_FILL` not to match the requested dimensions, so in general, images loaded with them must have this mismatch between *natural size* and actual pixel width.
@@ -334,7 +334,7 @@ There are circumstance, however, in which the the natural size of a resource ima
 In these cases the image may be used freely in layouts controlled by size negotiation.
 Additionally, if the requested size has the same aspect ratio as the eventual pixel array loaded, and the fitting mode is `SCALE_TO_FILL` or `BOX` and `NO_FILTER` sampling modes are avoided, even if they don't match in dimensions exactly, the eventual image will be drawn without aspect ratio distortion although it will be scaled at render time.
   
-The fitting and scaling modes [demo](#resourceimagescalingsamplingmodesdemoexamples) allows this behavior to be be explored dynamically when the fitting mode is changed from `SCALE_TO_FILL`.
+The fitting and scaling modes [demo](#resourceimagescaling-samplingmodesdemoexamples) allows this behavior to be be explored dynamically when the fitting mode is changed from `SCALE_TO_FILL`.
   
 The application can of course only pass dimensions which are just right if it happens to know the raw dimensions or if it accesses the the image resource and reads the raw dimensions from its header.
   
index bc37279..9f38656 100644 (file)
@@ -26,18 +26,18 @@ text complete it's current scrolling loop then stop.
 
 ## The additional properties below can be set to customize the scrolling behaviour
 
-#### AUTO_SCROLL_SPEED
+### AUTO_SCROLL_SPEED
 
 This controls the speed of the scrolling, the speed should be provided as pixels/second.
 
-#### AUTO_SCROLL_LOOP_COUNT
+### AUTO_SCROLL_LOOP_COUNT
 
 This specifies how many times the text will complete a full scroll cycle.
 If not set then it will keep scrolling until ENABLE_AUTO_SCROLL is set to false.
 
 Setting ENABLE_AUTO_SCROLL to false will stop scrolling whilst still maintaining the original loop count value for when it is next started.
 
-#### AUTO_SCROLL_GAP
+### AUTO_SCROLL_GAP
 
 This specifies the amount of whitespace to display before the scrolling text is shown again.
 
index 20cdec5..8d68e3c 100644 (file)
@@ -361,7 +361,7 @@ control.SetProperty( Control::Property::BACKGROUND, map );
 ~~~
 ___________________________________________________________________________________________________
 
-## Animated Image Visual {#animated-image-visual}
+### Animated Image Visual {#animated-image-visual}
 
 Renders an animated image into the visual's quad geometry. Currently, only the GIF format is supported.
 
index d42db5a..33c7947 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.3.24
+Version:    2.3.42
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT