Merge branch 'devel/master' into tizen submit/tizen/20220420.085640
authorJaehyun Cho <jae_hyun.cho@samsung.com>
Wed, 20 Apr 2022 07:19:54 +0000 (16:19 +0900)
committerJaehyun Cho <jae_hyun.cho@samsung.com>
Wed, 20 Apr 2022 07:19:54 +0000 (16:19 +0900)
Change-Id: I2bd935cee1c3e0e07ab05224f0aa8e94ae352697

113 files changed:
automated-tests/resources/MRendererTest.gltf [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-Gltf2Loader.cpp
automated-tests/src/dali-toolkit-internal/CMakeLists.txt
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.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/CMakeLists.txt
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-gl-abstraction.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-reflection.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-web-engine.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
automated-tests/src/dali-toolkit/utc-Dali-KeyboardFocusManager.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-TextLabel.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextSelectionPopup.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
dali-scene-loader/internal/gltf2-asset.h
dali-scene-loader/public-api/gltf2-loader.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/devel-api/focus-manager/focus-finder.cpp
dali-toolkit/devel-api/utility/npatch-utilities.cpp
dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/gl-view/drawable-view-impl.h [new file with mode: 0644]
dali-toolkit/internal/controls/gl-view/gl-view-impl.cpp
dali-toolkit/internal/controls/gl-view/gl-view-impl.h
dali-toolkit/internal/controls/gl-view/gl-view-interface-impl.h [new file with mode: 0644]
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.h
dali-toolkit/internal/controls/text-controls/text-editor-property-handler.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-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/text-controls/text-selection-popup-property-handler.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.h [new file with mode: 0644]
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/text/bounded-paragraph-run.h
dali-toolkit/internal/text/decorator/text-decorator.cpp
dali-toolkit/internal/text/layouts/layout-engine.cpp
dali-toolkit/internal/text/markup-processor-anchor.cpp
dali-toolkit/internal/text/markup-processor-background.cpp
dali-toolkit/internal/text/markup-processor-character-spacing.cpp
dali-toolkit/internal/text/markup-processor-color.cpp
dali-toolkit/internal/text/markup-processor-embedded-item.cpp
dali-toolkit/internal/text/markup-processor-font.cpp
dali-toolkit/internal/text/markup-processor-paragraph.cpp
dali-toolkit/internal/text/markup-processor-paragraph.h
dali-toolkit/internal/text/markup-processor-span.cpp
dali-toolkit/internal/text/markup-processor-strikethrough.cpp
dali-toolkit/internal/text/markup-processor-underline.cpp
dali-toolkit/internal/text/markup-processor.cpp
dali-toolkit/internal/text/markup-tags-and-attributes.h [new file with mode: 0644]
dali-toolkit/internal/text/rendering/text-typesetter.cpp
dali-toolkit/internal/text/text-controller-event-handler.cpp
dali-toolkit/internal/text/text-controller-impl-event-handler.cpp
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-controller-relayouter.cpp
dali-toolkit/internal/text/text-controller-relayouter.h
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
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/texture-manager/texture-manager-type.h
dali-toolkit/internal/texture-manager/texture-upload-observer.cpp
dali-toolkit/internal/texture-manager/texture-upload-observer.h
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/fixed-image-cache.h
dali-toolkit/internal/visuals/animated-image/image-cache.cpp
dali-toolkit/internal/visuals/animated-image/image-cache.h
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/npatch-data.cpp
dali-toolkit/internal/visuals/npatch-data.h
dali-toolkit/internal/visuals/npatch-loader.cpp
dali-toolkit/internal/visuals/npatch-loader.h
dali-toolkit/internal/visuals/npatch/npatch-visual.cpp
dali-toolkit/internal/visuals/npatch/npatch-visual.h
dali-toolkit/internal/visuals/svg/svg-visual.cpp
dali-toolkit/internal/visuals/svg/svg-visual.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/public-api/controls/gl-view/gl-view.cpp
dali-toolkit/public-api/controls/gl-view/gl-view.h
dali-toolkit/public-api/dali-toolkit-version.cpp
dali-toolkit/styles/images-common/button-down.9.png
dali-toolkit/styles/images-common/button-up.9.png
dali-toolkit/styles/images-common/checkbox-selected-disabled.png
dali-toolkit/styles/images-common/checkbox-selected.png
dali-toolkit/styles/images-common/checkbox-unselected-disabled.png
dali-toolkit/styles/images-common/checkbox-unselected.png
dali-toolkit/styles/images-common/radio-button-selected-disabled.png
dali-toolkit/styles/images-common/radio-button-selected.png
dali-toolkit/styles/images-common/radio-button-unselected-disabled.png
dali-toolkit/styles/images-common/radio-button-unselected.png
packaging/dali-toolkit.spec

diff --git a/automated-tests/resources/MRendererTest.gltf b/automated-tests/resources/MRendererTest.gltf
new file mode 100644 (file)
index 0000000..ae3d113
--- /dev/null
@@ -0,0 +1,179 @@
+{\r
+  "scene" : 0,\r
+  "scenes" : [\r
+    {\r
+      "nodes" : [ 0 ]\r
+    }\r
+  ],\r
+\r
+  "nodes" : [\r
+    {\r
+      "mesh" : 0,\r
+      "rotation" : [ 0.0, 0.0, 0.0, 1.0 ],\r
+      "scale": [\r
+          100.0,\r
+          100.0,\r
+          100.0\r
+      ],\r
+      "name": "RootNode"\r
+    }\r
+  ],\r
+\r
+  "meshes" : [\r
+    {\r
+      "primitives" : [ {\r
+        "attributes" : {\r
+          "POSITION" : 1\r
+        },\r
+        "indices" : 0,\r
+        "material": 0\r
+      } ]\r
+    }\r
+  ],\r
+\r
+  "animations": [\r
+    {\r
+      "samplers" : [\r
+        {\r
+          "input" : 2,\r
+          "interpolation" : "LINEAR",\r
+          "output" : 3\r
+        }\r
+      ],\r
+      "channels" : [ {\r
+        "sampler" : 0,\r
+        "target" : {\r
+          "node" : 0,\r
+          "path" : "rotation"\r
+        }\r
+      } ]\r
+    }\r
+  ],\r
+\r
+  "buffers" : [\r
+    {\r
+      "uri" : "simpleTriangle.bin",\r
+      "byteLength" : 44\r
+    },\r
+    {\r
+      "uri" : "animation.bin",\r
+      "byteLength" : 100\r
+    }\r
+  ],\r
+\r
+  "bufferViews" : [\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 6,\r
+      "target" : 34963\r
+    },\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 8,\r
+      "byteLength" : 36,\r
+      "target" : 34962\r
+    },\r
+    {\r
+      "buffer" : 1,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 100\r
+    }\r
+  ],\r
+\r
+  "accessors" : [\r
+    {\r
+      "bufferView" : 0,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5123,\r
+      "count" : 3,\r
+      "type" : "SCALAR",\r
+      "max" : [ 2 ],\r
+      "min" : [ 0 ]\r
+    },\r
+    {\r
+      "bufferView" : 1,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 3,\r
+      "type" : "VEC3",\r
+      "max" : [ 1.0, 1.0, 0.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 2,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 5,\r
+      "type" : "SCALAR",\r
+      "max" : [ 1.0 ],\r
+      "min" : [ 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 2,\r
+      "byteOffset" : 20,\r
+      "componentType" : 5126,\r
+      "count" : 5,\r
+      "type" : "VEC4",\r
+      "max" : [ 0.0, 0.0, 1.0, 1.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0, -0.707 ]\r
+    }\r
+  ],\r
+\r
+  "asset" : {\r
+    "generator": "glTF Tools for M-Renderer",\r
+    "version" : "2.0"\r
+  },\r
+\r
+  "materials": [\r
+    {\r
+      "pbrMetallicRoughness":\r
+      {\r
+        "baseColorTexture":\r
+        {\r
+          "index": 0,\r
+          "texCoord": 0\r
+        },\r
+        "metallicFactor": 0,\r
+        "baseColorFactor":\r
+        [\r
+          1,\r
+          1,\r
+          1,\r
+          1\r
+        ],\r
+        "roughnessFactor": 1\r
+      },\r
+      "emissiveFactor":\r
+      [\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "alphaMode": "OPAQUE",\r
+      "doubleSided": false\r
+    }\r
+  ],\r
+\r
+  "textures": [\r
+    {\r
+      "sampler": 0,\r
+      "source": 0\r
+    }\r
+  ],\r
+\r
+  "images": [\r
+    {\r
+      "uri": "AnimatedCube_BaseColor.png"\r
+    }\r
+  ],\r
+\r
+  "samplers": [\r
+    {\r
+      "magFilter": 9729,\r
+      "minFilter": 9986,\r
+      "wrapS": 10497,\r
+      "wrapT": 10497\r
+    }\r
+  ]\r
+}\r
index 3f829dc..47515b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
 // Enable debug log for test coverage
 #define DEBUG_ENABLED 1
 
+#include <dali-test-suite-utils.h>
+#include <string_view>
+#include "dali-scene-loader/public-api/gltf2-loader.h"
+#include "dali-scene-loader/public-api/load-result.h"
 #include "dali-scene-loader/public-api/resource-bundle.h"
 #include "dali-scene-loader/public-api/scene-definition.h"
-#include "dali-scene-loader/public-api/load-result.h"
-#include "dali-scene-loader/public-api/gltf2-loader.h"
 #include "dali-scene-loader/public-api/shader-definition-factory.h"
-#include <dali-test-suite-utils.h>
-#include <string_view>
 
 using namespace Dali;
 using namespace Dali::SceneLoader;
 
 #define DALI_TEST_THROW(expression, exception, predicate) \
-  {\
-    bool daliTestThrowSuccess__ = false;\
-    try\
-    {\
-      do { expression; } while(0);\
-      printf("No exception was thrown.\n");\
-    }\
-    catch (std::decay<exception>::type& ex)\
-    {\
-      daliTestThrowSuccess__ = predicate(ex);\
-    }\
-    catch (...)\
-    {\
-      printf("Wrong type of exception thrown.\n");\
-    }\
-    DALI_TEST_CHECK(daliTestThrowSuccess__);\
+  {                                                       \
+    bool daliTestThrowSuccess__ = false;                  \
+    try                                                   \
+    {                                                     \
+      do                                                  \
+      {                                                   \
+        expression;                                       \
+      } while(0);                                         \
+      printf("No exception was thrown.\n");               \
+    }                                                     \
+    catch(std::decay<exception>::type & ex)               \
+    {                                                     \
+      daliTestThrowSuccess__ = predicate(ex);             \
+    }                                                     \
+    catch(...)                                            \
+    {                                                     \
+      printf("Wrong type of exception thrown.\n");        \
+    }                                                     \
+    DALI_TEST_CHECK(daliTestThrowSuccess__);              \
   }
 
 namespace
 {
 struct Context
 {
-  ResourceBundle resources;
+  ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
+    return TEST_RESOURCE_DIR "/";
+  };
+
+  ResourceBundle  resources;
   SceneDefinition scene;
 
-  std::vector<AnimationDefinition> animations;
+  std::vector<AnimationDefinition>      animations;
   std::vector<AnimationGroupDefinition> animationGroups;
-  std::vector<CameraParameters> cameras;
-  std::vector<LightParameters> lights;
+  std::vector<CameraParameters>         cameras;
+  std::vector<LightParameters>          lights;
 
-  LoadResult loadResult {
+  LoadResult loadResult{
     resources,
     scene,
     animations,
     animationGroups,
     cameras,
-    lights
-  };
+    lights};
 };
 
 struct ExceptionMessageStartsWith
@@ -77,7 +83,7 @@ struct ExceptionMessageStartsWith
   bool operator()(const std::runtime_error& e)
   {
     const bool success = (0 == strncmp(e.what(), expected.data(), expected.size()));
-    if (!success)
+    if(!success)
     {
       printf("Expected: %s, got: %s.\n", expected.data(), e.what());
     }
@@ -85,7 +91,7 @@ struct ExceptionMessageStartsWith
   }
 };
 
-}
+} // namespace
 
 int UtcDaliGltfLoaderFailedToLoad(void)
 {
@@ -95,8 +101,8 @@ int UtcDaliGltfLoaderFailedToLoad(void)
   sdf.SetResources(ctx.resources);
 
   DALI_TEST_THROW(LoadGltfScene("non-existent.gltf", sdf, ctx.loadResult),
-    std::runtime_error,
-    ExceptionMessageStartsWith{"Failed to load"});
+                  std::runtime_error,
+                  ExceptionMessageStartsWith{"Failed to load"});
 
   DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
   DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
@@ -123,8 +129,8 @@ int UtcDaliGltfLoaderFailedToParse(void)
   sdf.SetResources(ctx.resources);
 
   DALI_TEST_THROW(LoadGltfScene(TEST_RESOURCE_DIR "/invalid.gltf", sdf, ctx.loadResult),
-    std::runtime_error,
-    ExceptionMessageStartsWith{"Failed to parse"});
+                  std::runtime_error,
+                  ExceptionMessageStartsWith{"Failed to parse"});
 
   DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
   DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
@@ -159,50 +165,46 @@ int UtcDaliGltfLoaderSuccess1(void)
 
   auto& materials = ctx.resources.mMaterials;
   DALI_TEST_EQUAL(2u, materials.size());
-  const MaterialDefinition materialGroundTruth[] {
-    {
-      MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
-        MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY | MaterialDefinition::GLTF_CHANNELS |
-        (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
-      0,
-      Vector4(1.f, .766f, .336f, 1.f),
-      1.f,
-      0.f,
-      {
-        { MaterialDefinition::ALBEDO,
-          { "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
-        { MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
-          { "AnimatedCube_MetallicRoughness.png",
-            SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT) } },
-        { MaterialDefinition::NORMAL,
-          { "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
-      }
-    },
-    {
-      MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
-        MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
-      0,
-      Vector4(1.f, .766f, .336f, 1.f),
-      1.f,
-      0.f,
-      {
-        { MaterialDefinition::ALBEDO,
-          { "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
-        { MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
-          { "AnimatedCube_MetallicRoughness.png",
-            SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT) } },
-        { MaterialDefinition::NORMAL,
-          { "AnimatedCube_BaseColor.png",
-            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
-      }
-    },
+  const MaterialDefinition materialGroundTruth[]{
+    {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
+       MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY | MaterialDefinition::GLTF_CHANNELS |
+       (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
+     0,
+     Vector4(1.f, .766f, .336f, 1.f),
+     1.f,
+     0.f,
+     {
+       {MaterialDefinition::ALBEDO,
+        {"AnimatedCube_BaseColor.png",
+         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
+       {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
+        {"AnimatedCube_MetallicRoughness.png",
+         SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT)}},
+       {MaterialDefinition::NORMAL,
+        {"AnimatedCube_BaseColor.png",
+         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
+     }},
+    {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
+       MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
+     0,
+     Vector4(1.f, .766f, .336f, 1.f),
+     1.f,
+     0.f,
+     {
+       {MaterialDefinition::ALBEDO,
+        {"AnimatedCube_BaseColor.png",
+         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
+       {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
+        {"AnimatedCube_MetallicRoughness.png",
+         SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT)}},
+       {MaterialDefinition::NORMAL,
+        {"AnimatedCube_BaseColor.png",
+         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
+     }},
   };
 
   auto iMaterial = materials.begin();
-  for (auto& m : materialGroundTruth)
+  for(auto& m : materialGroundTruth)
   {
     printf("material %ld\n", iMaterial - materials.begin());
     auto& md = iMaterial->first;
@@ -214,7 +216,7 @@ int UtcDaliGltfLoaderSuccess1(void)
 
     DALI_TEST_EQUAL(md.mTextureStages.size(), m.mTextureStages.size());
     auto iTexture = md.mTextureStages.begin();
-    for (auto& ts: m.mTextureStages)
+    for(auto& ts : m.mTextureStages)
     {
       printf("texture %ld\n", iTexture - md.mTextureStages.begin());
       DALI_TEST_EQUAL(iTexture->mSemantic, ts.mSemantic);
@@ -228,48 +230,47 @@ int UtcDaliGltfLoaderSuccess1(void)
   auto& meshes = ctx.resources.mMeshes;
   DALI_TEST_EQUAL(2u, meshes.size());
 
-  using Blob = MeshDefinition::Blob;
+  using Blob     = MeshDefinition::Blob;
   using Accessor = MeshDefinition::Accessor;
-  const MeshDefinition meshGroundTruth[] {
+  const MeshDefinition meshGroundTruth[]{
     {
       0,
       Geometry::TRIANGLES,
       "AnimatedCube.bin",
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
     },
     {
       0,
       Geometry::TRIANGLES,
       "AnimatedCube.bin",
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
-      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
+      Accessor{Blob{0, 0}, {}},
     },
   };
 
   auto iMesh = meshes.begin();
-  for (auto& m : meshGroundTruth)
+  for(auto& m : meshGroundTruth)
   {
     printf("mesh %ld\n", iMesh - meshes.begin());
 
     auto& md = iMesh->first;
     DALI_TEST_EQUAL(md.mFlags, m.mFlags);
     DALI_TEST_EQUAL(md.mPrimitiveType, m.mPrimitiveType);
-    for (auto mp: {
-      &MeshDefinition::mIndices,
-      &MeshDefinition::mPositions,
-      &MeshDefinition::mNormals,
-      &MeshDefinition::mTexCoords,
-      &MeshDefinition::mTangents,
-      &MeshDefinition::mJoints0,
-      &MeshDefinition::mWeights0
-    })
+    for(auto mp : {
+          &MeshDefinition::mIndices,
+          &MeshDefinition::mPositions,
+          &MeshDefinition::mNormals,
+          &MeshDefinition::mTexCoords,
+          &MeshDefinition::mTangents,
+          &MeshDefinition::mJoints0,
+          &MeshDefinition::mWeights0})
     {
       DALI_TEST_EQUAL((md.*mp).IsDefined(), (m.*mp).IsDefined());
       DALI_TEST_EQUAL((md.*mp).mBlob.IsDefined(), (m.*mp).mBlob.IsDefined());
@@ -296,24 +297,25 @@ int UtcDaliGltfLoaderSuccessShort(void)
   TestApplication app;
 
   const std::string resourcePath = TEST_RESOURCE_DIR "/";
-  auto pathProvider = [resourcePath](ResourceType::Value) {
+  auto              pathProvider = [resourcePath](ResourceType::Value) {
     return resourcePath;
   };
 
   Customization::Choices choices;
-  for (auto modelName : {
-    "2CylinderEngine",
-    "AnimatedMorphCube",
-    "AnimatedMorphSphere",
-    "AnimatedTriangle",
-    "BoxAnimated",
-    "CesiumMan",
-    "CesiumMilkTruck",
-    "EnvironmentTest",
-    "MetalRoughSpheres",
-    "MorphPrimitivesTest",
-    "SimpleSparseAccessor",
-  })
+  for(auto modelName : {
+        "2CylinderEngine",
+        "AnimatedMorphCube",
+        "AnimatedMorphSphere",
+        "AnimatedTriangle",
+        "BoxAnimated",
+        "CesiumMan",
+        "CesiumMilkTruck",
+        "EnvironmentTest",
+        "MetalRoughSpheres",
+        "MorphPrimitivesTest",
+        "MRendererTest",
+        "SimpleSparseAccessor",
+      })
   {
     Context ctx;
 
@@ -329,17 +331,17 @@ int UtcDaliGltfLoaderSuccessShort(void)
     DALI_TEST_CHECK(ctx.scene.GetNodeCount() > 0);
 
     auto& scene = ctx.scene;
-    for (auto iRoot : scene.GetRoots())
+    for(auto iRoot : scene.GetRoots())
     {
-      struct Visitor: NodeDefinition::IVisitor
+      struct Visitor : NodeDefinition::IVisitor
       {
-        struct ResourceReceiver: IResourceReceiver
+        struct ResourceReceiver : IResourceReceiver
         {
           std::vector<bool> mCounts;
 
           void Register(ResourceType::Value type, Index id) override
           {
-            if (type == ResourceType::Mesh)
+            if(type == ResourceType::Mesh)
             {
               mCounts[id] = true;
             }
@@ -348,21 +350,22 @@ int UtcDaliGltfLoaderSuccessShort(void)
 
         void Start(NodeDefinition& n) override
         {
-          if (n.mRenderable)
+          if(n.mRenderable)
           {
             n.mRenderable->RegisterResources(receiver);
           }
         }
 
         void Finish(NodeDefinition& n) override
-        {}
+        {
+        }
       } visitor;
       visitor.receiver.mCounts.resize(resources.mMeshes.size(), false);
 
       scene.Visit(iRoot, choices, visitor);
-      for (uint32_t i0 = 0, i1 = resources.mMeshes.size(); i0 < i1; ++i0)
+      for(uint32_t i0 = 0, i1 = resources.mMeshes.size(); i0 < i1; ++i0)
       {
-        if (visitor.receiver.mCounts[i0])
+        if(visitor.receiver.mCounts[i0])
         {
           auto raw = resources.mMeshes[i0].first.LoadRaw(resourcePath);
           DALI_TEST_CHECK(!raw.mAttribs.empty());
@@ -376,3 +379,59 @@ int UtcDaliGltfLoaderSuccessShort(void)
 
   END_TEST;
 }
+
+int UtcDaliGltfLoaderMRendererTest(void)
+{
+  Context ctx;
+
+  ShaderDefinitionFactory sdf;
+  sdf.SetResources(ctx.resources);
+  auto& resources = ctx.resources;
+  resources.mEnvironmentMaps.push_back({});
+
+  LoadGltfScene(TEST_RESOURCE_DIR "/MRendererTest.gltf", sdf, ctx.loadResult);
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "RootNode");
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mScale, Vector3(1.0f, 1.0f, 1.0f));
+
+  DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
+
+  ViewProjection viewProjection;
+  Transforms     xforms{
+    MatrixStack{},
+    viewProjection};
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+
+  Actor root = Actor::New();
+  SetActorCentered(root);
+  for(auto iRoot : roots)
+  {
+    auto resourceRefs = resources.CreateRefCounter();
+    scene.CountResourceRefs(iRoot, choices, resourceRefs);
+    resources.CountEnvironmentReferences(resourceRefs);
+    resources.LoadResources(resourceRefs, ctx.pathProvider);
+    if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
+    {
+      scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
+      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
+      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      root.Add(actor);
+    }
+  }
+
+  DALI_TEST_EQUAL(root.GetChildCount(), 1u);
+  DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
+  DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
+
+  END_TEST;
+}
index 5ba86de..035f6c3 100755 (executable)
@@ -73,6 +73,7 @@ SET(TEST_HARNESS_SOURCES
    ../dali-toolkit/dali-toolkit-test-utils/toolkit-tts-player.cpp
    ../dali-toolkit/dali-toolkit-test-utils/toolkit-vector-animation-renderer.cpp
    ../dali-toolkit/dali-toolkit-test-utils/toolkit-vector-image-renderer.cpp
+   ../dali-toolkit/dali-toolkit-test-utils/toolkit-web-engine.cpp
    ../dali-toolkit/dali-toolkit-test-utils/toolkit-window.cpp
    ../dali-toolkit/dali-toolkit-test-utils/toolkit-scene-holder.cpp
    ../dali-toolkit/dali-toolkit-test-utils/dali-test-suite-utils.cpp
index c156b6e..9a3ef2b 100644 (file)
@@ -12,6 +12,7 @@
 #include <dali-toolkit/devel-api/controls/control-devel.h>
 #include <dali-toolkit/devel-api/controls/popup/popup.h>
 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
+#include <dali-toolkit/devel-api/controls/web-view/web-view.h>
 #include <dali/devel-api/actors/actor-devel.h>
 #include <dali/devel-api/common/stage.h>
 #include <cstdlib>
@@ -1299,3 +1300,36 @@ int UtcDaliAccessibilityCheckHighlight(void)
   Dali::Accessibility::TestEnableSC( false );
   END_TEST;
 }
+
+int UtcDaliWebViewAccessible(void)
+{
+  ToolkitTestApplication application;
+
+  auto webView = Dali::Toolkit::WebView::New();
+  auto webViewAccessible = Dali::Accessibility::Accessible::Get(webView);
+
+  DALI_TEST_CHECK(webViewAccessible);
+
+  auto children = webViewAccessible->GetChildren();
+
+  DALI_TEST_CHECK(children.empty());
+
+  Dali::Accessibility::TestEnableSC(true);
+
+  children = webViewAccessible->GetChildren();
+
+  DALI_TEST_EQUALS(children.size(), 1u, TEST_LOCATION);
+
+  auto address = children[0]->GetAddress();
+
+  DALI_TEST_CHECK(address);
+  DALI_TEST_NOT_EQUALS(address.GetBus(), webViewAccessible->GetAddress().GetBus(), 0.0f, TEST_LOCATION);
+
+  Dali::Accessibility::TestEnableSC(false);
+
+  children = webViewAccessible->GetChildren();
+
+  DALI_TEST_CHECK(children.empty());
+
+  END_TEST;
+}
index 6ec8617..e8088a3 100644 (file)
@@ -591,6 +591,158 @@ int UtcTextureManagerUseInvalidMask(void)
   END_TEST;
 }
 
+int UtcTextureManagerUseInvalidMaskAndMaskLoadedFirst(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcTextureManagerUseInvalidMask when normal image loaded first, and mask image loaded first");
+  tet_infoline("Try to check PostLoad works well");
+
+  TextureManager textureManager; // Create new texture manager
+
+  TestObserver                       observer;
+  std::string                        filename(TEST_IMAGE_FILE_NAME);
+  std::string                        maskname("invalid.png");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl       = maskname;
+  maskInfo->mAlphaMaskId        = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask         = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  auto                          textureId(TextureManager::INVALID_TEXTURE_ID);
+  Vector4                       atlasRect(0.f, 0.f, 1.f, 1.f);
+  Dali::ImageDimensions         atlasRectSize(0, 0);
+  bool                          synchronousLoading(false);
+  bool                          atlasingStatus(false);
+  bool                          loadingStatus(false);
+  auto                          preMultiply         = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr          atlasManager        = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    synchronousLoading,
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply);
+
+  DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(observer.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcTextureManagerUseInvalidMaskAndMaskLoadedLater(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcTextureManagerUseInvalidMask when normal image loaded first, and mask image loaded later");
+  tet_infoline("Try to check CheckForWaitingTexture called");
+
+  TextureManager textureManager; // Create new texture manager
+
+  TestObserver                       observer;
+  std::string                        filename(TEST_IMAGE_FILE_NAME);
+  std::string                        maskname("invalid.png");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl       = maskname;
+  maskInfo->mAlphaMaskId        = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask         = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  auto                          textureId(TextureManager::INVALID_TEXTURE_ID);
+  Vector4                       atlasRect(0.f, 0.f, 1.f, 1.f);
+  Dali::ImageDimensions         atlasRectSize(0, 0);
+  bool                          synchronousLoading(false);
+  bool                          atlasingStatus(false);
+  bool                          loadingStatus(false);
+  auto                          preMultiply         = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr          atlasManager        = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    synchronousLoading,
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply);
+
+  DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // CAPTION : HARD-CODING for coverage. If you are a good boy, Do not follow this code.
+  {
+    Dali::Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(
+      filename,
+      ImageDimensions(),
+      FittingMode::SCALE_TO_FILL,
+      SamplingMode::BOX_THEN_LINEAR,
+      true, ///< synchronousLoading
+      nullptr,
+      true, ///< orientationCorrection
+      preMultiply);
+
+    textureManager.AsyncLoadComplete(textureId, pixelBuffer);
+    textureManager.AsyncLoadComplete(maskInfo->mAlphaMaskId, Dali::Devel::PixelBuffer());
+    textureManager.Remove(maskInfo->mAlphaMaskId, nullptr);
+    textureManager.Remove(textureId, &observer);
+  }
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(observer.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcTextureManagerSynchronousLoadingFail(void)
 {
   ToolkitTestApplication application;
index 13464d3..6999ffa 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
 #include <dali-toolkit/internal/visuals/color/color-visual.h>
+#include <dali-toolkit/internal/visuals/npatch/npatch-visual.h>
 #include <dummy-visual.h>
 #include <../dali-toolkit/dali-toolkit-test-utils/dummy-control.h>
 #include <dali-toolkit/devel-api/visuals/arc-visual-properties-devel.h>
@@ -253,3 +254,46 @@ int UtcDaliArcVisualCreateInstancePropertyMap(void)
 
   END_TEST;
 }
+int UtcDaliVisualUpdateBrokenImageRenderer(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UpdateBrokenImageRenderer Test" );
+
+  Toolkit::Internal::VisualFactoryCache* factoryCache = new Toolkit::Internal::VisualFactoryCache(false);
+
+  std::string defaultBrokenImageUrl = "not-9patch.png";
+
+  // Set default broken image
+  factoryCache->SetBrokenImageUrl(defaultBrokenImageUrl, std::vector<std::string>());
+
+  //Created dummy renderer
+  Geometry geometry = factoryCache->GetGeometry(Toolkit::Internal::VisualFactoryCache::QUAD_GEOMETRY);
+  Shader   shader   = Shader::New("foo","bar");
+  Renderer renderer = Renderer::New(geometry, shader);
+
+  DALI_TEST_CHECK(renderer);
+
+  // renderer doesn't changed.
+  factoryCache->UpdateBrokenImageRenderer(renderer, Vector2::ZERO, true);
+  Shader testShader1 = renderer.GetShader();
+
+  // Get default image renderer.
+  factoryCache->UpdateBrokenImageRenderer(renderer, Vector2::ZERO, false);
+  Shader testShader2 = renderer.GetShader();
+
+  // Get default image renderer but nine patch.
+  // Note : This API behavior can be changed. (DALi don't consider about default BrokenImageUrl is failed.
+  defaultBrokenImageUrl = "yes-9patch.9.png";
+  factoryCache->SetBrokenImageUrl(defaultBrokenImageUrl, std::vector<std::string>());
+  factoryCache->UpdateBrokenImageRenderer(renderer, Vector2::ZERO, false);
+  Shader testShader3 = renderer.GetShader();
+
+  DALI_TEST_CHECK(testShader1 != factoryCache->GetShader(Toolkit::Internal::VisualFactoryCache::IMAGE_SHADER));
+  DALI_TEST_CHECK(testShader1 != factoryCache->GetShader(Toolkit::Internal::VisualFactoryCache::NINE_PATCH_SHADER));
+  DALI_TEST_CHECK(testShader2 == factoryCache->GetShader(Toolkit::Internal::VisualFactoryCache::IMAGE_SHADER));
+  DALI_TEST_CHECK(testShader3 == factoryCache->GetShader(Toolkit::Internal::VisualFactoryCache::NINE_PATCH_SHADER));
+
+  delete factoryCache;
+
+  END_TEST;
+}
index 641a9df..1dbbf90 100755 (executable)
@@ -79,6 +79,7 @@ SET(TC_SOURCES
   utc-Dali-DragAndDropDetector.cpp
   utc-Dali-NPatchUtilities.cpp
   utc-Dali-GlView.cpp
+  utc-Dali-GlViewDirectRendering.cpp
 )
 
 # List of test harness files (Won't get parsed for test cases)
index cc7fac4..a1f8405 100644 (file)
@@ -997,10 +997,17 @@ public:
       {
         name            = uniform.name.substr(0, iter);
         auto arrayCount = std::stoi(uniform.name.substr(iter + 1));
+        iter            = uniform.name.find("]");
+        std::string suffix;
+        if(iter != std::string::npos && iter + 1 != uniform.name.length())
+        {
+          suffix = uniform.name.substr(iter + 1); // If there is a suffix, it means its an element of an array of struct
+        }
+
         for(int i = 0; i < arrayCount; ++i)
         {
           std::stringstream nss;
-          nss << name << "[" << i << "]";
+          nss << name << "[" << i << "]" << suffix;
           GetUniformLocation(program, nss.str().c_str()); // Generate a GL loc per element
         }
       }
index 5b3da8c..6cb90ba 100644 (file)
@@ -147,8 +147,6 @@ TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t p
   for(const auto& data : mCustomUniforms)
   {
     fprintf(stderr, "\ncustom uniforms: %s\n", data.name.c_str());
-    mDefaultUniformBlock.members.emplace_back();
-    auto& item = mDefaultUniformBlock.members.back();
 
     auto iter        = data.name.find("[", 0);
     int  numElements = 1;
@@ -161,26 +159,60 @@ TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t p
       {
         numElements = 1;
       }
-
-      item.name         = baseName;
-      item.binding      = 0;
-      item.bufferIndex  = 0;
-      item.uniformClass = Graphics::UniformClass::UNIFORM;
-      item.type         = data.type;
-      item.numElements  = numElements;
-
-      for(int i = 0; i < numElements; ++i)
+      iter = data.name.find("]");
+      std::string suffix;
+      if(iter != std::string::npos && iter + 1 != data.name.length())
       {
-        std::stringstream elementNameStream;
-        elementNameStream << baseName << "[" << i << "]";
+        suffix = data.name.substr(iter + 1); // If there is a suffix, it means it is an element of an array of struct
+      }
 
-        item.locations.push_back(gl.GetUniformLocation(programId, elementNameStream.str().c_str()));
-        item.offsets.push_back(offset);
-        offset += GetSizeForType(data.type);
+      if(!suffix.empty())
+      {
+        // Write multiple items
+        for(int i = 0; i < numElements; ++i)
+        {
+          std::stringstream elementNameStream;
+          elementNameStream << baseName << "[" << i << "]" << suffix;
+          mDefaultUniformBlock.members.emplace_back();
+          auto& item   = mDefaultUniformBlock.members.back();
+          item.name    = elementNameStream.str();
+          item.binding = 0;
+          item.offsets.push_back(offset);
+          item.locations.push_back(gl.GetUniformLocation(programId, elementNameStream.str().c_str()));
+          item.bufferIndex  = 0;
+          item.uniformClass = Graphics::UniformClass::UNIFORM;
+          item.type         = data.type;
+          offset += GetSizeForType(data.type);
+        }
+      }
+      else
+      {
+        // Write 1 item with multiple elements
+        mDefaultUniformBlock.members.emplace_back();
+        auto& item = mDefaultUniformBlock.members.back();
+
+        item.name         = baseName;
+        item.binding      = 0;
+        item.bufferIndex  = 0;
+        item.uniformClass = Graphics::UniformClass::UNIFORM;
+        item.type         = data.type;
+        item.numElements  = numElements;
+
+        for(int i = 0; i < numElements; ++i)
+        {
+          std::stringstream elementNameStream;
+          elementNameStream << baseName << "[" << i << "]";
+          item.locations.push_back(gl.GetUniformLocation(programId, elementNameStream.str().c_str()));
+          item.offsets.push_back(offset);
+          offset += GetSizeForType(data.type);
+        }
       }
     }
     else
     {
+      // Write 1 item with 1 element
+      mDefaultUniformBlock.members.emplace_back();
+      auto& item   = mDefaultUniformBlock.members.back();
       item.name    = data.name;
       item.binding = 0;
       item.offsets.push_back(offset);
index ab2548a..000bfa7 100755 (executable)
@@ -1455,6 +1455,11 @@ public:
     return mScaleFactor;
   }
 
+  Dali::Accessibility::Address GetAccessibilityAddress()
+  {
+    return {":9.99", "root"};
+  }
+
   Dali::PixelData GetScreenshot(Dali::Rect<int32_t> viewArea, float scaleFactor)
   {
     uint32_t bufferSize = viewArea.width * viewArea.height * 4 ;
@@ -2226,6 +2231,11 @@ void WebEngine::ActivateAccessibility(bool activated)
 {
 }
 
+Accessibility::Address WebEngine::GetAccessibilityAddress()
+{
+  return Internal::Adaptor::GetImplementation(*this).GetAccessibilityAddress();
+}
+
 bool WebEngine::HighlightText(const std::string& text, Dali::WebEnginePlugin::FindOption options, uint32_t maxMatchCount)
 {
   return true;
@@ -2471,4 +2481,3 @@ void WebEngine::GetPlainTextAsynchronously(Dali::WebEnginePlugin::PlainTextRecei
 }
 
 } // namespace Dali;
-
index 0d2efcc..1fffc97 100644 (file)
@@ -306,7 +306,9 @@ int UtcDaliAnimatedImageVisualGetPropertyMap04(void)
       .Add(ImageVisual::Property::URL, TEST_GIF_FILE_NAME)
       .Add(ImageVisual::Property::BATCH_SIZE, 1)
       .Add(ImageVisual::Property::CACHE_SIZE, 1)
-      .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false)
+      .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, true)
+      .Add(ImageVisual::Property::RELEASE_POLICY, ImageVisual::ReleasePolicy::DETACHED)
+      .Add(ImageVisual::Property::LOAD_POLICY, ImageVisual::LoadPolicy::ATTACHED)
       .Add(DevelVisual::Property::BORDERLINE_WIDTH, 0.4f));
 
   Property::Map resultMap;
@@ -329,6 +331,18 @@ int UtcDaliAnimatedImageVisualGetPropertyMap04(void)
   DALI_TEST_CHECK(value);
   DALI_TEST_CHECK(value->Get<int>() == 2);
 
+  value = resultMap.Find(ImageVisual::Property::SYNCHRONOUS_LOADING, Property::BOOLEAN);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_CHECK(value->Get<bool>() == true);
+
+  value = resultMap.Find(ImageVisual::Property::RELEASE_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_CHECK(value->Get<int>() == ImageVisual::ReleasePolicy::DETACHED);
+
+  value = resultMap.Find(ImageVisual::Property::LOAD_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(value);
+  DALI_TEST_CHECK(value->Get<int>() == ImageVisual::LoadPolicy::ATTACHED);
+
   value = resultMap.Find(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, "totalFrameNumber");
   DALI_TEST_CHECK(value);
   DALI_TEST_EQUALS(value->Get<int>(), 4, TEST_LOCATION);
@@ -427,7 +441,8 @@ int UtcDaliAnimatedImageVisualSynchronousLoading(void)
     application.SendNotification();
     application.Render(20);
 
-    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+    // The first frame is loaded synchronously and load next batch.
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
     application.SendNotification();
     application.Render();
@@ -440,12 +455,12 @@ int UtcDaliAnimatedImageVisualSynchronousLoading(void)
     application.SendNotification();
     application.Render(20);
 
-    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
     application.SendNotification();
     application.Render();
 
-    DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 3, TEST_LOCATION);
+    DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 2, TEST_LOCATION);
 
     dummyControl.Unparent();
   }
diff --git a/automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp b/automated-tests/src/dali-toolkit/utc-Dali-GlViewDirectRendering.cpp
new file mode 100644 (file)
index 0000000..82e3be9
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2022 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 <unistd.h>
+#include <thread>
+
+#include <dali-toolkit-test-suite-utils.h>
+
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+// Positive test case for a method
+int UtcDaliGlViewDirectRenderingNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliGlViewDirectRenderingNew");
+  GlView view = GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGBA8888);
+  DALI_TEST_CHECK(view);
+
+  auto mode1 = view.GetBackendMode();
+
+  DALI_TEST_EQUALS(mode1, GlView::BackendMode::DIRECT_RENDERING, TEST_LOCATION);
+
+  GlView view2 = GlView::New(GlView::BackendMode::EGL_IMAGE_OFFSCREEN_RENDERING, GlView::ColorFormat::RGBA8888);
+  DALI_TEST_CHECK(view2);
+
+  auto mode2 = view2.GetBackendMode();
+  DALI_TEST_EQUALS(mode2, GlView::BackendMode::EGL_IMAGE_OFFSCREEN_RENDERING, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingNewN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliGlViewDirectRenderingNewN");
+  // Invalid backend mode
+  GlView view = GlView::New(GlView::BackendMode(11111), GlView::ColorFormat::RGBA8888);
+  DALI_TEST_CHECK(!view);
+
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliGlViewDirectRenderingDownCast(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliGlViewDirectRenderingDownCast");
+
+  GlView     view = GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+  BaseHandle handle(view);
+
+  Toolkit::GlView view2 = Toolkit::GlView::DownCast(handle);
+  DALI_TEST_CHECK(view);
+  DALI_TEST_CHECK(view2);
+  DALI_TEST_CHECK(view == view2);
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingCopyAndAssignment(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingCopyAndAssignment");
+
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+  DALI_TEST_CHECK(view);
+
+  GlView copy(view);
+  DALI_TEST_CHECK(view == copy);
+
+  GlView assign;
+  DALI_TEST_CHECK(!assign);
+
+  assign = copy;
+  DALI_TEST_CHECK(assign == view);
+
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingMoveAssignment");
+
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+  DALI_TEST_EQUALS(1, view.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  GlView moved;
+  moved = std::move(view);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(!view);
+
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingSetGraphicsConfigGles20N(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingSetGraphicsConfigGles20");
+  GlView view;
+  try
+  {
+    view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+    DALI_TEST_CHECK(false);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingSetGraphicsConfigGles30(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingSetGraphicsConfigGles30");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  try
+  {
+    view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_3_0);
+    DALI_TEST_CHECK(true);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(false);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingRenderingMode(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingRenderingMode");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  view.SetRenderingMode(GlView::RenderingMode::ON_DEMAND);
+
+  GlView::RenderingMode mode = view.GetRenderingMode();
+
+  DALI_TEST_EQUALS(GlView::RenderingMode::ON_DEMAND, mode, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingOnSizeSet(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingOnSizeSet");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 size(200.0f, 300.0f, 0.0f);
+  view.SetProperty(Actor::Property::SIZE, size);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(view.GetCurrentProperty<Vector3>(Actor::Property::SIZE), size, TEST_LOCATION);
+
+  END_TEST;
+}
+
+namespace DirectRenderingCode
+{
+
+// Internal callback function
+void glInit(void)
+{
+}
+
+int glRenderFrame(void)
+{
+  static unsigned int retFlag = 0;
+  return retFlag++;
+}
+
+void glTerminate(void)
+{
+}
+
+void resizeCB(Vector2 size)
+{
+}
+
+}
+
+int UtcDaliGlViewDirectRenderingRegisterGlCallbacksN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingRegisterGlCallbacksN");
+  GlView view;
+
+  try
+  {
+    view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInit), Dali::MakeCallback(DirectRenderingCode::glRenderFrame), Dali::MakeCallback(DirectRenderingCode::glTerminate));
+    DALI_TEST_CHECK(false);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingSetResizeCallbackN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingSetResizeCallback");
+  GlView view;
+
+  try
+  {
+    view.SetResizeCallback(Dali::MakeCallback(DirectRenderingCode::resizeCB));
+    DALI_TEST_CHECK(false);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingRenderOnce(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingRenderOnce");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  try
+  {
+    view.RenderOnce();
+    DALI_TEST_CHECK(true);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(false);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingWindowVisibilityChanged(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingWindowVisibilityChanged");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+  application.GetScene().Add(view);
+  view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS);
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInit), Dali::MakeCallback(DirectRenderingCode::glRenderFrame), Dali::MakeCallback(DirectRenderingCode::glTerminate));
+  view.SetResizeCallback(Dali::MakeCallback(DirectRenderingCode::resizeCB));
+
+  application.SendNotification();
+  application.Render();
+
+  Window window = DevelWindow::Get(view);
+  window.Hide();
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingOnScene(void)
+{
+  ToolkitTestApplication application;
+
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  //Onscene
+  application.GetScene().Add(view);
+  view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS);
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInit), Dali::MakeCallback(DirectRenderingCode::glRenderFrame), Dali::MakeCallback(DirectRenderingCode::glTerminate));
+
+  application.SendNotification();
+  application.Render();
+
+  //Offscene
+  application.GetScene().Remove(view);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingControlVisibilityChanged(void)
+{
+  ToolkitTestApplication application;
+
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  view.SetProperty(Actor::Property::VISIBLE, false);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(view.GetCurrentProperty<bool>(Actor::Property::VISIBLE) == false);
+
+  view.SetProperty(Actor::Property::VISIBLE, true);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(view.GetCurrentProperty<bool>(Actor::Property::VISIBLE) == true);
+
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingResize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingResize");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  application.GetScene().Add(view);
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInit), Dali::MakeCallback(DirectRenderingCode::glRenderFrame), Dali::MakeCallback(DirectRenderingCode::glTerminate));
+  view.SetResizeCallback(Dali::MakeCallback(DirectRenderingCode::resizeCB));
+  view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  view.SetProperty(Actor::Property::SIZE, Vector2(360.0f, 360.0f));
+
+  application.SendNotification();
+  application.Render();
+
+  //To GlViewRenderThread can recognize Resize signal the main thread have to sleep.
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
+
+int UtcDaliGlViewDirectRenderingTerminateCallback(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewDirectRenderingTerminateCallback");
+  GlView view = Toolkit::GlView::New(GlView::BackendMode::DIRECT_RENDERING, GlView::ColorFormat::RGB888);
+
+  application.GetScene().Add(view);
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallbacks(Dali::MakeCallback(DirectRenderingCode::glInit), Dali::MakeCallback(DirectRenderingCode::glRenderFrame), Dali::MakeCallback(DirectRenderingCode::glTerminate));
+  view.SetResizeCallback(Dali::MakeCallback(DirectRenderingCode::resizeCB));
+  view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  view.SetProperty(Actor::Property::SIZE, Vector2(360.0f, 360.0f));
+
+  application.SendNotification();
+  application.Render();
+
+
+
+  //To GlViewRenderThread can recognize Resize signal the main thread have to sleep.
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
\ No newline at end of file
index 823c2c2..ab50286 100644 (file)
@@ -33,6 +33,7 @@
 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
 #include <dali-toolkit/devel-api/styling/style-manager-devel.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-base.h>
+#include <dali-toolkit/devel-api/visuals/animated-image-visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
@@ -76,7 +77,8 @@ static const char* gImage_600_RGB = TEST_RESOURCE_DIR "/test-image-600.jpg";
 // resolution: 50*50, frame count: 4, frame delay: 0.2 second for each frame
 const char* TEST_GIF_FILE_NAME = TEST_RESOURCE_DIR "/anim.gif";
 
-const char* TEST_VECTOR_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/insta_camera.json";
+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";
 
 void TestUrl(ImageView imageView, const std::string url)
 {
@@ -2359,7 +2361,7 @@ int UtcDaliImageViewFittingModesWithAnimatedVectorImageVisual(void)
   ImageView     imageView = ImageView::New();
   Property::Map imageMap;
   imageMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE);
-  imageMap.Add(Toolkit::ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME); // 249x169 image
+  imageMap.Add(Toolkit::ImageVisual::Property::URL, TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME); // 249x169 image
 
   imageView.SetProperty(Toolkit::ImageView::Property::IMAGE, imageMap);
   imageView.SetProperty(Actor::Property::SIZE, Vector2(600, 600));
@@ -3128,6 +3130,12 @@ void OnResourceReadySignal03(Control control)
   gResourceReadySignalCounter++;
 }
 
+void OnSimpleResourceReadySignal(Control control)
+{
+  // simply increate counter
+  gResourceReadySignalCounter++;
+}
+
 } // namespace
 
 int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
@@ -3228,3 +3236,235 @@ int UtcDaliImageViewSetImageOnResourceReadySignal03(void)
 
   END_TEST;
 }
+
+int UtcDaliImageViewOnResourceReadySignalWithBrokenAlphaMask01(void)
+{
+  tet_infoline("Test signal handler when image / mask image is broken.");
+
+  ToolkitTestApplication application;
+
+  auto TestResourceReadyUrl = [&application](int eventTriggerCount, bool isSynchronous, const std::string& url, const std::string& mask, const char* location) {
+    gResourceReadySignalCounter = 0;
+
+    Property::Map map;
+    map[Toolkit::ImageVisual::Property::URL] = url;
+    if(!mask.empty())
+    {
+      map[Toolkit::ImageVisual::Property::ALPHA_MASK_URL] = mask;
+    }
+    map[Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING] = isSynchronous;
+
+    ImageView imageView                            = ImageView::New();
+    imageView[Toolkit::ImageView::Property::IMAGE] = map;
+    imageView[Actor::Property::SIZE]               = Vector2(100.0f, 200.0f);
+    imageView.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+
+    application.GetScene().Add(imageView);
+    application.SendNotification();
+    application.Render();
+
+    if(!isSynchronous)
+    {
+      // Wait for loading
+      DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(eventTriggerCount), true, location);
+    }
+    tet_printf("test %s [sync:%d] signal fired\n", url.c_str(), isSynchronous ? 1 : 0);
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, location);
+
+    imageView.Unparent();
+  };
+
+  for(int synchronous = 0; synchronous <= 1; synchronous++)
+  {
+    tet_printf("Test normal case (sync:%d)\n", synchronous);
+    TestResourceReadyUrl(1, synchronous, gImage_600_RGB, "", TEST_LOCATION);
+    TestResourceReadyUrl(3, synchronous, gImage_600_RGB, gImage_34_RGBA, TEST_LOCATION); // 3 event trigger required : 2 image load + 1 apply mask
+
+    tet_printf("Test broken image case (sync:%d)\n", synchronous);
+    TestResourceReadyUrl(1, synchronous, "invalid.jpg", "", TEST_LOCATION);
+    TestResourceReadyUrl(2, synchronous, "invalid.jpg", gImage_34_RGBA, TEST_LOCATION);
+
+    tet_printf("Test broken mask image case (sync:%d)\n", synchronous);
+    TestResourceReadyUrl(2, synchronous, gImage_600_RGB, "invalid.png", TEST_LOCATION);
+
+    tet_printf("Test broken both image, mask image case (sync:%d)\n", synchronous);
+    TestResourceReadyUrl(2, synchronous, "invalid.jpg", "invalid.png", TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliImageViewOnResourceReadySignalWithBrokenAlphaMask02(void)
+{
+  tet_infoline("Test signal handler when image try to use cached-and-broken mask image.");
+
+  ToolkitTestApplication application;
+
+  gResourceReadySignalCounter = 0;
+
+  auto TestBrokenMaskResourceReadyUrl = [&application](const std::string& url, const char* location) {
+    Property::Map map;
+    map[Toolkit::ImageVisual::Property::URL] = url;
+    // Use invalid mask url
+    map[Toolkit::ImageVisual::Property::ALPHA_MASK_URL] = "invalid.png";
+
+    ImageView imageView                            = ImageView::New();
+    imageView[Toolkit::ImageView::Property::IMAGE] = map;
+    imageView[Actor::Property::SIZE]               = Vector2(100.0f, 200.0f);
+    imageView.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+
+    application.GetScene().Add(imageView);
+    application.SendNotification();
+    application.Render();
+
+    // Don't unparent imageView, for keep the cache.
+  };
+
+  // Use more than 4 images (The number of LocalImageLoadThread)
+  const std::vector<std::string> testUrlList = {gImage_34_RGBA, gImage_600_RGB, "invalid.jpg" /* invalid url */, TEST_IMAGE_1, TEST_IMAGE_2, TEST_BROKEN_IMAGE_DEFAULT};
+
+  int expectResourceReadySignalCounter = 0;
+
+  for(auto& url : testUrlList)
+  {
+    TestBrokenMaskResourceReadyUrl(url, TEST_LOCATION);
+    expectResourceReadySignalCounter++;
+  }
+
+  // Remain 1 signal due to we use #URL + 1 mask image.
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(expectResourceReadySignalCounter + 1), true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(gResourceReadySignalCounter, expectResourceReadySignalCounter, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageViewCheckVariousCaseSendOnResourceReadySignal(void)
+{
+  tet_infoline("Test signal handler various case.");
+
+  ToolkitTestApplication application;
+
+  auto TestResourceReadyUrl = [&application](int eventTriggerCount, bool isSynchronous, bool loadSuccess, const std::string& url, const std::string& mask, const char* location) {
+    gResourceReadySignalCounter = 0;
+
+    Property::Map map;
+    map[Toolkit::ImageVisual::Property::URL] = url;
+    if(!mask.empty())
+    {
+      map[Toolkit::ImageVisual::Property::ALPHA_MASK_URL] = mask;
+    }
+    map[Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING] = isSynchronous;
+
+    ImageView imageView                            = ImageView::New();
+    imageView[Toolkit::ImageView::Property::IMAGE] = map;
+    imageView[Actor::Property::SIZE]               = Vector2(100.0f, 200.0f);
+    imageView.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+
+    application.GetScene().Add(imageView);
+    application.SendNotification();
+    application.Render();
+
+    // Wait for loading
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(eventTriggerCount), true, location);
+
+    tet_printf("test %s [sync:%d] signal fired\n", url.c_str(), isSynchronous ? 1 : 0);
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, location);
+    DALI_TEST_EQUALS(imageView.GetVisualResourceStatus(Toolkit::ImageView::Property::IMAGE), loadSuccess ? Toolkit::Visual::ResourceStatus::READY : Toolkit::Visual::ResourceStatus::FAILED, TEST_LOCATION);
+
+    imageView.Unparent();
+  };
+
+  auto TestAuxiliaryResourceReadyUrl = [&application](bool isSynchronous, bool loadSuccess, const std::string& url, const std::string& auxiliaryUrl, const char* location) {
+    gResourceReadySignalCounter = 0;
+
+    Property::Map map;
+    map[Toolkit::ImageVisual::Property::URL]                        = url;
+    map[Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE]       = auxiliaryUrl;
+    map[Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA] = 0.5f;
+    map[Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING]        = isSynchronous;
+
+    ImageView imageView = ImageView::New();
+    imageView.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+    imageView.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 200.0f));
+    imageView.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+    application.GetScene().Add(imageView);
+
+    application.SendNotification();
+    application.Render();
+
+    if(!isSynchronous)
+    {
+      // Wait for loading
+      DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, location);
+    }
+
+    tet_printf("test %s [sync:%d] signal fired\n", url.c_str(), isSynchronous ? 1 : 0);
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, location);
+    DALI_TEST_EQUALS(imageView.GetVisualResourceStatus(Toolkit::ImageView::Property::IMAGE), loadSuccess ? Toolkit::Visual::ResourceStatus::READY : Toolkit::Visual::ResourceStatus::FAILED, TEST_LOCATION);
+
+    imageView.Unparent();
+  };
+
+  // Case 1 : asynchronous loading
+  tet_printf("Test invalid single simple image Asynchronous\n");
+
+  // Test normal case
+  TestResourceReadyUrl(1, 0, 1, gImage_600_RGB, "", TEST_LOCATION);
+  TestResourceReadyUrl(1, 0, 1, TEST_SVG_FILE_NAME, "", TEST_LOCATION); // 1 rasterize
+  TestResourceReadyUrl(1, 0, 1, TEST_BROKEN_IMAGE_L, "", TEST_LOCATION);
+
+  TestResourceReadyUrl(2, 0, 1, TEST_GIF_FILE_NAME, "", TEST_LOCATION);                   // 2 image loading - batch size
+  TestResourceReadyUrl(1, 0, 1, TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME, "", TEST_LOCATION); // 1 rasterize
+
+  TestResourceReadyUrl(3, 0, 1, gImage_600_RGB, gImage_34_RGBA, TEST_LOCATION); // 2 image loading + 1 applymask
+
+  TestAuxiliaryResourceReadyUrl(0, 1, TEST_BROKEN_IMAGE_L, gImage_34_RGBA, TEST_LOCATION);
+
+  // Test broken case
+  TestResourceReadyUrl(1, 0, 0, "invalid.jpg", "", TEST_LOCATION);
+  TestResourceReadyUrl(0, 0, 0, "invalid.svg", "", TEST_LOCATION); // 0 rasterize
+  TestResourceReadyUrl(1, 0, 0, "invalid.9.png", "", TEST_LOCATION);
+  TestResourceReadyUrl(1, 0, 0, "invalid.gif", "", TEST_LOCATION);  // 1 image loading
+  TestResourceReadyUrl(0, 0, 0, "invalid.json", "", TEST_LOCATION); // 0 rasterize
+
+  TestResourceReadyUrl(2, 0, 0, "invalid.jpg", "invalid.png", TEST_LOCATION);  // 2 image loading
+  TestResourceReadyUrl(2, 0, 1, gImage_600_RGB, "invalid.png", TEST_LOCATION); // 2 image loading
+  TestResourceReadyUrl(2, 0, 0, "invalid.jpg", gImage_34_RGBA, TEST_LOCATION); // 2 image loading
+
+  TestAuxiliaryResourceReadyUrl(0, 0, "invalid.9.png", "invalid.png", TEST_LOCATION);
+  TestAuxiliaryResourceReadyUrl(0, 1, TEST_BROKEN_IMAGE_L, "invalid.png", TEST_LOCATION);
+  TestAuxiliaryResourceReadyUrl(0, 0, "invalid.9.png", gImage_34_RGBA, TEST_LOCATION);
+
+  // Case 2 : Synchronous loading
+  tet_printf("Test invalid single simple image Synchronous\n");
+
+  // Test normal case
+  TestResourceReadyUrl(0, 1, 1, gImage_600_RGB, "", TEST_LOCATION);
+  TestResourceReadyUrl(0, 1, 1, TEST_SVG_FILE_NAME, "", TEST_LOCATION); // synchronous rasterize
+  TestResourceReadyUrl(0, 1, 1, TEST_BROKEN_IMAGE_L, "", TEST_LOCATION);
+
+  TestResourceReadyUrl(1, 1, 1, TEST_GIF_FILE_NAME, "", TEST_LOCATION);                   // first frame image loading sync + second frame image loading async
+  TestResourceReadyUrl(0, 1, 1, TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME, "", TEST_LOCATION); // synchronous rasterize
+
+  TestResourceReadyUrl(0, 1, 1, gImage_600_RGB, gImage_34_RGBA, TEST_LOCATION);
+
+  TestAuxiliaryResourceReadyUrl(1, 1, TEST_BROKEN_IMAGE_L, gImage_34_RGBA, TEST_LOCATION);
+
+  // Test broken case
+  TestResourceReadyUrl(0, 1, 0, "invalid.jpg", "", TEST_LOCATION);
+  TestResourceReadyUrl(0, 1, 0, "invalid.svg", "", TEST_LOCATION); // 0 rasterize
+  TestResourceReadyUrl(0, 1, 0, "invalid.9.png", "", TEST_LOCATION);
+  TestResourceReadyUrl(0, 1, 0, "invalid.gif", "", TEST_LOCATION);
+  TestResourceReadyUrl(0, 1, 0, "invalid.json", "", TEST_LOCATION); // 0 rasterize
+
+  TestResourceReadyUrl(0, 1, 0, "invalid.jpg", "invalid.png", TEST_LOCATION);
+  TestResourceReadyUrl(0, 1, 1, gImage_600_RGB, "invalid.png", TEST_LOCATION);
+  TestResourceReadyUrl(0, 1, 0, "invalid.jpg", gImage_34_RGBA, TEST_LOCATION);
+
+  TestAuxiliaryResourceReadyUrl(1, 0, "invalid.9.png", "invalid.png", TEST_LOCATION);
+  TestAuxiliaryResourceReadyUrl(1, 1, TEST_BROKEN_IMAGE_L, "invalid.png", TEST_LOCATION);
+  TestAuxiliaryResourceReadyUrl(1, 0, "invalid.9.png", gImage_34_RGBA, TEST_LOCATION);
+
+  END_TEST;
+}
index 7b08e1a..1ca7bd4 100644 (file)
@@ -1936,8 +1936,8 @@ int UtcDaliKeyboardFocusManagerSetAndGetCurrentFocusActorInTouchMode(void)
   application.SendNotification();
   application.Render();
 
-  // Check that the focus is successfully to clear
-  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor());
+  // Since no focus has been moved, the current focus actor is the same.
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
 
   // Make the second actor focusableInTouchMode
   second.SetProperty(DevelActor::Property::TOUCH_FOCUSABLE, true);
@@ -2210,3 +2210,46 @@ int UtcDaliKeyboardFocusManagerChangeFocusDirectionByCustomWheelEvent(void)
 
   END_TEST;
 }
+
+int UtcDaliKeyboardFocusManagerWithUserInteractionEnabled(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" UtcDaliKeyboardFocusManagerWithUserInteractionEnabled");
+
+  KeyboardFocusManager manager = KeyboardFocusManager::Get();
+  DALI_TEST_CHECK(manager);
+
+  // 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);
+
+  // Create the second actor and add it to the first actor.
+  Actor second = Actor::New();
+  second.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
+  first.Add(second);
+
+  // Check that no actor is being focused yet.
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor());
+
+  // Check that the focus is set on the first actor
+  DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
+
+  // Set USER_INTERACTION_ENABLED false.
+  second.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, false);
+
+  // Check that it will fail to set focus on the second actor as it's not userInteractionEnabled
+  DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == false);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first);
+
+  // Set KeyboardFocusableChildren true.
+  second.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, true);
+
+  // Check that the focus is set on the second actor
+  DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true);
+  DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second);
+
+  END_TEST;
+}
\ No newline at end of file
index 19c3583..476da78 100644 (file)
@@ -20,6 +20,8 @@
 #include <dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+
+#include <dali/devel-api/actors/actor-devel.h>
 #include <dali/devel-api/adaptor-framework/clipboard.h>
 #include <dali/devel-api/adaptor-framework/key-devel.h>
 #include <dali/devel-api/text-abstraction/font-client.h>
@@ -689,6 +691,14 @@ int UtcDaliTextEditorSetPropertyP(void)
   editor.SetProperty(TextEditor::Property::HORIZONTAL_ALIGNMENT, "END");
   DALI_TEST_EQUALS(editor.GetProperty<std::string>(TextEditor::Property::HORIZONTAL_ALIGNMENT), "END", TEST_LOCATION);
 
+  // Check that the Alignment properties can be correctly set
+  editor.SetProperty(DevelTextEditor::Property::VERTICAL_ALIGNMENT, "BOTTOM");
+  DALI_TEST_EQUALS(editor.GetProperty<std::string>(DevelTextEditor::Property::VERTICAL_ALIGNMENT), "BOTTOM", TEST_LOCATION);
+  editor.SetProperty(DevelTextEditor::Property::VERTICAL_ALIGNMENT, "CENTER");
+  DALI_TEST_EQUALS(editor.GetProperty<std::string>(DevelTextEditor::Property::VERTICAL_ALIGNMENT), "CENTER", TEST_LOCATION);
+  editor.SetProperty(DevelTextEditor::Property::VERTICAL_ALIGNMENT, "TOP");
+  DALI_TEST_EQUALS(editor.GetProperty<std::string>(DevelTextEditor::Property::VERTICAL_ALIGNMENT), "TOP", TEST_LOCATION);
+
   // Check scroll properties.
   editor.SetProperty(TextEditor::Property::SCROLL_THRESHOLD, 1.f);
   DALI_TEST_EQUALS(editor.GetProperty<float>(TextEditor::Property::SCROLL_THRESHOLD), 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
@@ -3630,6 +3640,9 @@ int UtcDaliTextEditorEnableEditing(void)
   application.SendNotification();
   application.Render();
 
+  textEditor.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, true);
+  DALI_TEST_EQUALS(textEditor.GetProperty(DevelActor::Property::USER_INTERACTION_ENABLED).Get<bool>(), true, TEST_LOCATION);
+
   textEditor.SetKeyInputFocus();
   textEditor.SetProperty(DevelTextEditor::Property::ENABLE_EDITING, false);
   application.ProcessEvent(GenerateKey("D", "", "D", KEY_D_CODE, 0, 0, Integration::KeyEvent::DOWN, "D", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
@@ -3652,6 +3665,24 @@ int UtcDaliTextEditorEnableEditing(void)
   DALI_TEST_EQUALS(textEditor.GetProperty(TextEditor::Property::TEXT).Get<std::string>(), "D", TEST_LOCATION);
   DALI_TEST_EQUALS(textEditor.GetProperty(DevelTextEditor::Property::ENABLE_EDITING).Get<bool>(), true, TEST_LOCATION);
 
+  // Check the user interaction enabled and for coverage
+  DevelTextEditor::SelectWholeText(textEditor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  textEditor.SetKeyInputFocus();
+  textEditor.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, false);
+  application.ProcessEvent(GenerateKey("D", "", "D", KEY_D_CODE, 0, 0, Integration::KeyEvent::DOWN, "D", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(textEditor.GetProperty(TextEditor::Property::TEXT).Get<std::string>(), "D", TEST_LOCATION);
+  DALI_TEST_EQUALS(textEditor.GetProperty(DevelActor::Property::USER_INTERACTION_ENABLED).Get<bool>(), false, TEST_LOCATION);
+
   END_TEST;
 }
 
@@ -5765,6 +5796,55 @@ int UtcDaliToolkitTextEditorUnderlineTypesGeneration3(void)
   END_TEST;
 }
 
+int UtcDaliToolkitTextEditorMarkupRelativeLineHeight(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextEditorMarkupRelativeLineHeight");
+
+  TextEditor editor = TextEditor::New();
+  editor.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  editor.SetProperty(TextEditor::Property::POINT_SIZE, 10);
+  editor.SetProperty(TextEditor::Property::TEXT, "line 1\nline 2\nline 3\nline 4\nline 5");
+  editor.SetProperty(DevelTextEditor::Property::RELATIVE_LINE_SIZE, 1.0f);
+  editor.SetProperty(DevelTextEditor::Property::ELLIPSIS, false);
+  editor.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+
+  TextEditor editorSingleLineParagraph = TextEditor::New();
+  editorSingleLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  editorSingleLineParagraph.SetProperty(TextEditor::Property::POINT_SIZE, 10);
+  editorSingleLineParagraph.SetProperty(TextEditor::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line 2</p>line 3<p rel-line-height=3>line 4</p>line 5");
+  editorSingleLineParagraph.SetProperty(DevelTextEditor::Property::RELATIVE_LINE_SIZE, 1.0f);
+  editorSingleLineParagraph.SetProperty(DevelTextEditor::Property::ELLIPSIS, false);
+  editorSingleLineParagraph.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+
+  TextEditor editorMultiLineParagraph = TextEditor::New();
+  editorMultiLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  editorMultiLineParagraph.SetProperty(TextEditor::Property::POINT_SIZE, 10);
+  editorMultiLineParagraph.SetProperty(TextEditor::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line\n2</p>line 3<p rel-line-height=3>line\n4</p>line 5");
+  editorMultiLineParagraph.SetProperty(DevelTextEditor::Property::RELATIVE_LINE_SIZE, 1.0f);
+  editorMultiLineParagraph.SetProperty(DevelTextEditor::Property::ELLIPSIS, false);
+  editorMultiLineParagraph.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+
+  application.GetScene().Add(editor);
+  application.GetScene().Add(editorSingleLineParagraph);
+  application.GetScene().Add(editorMultiLineParagraph);
+  application.SendNotification();
+  application.Render();
+
+  Vector3 naturalSize               = editor.GetNaturalSize();
+  Vector3 relativeSingleNaturalSize = editorSingleLineParagraph.GetNaturalSize();
+  Vector3 relativeMultiNaturalSize  = editorMultiLineParagraph.GetNaturalSize();
+
+  float lineSize = naturalSize.y / 5.0f; //total size/number of lines
+
+  //no effect of relative line size for paragraph with single line
+  DALI_TEST_EQUALS(naturalSize.y, relativeSingleNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(lineSize*8.5f, relativeMultiNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextEditorRelativeLineHeight(void)
 {
   ToolkitTestApplication application;
@@ -5857,6 +5937,50 @@ int UtcDaliTextEditorTextSizeNegativeLineSpacing(void)
   END_TEST;
 }
 
+int UtcDaliTextEditorLineSpacingKeyArrowDown(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliTextEditorLineSpacingKeyArrowDown");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK(editor);
+
+  application.GetScene().Add(editor);
+
+  std::string cutText    = "";
+  std::string copiedText = "";
+
+  editor.SetProperty(TextEditor::Property::TEXT, "l1\nl2\nl3\n4");
+  editor.SetProperty(TextEditor::Property::POINT_SIZE, 10.f);
+  editor.SetProperty(Actor::Property::SIZE, Vector2(300.f, 200.f));
+  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(TextEditor::Property::LINE_SPACING, -15);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Tap on the text editor
+  TestGenerateTap(application, 1.0f, 1.0f);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Move to second line of the text.
+  application.ProcessEvent(GenerateKey("", "", "", DALI_KEY_CURSOR_DOWN, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(editor.GetProperty(DevelTextEditor::Property::PRIMARY_CURSOR_POSITION).Get<int>(), 3, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliTextEditorNegativeLineSpacingWithEllipsis(void)
 {
   ToolkitTestApplication application;
index 48c2bcc..b86f96b 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/integration-api/events/key-event-integ.h>
 #include <dali/integration-api/events/touch-event-integ.h>
 #include <dali/public-api/rendering/renderer.h>
+#include <dali/devel-api/actors/actor-devel.h>
 
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/dali-toolkit.h>
@@ -3836,6 +3837,9 @@ int UtcDaliTextFieldEnableEditing(void)
   application.SendNotification();
   application.Render();
 
+  textField.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, true);
+  DALI_TEST_EQUALS(textField.GetProperty(DevelActor::Property::USER_INTERACTION_ENABLED).Get<bool>(), true, TEST_LOCATION);
+
   textField.SetKeyInputFocus();
   textField.SetProperty(DevelTextField::Property::ENABLE_EDITING, false);
   application.ProcessEvent(GenerateKey("D", "", "D", KEY_D_CODE, 0, 0, Integration::KeyEvent::DOWN, "D", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
@@ -3858,6 +3862,24 @@ int UtcDaliTextFieldEnableEditing(void)
   DALI_TEST_EQUALS(textField.GetProperty(TextField::Property::TEXT).Get<std::string>(), "D", TEST_LOCATION);
   DALI_TEST_EQUALS(textField.GetProperty(DevelTextField::Property::ENABLE_EDITING).Get<bool>(), true, TEST_LOCATION);
 
+  // Check the user interaction enabled and for coverage
+  DevelTextField::SelectWholeText(textField);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  textField.SetKeyInputFocus();
+  textField.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, false);
+  application.ProcessEvent(GenerateKey("D", "", "D", KEY_D_CODE, 0, 0, Integration::KeyEvent::DOWN, "D", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE));
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(textField.GetProperty(TextField::Property::TEXT).Get<std::string>(), "D", TEST_LOCATION);
+  DALI_TEST_EQUALS(textField.GetProperty(DevelActor::Property::USER_INTERACTION_ENABLED).Get<bool>(), false, TEST_LOCATION);
+
   END_TEST;
 }
 
index bbd3d77..30aaade 100644 (file)
@@ -2632,6 +2632,58 @@ int UtcDaliToolkitTextLabelStrikethroughGeneration(void)
   END_TEST;
 }
 
+int UtcDaliToolkitTextLabelMarkupRelativeLineHeight(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelMarkupRelativeLineHeight");
+
+  TextLabel label = TextLabel::New();
+  label.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(TextLabel::Property::TEXT, "line 1\nline 2\nline 3\nline 4\nline 5");
+  label.SetProperty(DevelTextLabel::Property::RELATIVE_LINE_SIZE, 1.0f);
+  label.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+
+  TextLabel labelSingleLineParagraph = TextLabel::New();
+  labelSingleLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::POINT_SIZE, 10);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line 2</p>line 3<p rel-line-height=3>line 4</p>line 5");
+  labelSingleLineParagraph.SetProperty(DevelTextLabel::Property::RELATIVE_LINE_SIZE, 1.0f);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+
+  TextLabel labelMultiLineParagraph = TextLabel::New();
+  labelMultiLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::POINT_SIZE, 10);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line\n2</p>line 3<p rel-line-height=3>line\n4</p>line 5");
+  labelMultiLineParagraph.SetProperty(DevelTextLabel::Property::RELATIVE_LINE_SIZE, 1.0f);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+
+  application.GetScene().Add(label);
+  application.GetScene().Add(labelSingleLineParagraph);
+  application.GetScene().Add(labelMultiLineParagraph);
+  application.SendNotification();
+  application.Render();
+
+  Vector3 naturalSize               = label.GetNaturalSize();
+  Vector3 relativeSingleNaturalSize = labelSingleLineParagraph.GetNaturalSize();
+  Vector3 relativeMultiNaturalSize  = labelMultiLineParagraph.GetNaturalSize();
+
+  float lineSize = naturalSize.y / 5.0f; //total size/number of lines
+
+  //no effect of relative line size for paragraph with single line
+  DALI_TEST_EQUALS(naturalSize.y, relativeSingleNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(lineSize*8.5f, relativeMultiNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextLabelRelativeLineHeight(void)
 {
   ToolkitTestApplication application;
index f8ee78d..c256b7a 100644 (file)
@@ -238,6 +238,7 @@ int UtcDaliToolkitTextSelectionPopupIconProperties(void)
   popup.SetProperty(TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE, "POPUP_PASTE_BUTTON_ICON_IMAGE");
   popup.SetProperty(TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE, "POPUP_SELECT_BUTTON_ICON_IMAGE");
   popup.SetProperty(TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE, "POPUP_SELECT_ALL_BUTTON_ICON_IMAGE");
+  popup.SetProperty(TextSelectionPopup::Property::POPUP_PRESSED_IMAGE, "POPUP_PRESSED_IMAGE");
 
   DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE).Get<std::string>(), "POPUP_CLIPBOARD_BUTTON_ICON_IMAGE", TEST_LOCATION);
   DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE).Get<std::string>(), "POPUP_CUT_BUTTON_ICON_IMAGE", TEST_LOCATION);
@@ -245,6 +246,7 @@ int UtcDaliToolkitTextSelectionPopupIconProperties(void)
   DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE).Get<std::string>(), "POPUP_PASTE_BUTTON_ICON_IMAGE", TEST_LOCATION);
   DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE).Get<std::string>(), "POPUP_SELECT_BUTTON_ICON_IMAGE", TEST_LOCATION);
   DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE).Get<std::string>(), "POPUP_SELECT_ALL_BUTTON_ICON_IMAGE", TEST_LOCATION);
+  DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_PRESSED_IMAGE).Get<std::string>(), "POPUP_PRESSED_IMAGE", TEST_LOCATION);
 
   END_TEST;
 }
@@ -273,3 +275,35 @@ int UtcDaliToolkitTextSelectionPopupSizeProperties(void)
 
   END_TEST;
 }
+
+int UtcDaliToolkitTextSelectionPopupDurationProperties(void)
+{
+  ToolkitTestApplication application;
+  TextSelectionPopup     popup = TextSelectionPopup::New(nullptr);
+
+  const float popupFadeInDuration = 5.0f;
+  const float popupFadeOutDuration = 10.0f;
+  popup.SetProperty(TextSelectionPopup::Property::POPUP_FADE_IN_DURATION, popupFadeInDuration);
+  popup.SetProperty(TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION, popupFadeOutDuration);
+
+  DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_FADE_IN_DURATION).Get<float>(), popupFadeInDuration, TEST_LOCATION);
+  DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION).Get<float>(), popupFadeOutDuration, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliToolkitTextSelectionPopupColorProperties(void)
+{
+  ToolkitTestApplication application;
+  TextSelectionPopup     popup = TextSelectionPopup::New(nullptr);
+
+  popup.SetProperty(TextSelectionPopup::Property::POPUP_DIVIDER_COLOR, Color::RED);
+  popup.SetProperty(TextSelectionPopup::Property::POPUP_ICON_COLOR, Color::BLUE);
+  popup.SetProperty(TextSelectionPopup::Property::POPUP_PRESSED_COLOR, Color::BLACK);
+
+  DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_DIVIDER_COLOR).Get<Vector4>(), Color::RED, TEST_LOCATION);
+  DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_ICON_COLOR).Get<Vector4>(), Color::BLUE, TEST_LOCATION);
+  DALI_TEST_EQUALS(popup.GetProperty(TextSelectionPopup::Property::POPUP_PRESSED_COLOR).Get<Vector4>(), Color::BLACK, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index 8b0adff..6291e50 100644 (file)
@@ -110,6 +110,13 @@ void TestVisualAsynchronousRender(ToolkitTestApplication& application,
   DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
 }
 
+int gResourceReadySignalCounter;
+
+void OnResourceReadySignal(Control control)
+{
+  gResourceReadySignalCounter++;
+}
+
 } // namespace
 
 void dali_visual_factory_startup(void)
@@ -729,7 +736,7 @@ int UtcDaliVisualFactoryGetNPatchVisual2(void)
   propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
   propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(2, 2, 2, 2));
   {
-    tet_infoline("whole grid");
+    tet_infoline("whole grid (2,2,2,2) async");
     Visual::Base visual = factory.CreateVisual(propertyMap);
     DALI_TEST_CHECK(visual);
 
@@ -770,9 +777,80 @@ int UtcDaliVisualFactoryGetNPatchVisual2(void)
   propertyMap.Clear();
   propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
   propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
+  propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(2, 2, 2, 2));
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+  {
+    tet_infoline("whole grid (2,2,2,2) sync");
+    Visual::Base visual = factory.CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    TestGlAbstraction& gl           = application.GetGlAbstraction();
+    TraceCallStack&    textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+    TestVisualRender(application, actor, visual);
+
+    DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+
+    Vector2 naturalSize(0.0f, 0.0f);
+    visual.GetNaturalSize(naturalSize);
+    DALI_TEST_EQUALS(naturalSize, Vector2(imageSize.GetWidth(), imageSize.GetHeight()), TEST_LOCATION);
+  }
+
+  propertyMap.Clear();
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
   propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(1, 1, 1, 1));
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
   {
-    tet_infoline("whole grid");
+    tet_infoline("whole grid (1,1,1,1) sync : for testing same image, different border");
+    Visual::Base visual = factory.CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    TestGlAbstraction& gl           = application.GetGlAbstraction();
+    TraceCallStack&    textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+    TestVisualRender(application, actor, visual);
+
+    DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+
+    Vector2 naturalSize(0.0f, 0.0f);
+    visual.GetNaturalSize(naturalSize);
+    DALI_TEST_EQUALS(naturalSize, Vector2(imageSize.GetWidth(), imageSize.GetHeight()), TEST_LOCATION);
+  }
+
+  propertyMap.Clear();
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
+  propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(1, 1, 1, 1));
+  {
+    tet_infoline("whole grid (1,1,1,1) async");
+    Visual::Base visual = factory.CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    TestGlAbstraction& gl           = application.GetGlAbstraction();
+    TraceCallStack&    textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+    TestVisualRender(application, actor, visual);
+
+    DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+
+    Vector2 naturalSize(0.0f, 0.0f);
+    visual.GetNaturalSize(naturalSize);
+    DALI_TEST_EQUALS(naturalSize, Vector2(imageSize.GetWidth(), imageSize.GetHeight()), TEST_LOCATION);
+  }
+
+  propertyMap.Clear();
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
+  propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(3, 3, 3, 3));
+  {
+    tet_infoline("whole grid (3,3,3,3) async");
     Visual::Base visual = factory.CreateVisual(propertyMap);
     DALI_TEST_CHECK(visual);
 
@@ -1103,6 +1181,169 @@ int UtcDaliVisualFactoryGetNPatchVisual8(void)
   END_TEST;
 }
 
+int UtcDaliVisualFactoryGetNPatchVisual9(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliVisualFactoryGetNPatchVisual9: Request n-patch visual sync during another n-patch visual load image asynchronously");
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_NPATCH_FILE_NAME);
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl actor = DummyControl::New(true);
+
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+  actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  propertyMap.Clear();
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_NPATCH_FILE_NAME);
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+  Visual::Base visual2 = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual2);
+
+  DummyControl actor2 = DummyControl::New(true);
+
+  DummyControlImpl& dummyImpl2 = static_cast<DummyControlImpl&>(actor2.GetImplementation());
+  dummyImpl2.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual2);
+
+  actor2.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+  DALI_TEST_EQUALS(actor2.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor2);
+
+  application.SendNotification();
+  application.Render();
+
+  application.SendNotification();
+  application.Render();
+
+  // Async loading is not finished yet.
+  {
+    DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+  }
+  // Sync loading is finished.
+  {
+    Renderer renderer = actor2.GetRendererAt(0);
+    auto     textures = renderer.GetTextures();
+
+    DALI_TEST_EQUALS(textures.GetTextureCount(), 1, TEST_LOCATION);
+  }
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+  // Async loading is finished.
+  {
+    Renderer renderer = actor.GetRendererAt(0);
+    auto     textures = renderer.GetTextures();
+
+    DALI_TEST_EQUALS(textures.GetTextureCount(), 1, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliVisualFactoryGetNPatchVisual10(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliVisualFactoryGetNPatchVisual10: Request same 9-patch visual with a different border");
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  // Get actual size of test image
+  ImageDimensions imageSize = Dali::GetClosestImageSize(gImage_34_RGBA);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
+  propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(2, 2, 2, 2));
+  {
+    tet_infoline("whole grid (2,2,2,2) async");
+    Visual::Base visual = factory.CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    TestGlAbstraction& gl           = application.GetGlAbstraction();
+    TraceCallStack&    textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+    TestVisualAsynchronousRender(application, actor, visual);
+
+    DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+
+    Vector2 naturalSize(0.0f, 0.0f);
+    visual.GetNaturalSize(naturalSize);
+    DALI_TEST_EQUALS(naturalSize, Vector2(imageSize.GetWidth(), imageSize.GetHeight()), TEST_LOCATION);
+  }
+
+  propertyMap.Clear();
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
+  propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(1, 1, 1, 1));
+  {
+    tet_infoline("whole grid (1,1,1,1) async. Check whether we use cached texture");
+    // We don't use dummyControl here
+
+    const int expectResourceReadySignalCounter = 10;
+    gResourceReadySignalCounter                = 0;
+
+    for(int i = 0; i < expectResourceReadySignalCounter; i++)
+    {
+      ImageView imageView                            = ImageView::New();
+      imageView[Toolkit::ImageView::Property::IMAGE] = propertyMap;
+      imageView.ResourceReadySignal().Connect(&OnResourceReadySignal);
+      application.GetScene().Add(imageView);
+    }
+
+    // Dont wait for loading. All border use cached texture.
+
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, expectResourceReadySignalCounter, TEST_LOCATION);
+  }
+
+  propertyMap.Clear();
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::N_PATCH);
+  propertyMap.Insert(ImageVisual::Property::URL, gImage_34_RGBA);
+  propertyMap.Insert(ImageVisual::Property::BORDER, Rect<int>(1, 2, 1, 2));
+  {
+    tet_infoline("whole grid (1,2,1,2) async. Check whether we use cached texture");
+    // We don't use dummyControl here
+
+    const int expectResourceReadySignalCounter = 10;
+    gResourceReadySignalCounter                = 0;
+
+    for(int i = 0; i < expectResourceReadySignalCounter; i++)
+    {
+      ImageView imageView                            = ImageView::New();
+      imageView[Toolkit::ImageView::Property::IMAGE] = propertyMap;
+      imageView.ResourceReadySignal().Connect(&OnResourceReadySignal);
+      application.GetScene().Add(imageView);
+    }
+
+    // Dont wait for loading. All border use cached texture.
+
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, expectResourceReadySignalCounter, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
 int UtcDaliNPatchVisualAuxiliaryImage01(void)
 {
   ToolkitTestApplication application;
index 1ec67e2..f5bd6b9 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef DALI_SCENE_LOADER_GLTF2_ASSET_H_
 #define DALI_SCENE_LOADER_GLTF2_ASSET_H_
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -93,6 +93,7 @@ private:
 
 struct Asset
 {
+  std::string_view mGenerator;
   std::string_view mVersion;
 };
 
index 18923cf..923d0e4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -43,6 +43,11 @@ const std::string ORIENTATION_PROPERTY("orientation");
 const std::string SCALE_PROPERTY("scale");
 const std::string BLEND_SHAPE_WEIGHTS_UNIFORM("uBlendShapeWeight");
 
+const std::string MRENDERER_MODEL_IDENTIFICATION("M-Renderer");
+
+const std::string ROOT_NODE_NAME("RootNode");
+const Vector3     SCALE_TO_ADJUST(100.0f, 100.0f, 100.0f);
+
 const Geometry::Type GLTF2_TO_DALI_PRIMITIVES[]{
   Geometry::POINTS,
   Geometry::LINES,
@@ -692,7 +697,7 @@ void ConvertCamera(const gt::Camera& camera, CameraParameters& camParams)
   }
 }
 
-void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, ConversionContext& cctx)
+void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, ConversionContext& cctx, bool isMRendererModel)
 {
   auto& output    = cctx.mOutput;
   auto& scene     = output.mScene;
@@ -715,6 +720,11 @@ void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, Con
       nodeDef->mPosition    = node.mTranslation;
       nodeDef->mOrientation = node.mRotation;
       nodeDef->mScale       = node.mScale;
+
+      if(isMRendererModel && node.mName == ROOT_NODE_NAME && node.mScale == SCALE_TO_ADJUST)
+      {
+        nodeDef->mScale *= 0.01f;
+      }
     }
 
     return nodeDef;
@@ -774,11 +784,11 @@ void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, Con
 
   for(auto& n : node.mChildren)
   {
-    ConvertNode(*n, n.GetIndex(), idx, cctx);
+    ConvertNode(*n, n.GetIndex(), idx, cctx, isMRendererModel);
   }
 }
 
-void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx)
+void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx, bool isMRendererModel)
 {
   auto& outScene = cctx.mOutput.mScene;
   Index rootIdx  = outScene.GetNodeCount();
@@ -788,7 +798,7 @@ void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx)
       break;
 
     case 1:
-      ConvertNode(*scene.mNodes[0], scene.mNodes[0].GetIndex(), INVALID_INDEX, cctx);
+      ConvertNode(*scene.mNodes[0], scene.mNodes[0].GetIndex(), INVALID_INDEX, cctx, isMRendererModel);
       outScene.AddRootNode(rootIdx);
       break;
 
@@ -802,25 +812,25 @@ void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx)
 
       for(auto& n : scene.mNodes)
       {
-        ConvertNode(*n, n.GetIndex(), rootIdx, cctx);
+        ConvertNode(*n, n.GetIndex(), rootIdx, cctx, isMRendererModel);
       }
       break;
     }
   }
 }
 
-void ConvertNodes(const gt::Document& doc, ConversionContext& cctx)
+void ConvertNodes(const gt::Document& doc, ConversionContext& cctx, bool isMRendererModel)
 {
-  ConvertSceneNodes(*doc.mScene, cctx);
+  ConvertSceneNodes(*doc.mScene, cctx, isMRendererModel);
 
   for(uint32_t i = 0, i1 = doc.mScene.GetIndex(); i < i1; ++i)
   {
-    ConvertSceneNodes(doc.mScenes[i], cctx);
+    ConvertSceneNodes(doc.mScenes[i], cctx, isMRendererModel);
   }
 
   for(uint32_t i = doc.mScene.GetIndex() + 1; i < doc.mScenes.size(); ++i)
   {
-    ConvertSceneNodes(doc.mScenes[i], cctx);
+    ConvertSceneNodes(doc.mScenes[i], cctx, isMRendererModel);
   }
 }
 
@@ -1155,10 +1165,23 @@ void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactor
 
   gt::Document doc;
 
-  auto& rootObj        = js::Cast<json_object_s>(*root);
-  auto  jsAsset        = js::FindObjectChild("asset", rootObj);
-  auto  jsAssetVersion = js::FindObjectChild("version", js::Cast<json_object_s>(*jsAsset));
-  doc.mAsset.mVersion  = js::Read::StringView(*jsAssetVersion);
+  auto& rootObj = js::Cast<json_object_s>(*root);
+  auto  jsAsset = js::FindObjectChild("asset", rootObj);
+
+  auto jsAssetVersion = js::FindObjectChild("version", js::Cast<json_object_s>(*jsAsset));
+  if(jsAssetVersion)
+  {
+    doc.mAsset.mVersion = js::Read::StringView(*jsAssetVersion);
+  }
+
+  bool isMRendererModel(false);
+  auto jsAssetGenerator = js::FindObjectChild("generator", js::Cast<json_object_s>(*jsAsset));
+  if(jsAssetGenerator)
+  {
+    doc.mAsset.mGenerator = js::Read::StringView(*jsAssetGenerator);
+    isMRendererModel      = (doc.mAsset.mGenerator.find(MRENDERER_MODEL_IDENTIFICATION) != std::string_view::npos);
+  }
+
 
   gt::SetRefReaderObject(doc);
   DOCUMENT_READER.Read(rootObj, doc);
@@ -1168,7 +1191,7 @@ void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactor
 
   ConvertMaterials(doc, cctx);
   ConvertMeshes(doc, cctx);
-  ConvertNodes(doc, cctx);
+  ConvertNodes(doc, cctx, isMRendererModel);
   ConvertAnimations(doc, cctx);
 
   ProcessSkins(doc, cctx);
index b990dc7..4645576 100644 (file)
@@ -310,6 +310,14 @@ enum Type
    * @note If the value is less than 1, the lines could to be overlapped.
    */
   RELATIVE_LINE_SIZE,
+
+  /**
+   * @brief The line vertical alignment.
+   * @details Name "verticalAlignment", type Property::STRING or type VerticalAlignment::Type (Property::INTEGER).
+   *          Values "TOP", "CENTER", "BOTTOM", default TOP.
+   * @note Return type is Property::STRING
+   */
+  VERTICAL_ALIGNMENT,
 };
 
 } // namespace Property
index d338e39..2fdf285 100644 (file)
@@ -343,6 +343,7 @@ bool IsBetterCandidate(Toolkit::Control::KeyboardFocus::Direction direction, Rec
 bool IsFocusable(Actor& actor)\r
 {\r
   return (actor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) &&\r
+          actor.GetProperty<bool>(DevelActor::Property::USER_INTERACTION_ENABLED) &&\r
           actor.GetProperty<bool>(Actor::Property::VISIBLE) &&\r
           actor.GetProperty<Vector4>(Actor::Property::WORLD_COLOR).a > FULLY_TRANSPARENT);\r
 }\r
index 78a6268..aead2a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2021 Samsung Electronics Co., Ltd.
+* Copyright (c) 2022 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.
@@ -176,6 +176,8 @@ void GetRedOffsetAndMask(Dali::Pixel::Format pixelFormat, int32_t& byteOffset, i
     case Dali::Pixel::DEPTH_FLOAT:
     case Dali::Pixel::DEPTH_STENCIL:
     case Dali::Pixel::R11G11B10F:
+    case Dali::Pixel::CHROMINANCE_U:
+    case Dali::Pixel::CHROMINANCE_V:
     {
       DALI_LOG_ERROR("Pixel format not compatible.\n");
       byteOffset = 0;
diff --git a/dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp b/dali-toolkit/internal/controls/gl-view/drawable-view-impl.cpp
new file mode 100644 (file)
index 0000000..9b94546
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2021 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/gl-view/drawable-view-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/lifecycle-controller.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/rendering/renderer.h>
+
+namespace Dali::Toolkit::Internal
+{
+Dali::Toolkit::GlView DrawableView::New()
+{
+  auto* impl   = new DrawableView();
+  Dali::Toolkit::GlView handle = Dali::Toolkit::GlView(*impl);
+  impl->Initialize();
+  return handle;
+}
+
+DrawableView::DrawableView()
+: Dali::Toolkit::Internal::GlViewImpl( GlView::BackendMode::DIRECT_RENDERING ),
+  mRenderingMode(Toolkit::GlView::RenderingMode::CONTINUOUS),
+  mDepth(false),
+  mStencil(false),
+  mMSAA(0)
+{
+  mRenderCallback = RenderCallback::New( this, &DrawableView::OnRenderCallback);
+}
+
+DrawableView::~DrawableView() = default;
+
+void DrawableView::RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback)
+{
+  mOnInitCallback.reset( initCallback );
+  mOnRenderCallback.reset(renderFrameCallback );
+  mOnTerminateCallback. reset( terminateCallback );
+}
+
+void DrawableView::SetResizeCallback(CallbackBase* resizeCallback)
+{
+  mOnResizeCallback.reset( resizeCallback );
+}
+
+bool DrawableView::SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version)
+{
+  DALI_LOG_ERROR( "DrawableView::SetGraphicsConfig() is currently not implemented");
+
+  return true;
+}
+
+void DrawableView::SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode)
+{
+  mRenderingMode    = mode;
+  Renderer renderer = Self().GetRendererAt(0);
+
+  if(mRenderingMode == Dali::Toolkit::GlView::RenderingMode::ON_DEMAND)
+  {
+    renderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::IF_REQUIRED);
+  }
+  else
+  {
+    renderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY);
+  }
+}
+
+Dali::Toolkit::GlView::RenderingMode DrawableView::GetRenderingMode() const
+{
+  return mRenderingMode;
+}
+
+void DrawableView::RenderOnce()
+{
+  // Ignored.
+  // TODO: without rendering on the separate thread the RenderOnce won't
+  // work as expected. Potential implementation of threading may enable that
+  // feature.
+}
+
+void DrawableView::OnInitialize()
+{
+  AddRenderer();
+
+  // Adding VisibilityChange Signal.
+  Actor self = Self();
+  Dali::DevelActor::VisibilityChangedSignal(self).Connect(this, &DrawableView::OnControlVisibilityChanged);
+}
+
+void DrawableView::OnSizeSet(const Vector3& targetSize)
+{
+  Control::OnSizeSet(targetSize);
+
+  mSurfaceSize = targetSize;
+
+  // If the callbacks are set then schedule execution of resize callback
+  if(mRenderCallback && mOnResizeCallback)
+  {
+    mSurfaceResized = true;
+  }
+}
+
+void DrawableView::OnControlVisibilityChanged(Dali::Actor actor, bool visible, Dali::DevelActor::VisibilityChange::Type type)
+{
+  // Ignored due to lack dedicated rendering thread
+}
+
+void DrawableView::OnWindowVisibilityChanged(Window window, bool visible)
+{
+  // Ignored due to lack dedicated rendering thread
+}
+
+void DrawableView::OnSceneConnection(int depth)
+{
+  Control::OnSceneConnection(depth);
+
+  Actor  self   = Self();
+  Window window = DevelWindow::Get(self);
+
+  // Despite OnWindowVisibilityChanged() is ignored it still should follow
+  // the designed behaviour of GlView so signal is connected regardless
+  if(window)
+  {
+    DevelWindow::VisibilityChangedSignal(window).Connect(this, &DrawableView::OnWindowVisibilityChanged);
+  }
+}
+
+void DrawableView::OnSceneDisconnection()
+{
+  Control::OnSceneDisconnection();
+}
+
+void DrawableView::AddRenderer()
+{
+  Actor    self     = Self();
+  Renderer renderer = Renderer::New( *mRenderCallback );
+  self.AddRenderer(renderer);
+}
+
+bool DrawableView::OnRenderCallback( const RenderCallbackInput& renderCallbackInput )
+{
+  // Init state
+  if( mCurrentViewState == ViewState::INIT )
+  {
+    if(mOnInitCallback)
+    {
+      CallbackBase::Execute(*mOnInitCallback);
+    }
+    mCurrentViewState = ViewState::RENDER;
+  }
+
+  int renderFrameResult = 0;
+  if( mCurrentViewState == ViewState::RENDER )
+  {
+    // The mSurfaceResized is set by another thread so atomic check must be provided
+    bool expected{ true };
+    if(mSurfaceResized.compare_exchange_weak( expected, false,
+                                             std::memory_order_release,
+                                             std::memory_order_relaxed
+                                             ) && mOnResizeCallback)
+    {
+      CallbackBase::Execute(*mOnResizeCallback, static_cast<int>(mSurfaceSize.x), static_cast<int>(mSurfaceSize.y));
+    }
+
+    if(mOnRenderCallback)
+    {
+      renderFrameResult = CallbackBase::ExecuteReturn<int>(*mOnRenderCallback);
+      if(renderFrameResult)
+      {
+        // TODO: may be utilized for RenderOnce feature
+      }
+    }
+  }
+
+  // The terminate callback isn't easy to implement for DR. The NativeImage backend
+  // calls it when the GlView is being destroyed. For DrawableView it means that
+  // the RenderCallback won't be executed (as it is a part of graphics pipeline).
+  // We don't have currenty no way to know whether the View will be destroyed and
+  // to execute last native draw command in the pipeline.
+  //
+  // else if( mCurrentViewState == ViewState::TERMINATE )
+  // {
+  //    CallbackBase::Execute(*mOnTerminateCallback);
+  // }
+
+  return true;
+}
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/gl-view/drawable-view-impl.h b/dali-toolkit/internal/controls/gl-view/drawable-view-impl.h
new file mode 100644 (file)
index 0000000..c61832a
--- /dev/null
@@ -0,0 +1,168 @@
+#ifndef DALI_TOOLKIT_INTERNAL_DRAWABLE_VIEW_H
+#define DALI_TOOLKIT_INTERNAL_DRAWABLE_VIEW_H
+
+/*
+ * Copyright (c) 2021 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/actors/actor-devel.h>
+#include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
+#include <dali/public-api/adaptor-framework/window.h>
+#include <dali/public-api/rendering/geometry.h>
+#include <dali/public-api/rendering/shader.h>
+#include <dali/public-api/signals/render-callback.h>
+#include "gl-view-interface-impl.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/gl-view/gl-view-interface-impl.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
+
+namespace Dali::Toolkit
+{
+class GlView;
+
+namespace Internal
+{
+class DrawableView : public Dali::Toolkit::Internal::GlViewImpl
+{
+protected:
+  virtual ~DrawableView();
+
+public:
+  /**
+   * @copydoc Dali::Toolkit::GlView::New()
+   */
+  static Dali::Toolkit::GlView New();
+
+  /**
+   * Construct a new GlView.
+   */
+  DrawableView();
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RegisterGlCallbacks()
+   */
+  void RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback) override;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetResizeCallback()
+   */
+  void SetResizeCallback(CallbackBase* resizeCallback) override;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetGraphicsConfig()
+   */
+  bool SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version) override;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetRenderingMode()
+   */
+  void SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode) override;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::GetRenderingMode()
+   */
+  Dali::Toolkit::GlView::RenderingMode GetRenderingMode() const override;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RenderOnce()
+   */
+  void RenderOnce();
+
+private: // From Control
+  /**
+   * @copydoc Toolkit::Control::OnInitialize()
+   */
+  virtual void OnInitialize() override;
+
+  /**
+   * @copydoc Toolkit::Control::OnSceneConnection()
+   */
+  void OnSceneConnection(int depth) override;
+
+  /**
+   * @copydoc Toolkit::Control::OnSceneDisconnection()
+   */
+  void OnSceneDisconnection() override;
+
+  /**
+   * @copydoc Toolkit::Control::OnSizeSet()
+   */
+  void OnSizeSet(const Vector3& targetSize) override;
+
+private:
+  // Undefined copy constructor and assignment operators
+  DrawableView(const DrawableView& GlView);
+  DrawableView& operator=(const DrawableView& GlView);
+
+  /**
+   * Callback when the visibility of the GlView is changed
+   */
+  void OnControlVisibilityChanged(Dali::Actor actor, bool visible, Dali::DevelActor::VisibilityChange::Type type);
+
+  /**
+   * Callback when the visibility of the window is changed
+   */
+  void OnWindowVisibilityChanged(Dali::Window window, bool visible);
+
+  /**
+   * Adds renderer to Actor.
+   */
+  void AddRenderer();
+
+private:
+
+  bool OnRenderCallback( const RenderCallbackInput& renderCallbackInput );
+
+private:
+  Dali::Toolkit::GlView::RenderingMode mRenderingMode;
+
+  bool mDepth;
+  bool mStencil;
+  int  mMSAA;
+
+  std::unique_ptr<RenderCallback> mRenderCallback;
+
+  /*
+   * Used within RenderCallback to handle the current render state
+   */
+  enum class ViewState
+  {
+    INIT,
+    RENDER,
+    TERMINATE
+  };
+
+  ViewState mCurrentViewState{ViewState::INIT}; ///< state within RenderCallback
+
+  // These callbacks are stored for GLView API compatibility
+  std::unique_ptr<CallbackBase> mOnInitCallback;
+  std::unique_ptr<CallbackBase> mOnRenderCallback;
+  std::unique_ptr<CallbackBase> mOnTerminateCallback;
+  std::unique_ptr<CallbackBase> mOnResizeCallback;
+
+  std::atomic_bool mSurfaceResized{false}; ///< Flag to invoke surface resize callback
+
+  Size mSurfaceSize{}; ///< Surface size
+};
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_DRAWABLE_VIEW_H
index b37189a..2eaf78c 100644 (file)
@@ -40,14 +40,14 @@ namespace Internal
 {
 Dali::Toolkit::GlView GlView::New(Dali::Toolkit::GlView::ColorFormat colorFormat)
 {
-  GlView*               impl   = new GlView(colorFormat);
+  auto* impl   = new Dali::Toolkit::Internal::GlView(colorFormat);
   Dali::Toolkit::GlView handle = Dali::Toolkit::GlView(*impl);
   impl->Initialize();
   return handle;
 }
 
 GlView::GlView(Dali::Toolkit::GlView::ColorFormat colorFormat)
-: Control(ControlBehaviour(ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS)),
+: Dali::Toolkit::Internal::GlViewImpl( Toolkit::GlView::BackendMode::EGL_IMAGE_OFFSCREEN_RENDERING ),
   mRenderThread(nullptr),
   mNativeImageQueue(nullptr),
   mRenderingMode(Toolkit::GlView::RenderingMode::CONTINUOUS),
index 2dcb0b1..306a53b 100644 (file)
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h>
+#include <dali-toolkit/internal/controls/gl-view/gl-view-interface-impl.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
 #include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
 
-namespace Dali
-{
-namespace Toolkit
+
+namespace Dali::Toolkit
 {
 class GlView;
 
 namespace Internal
 {
-class GlView : public Dali::Toolkit::Internal::Control
+class GlView : public Dali::Toolkit::Internal::GlViewImpl
 {
 protected:
   virtual ~GlView();
@@ -57,32 +57,32 @@ public:
   /**
    * @copydoc Dali::Toolkit::GlView::RegisterGlCallbacks()
    */
-  void RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback);
+  void RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback) override;
 
   /**
    * @copydoc Dali::Toolkit::GlView::SetResizeCallback()
    */
-  void SetResizeCallback(CallbackBase* resizeCallback);
+  void SetResizeCallback(CallbackBase* resizeCallback) override;
 
   /**
-   * @copydoc Dali::Toolkit::GlView::SetGraphisConfig()
+   * @copydoc Dali::Toolkit::GlView::SetGraphicsConfig()
    */
-  bool SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version);
+  bool SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version) override;
 
   /**
    * @copydoc Dali::Toolkit::GlView::SetRenderingMode()
    */
-  void SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode);
+  void SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode) override;
 
   /**
    * @copydoc Dali::Toolkit::GlView::GetRenderingMode()
    */
-  Dali::Toolkit::GlView::RenderingMode GetRenderingMode() const;
+  Dali::Toolkit::GlView::RenderingMode GetRenderingMode() const override;
 
   /**
    * @copydoc Dali::Toolkit::GlView::RenderOnce()
    */
-  void RenderOnce();
+  void RenderOnce() override;
 
 private: // From Control
   /**
@@ -155,22 +155,6 @@ private:
 
 } // namespace Internal
 
-inline Dali::Toolkit::Internal::GlView& GetImpl(Dali::Toolkit::GlView& handle)
-{
-  DALI_ASSERT_ALWAYS(handle);
-  Dali::RefObject& impl = handle.GetImplementation();
-  return static_cast<Dali::Toolkit::Internal::GlView&>(impl);
-}
-
-inline const Dali::Toolkit::Internal::GlView& GetImpl(const Dali::Toolkit::GlView& handle)
-{
-  DALI_ASSERT_ALWAYS(handle);
-  const Dali::RefObject& impl = handle.GetImplementation();
-  return static_cast<const Dali::Toolkit::Internal::GlView&>(impl);
-}
-
-} // namespace Toolkit
-
 } // namespace Dali
 
 #endif // DALI_TOOLKIT_INTERNAL_GL_VIEW_H
diff --git a/dali-toolkit/internal/controls/gl-view/gl-view-interface-impl.h b/dali-toolkit/internal/controls/gl-view/gl-view-interface-impl.h
new file mode 100644 (file)
index 0000000..5547789
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef DALI_TOOLKIT_INTERNAL_GL_VIEW_IMPL_H
+#define DALI_TOOLKIT_INTERNAL_GL_VIEW_IMPL_H
+
+/*
+ * Copyright (c) 2022 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/actors/actor-devel.h>
+#include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
+#include <dali/public-api/adaptor-framework/window.h>
+#include <dali/public-api/rendering/geometry.h>
+#include <dali/public-api/rendering/shader.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
+
+namespace Dali::Toolkit
+{
+class GlView;
+
+namespace Internal
+{
+class GlViewImpl : public Dali::Toolkit::Internal::Control
+{
+protected:
+
+  virtual ~GlViewImpl() = default;
+
+public:
+
+  /**
+   * Construct a new GlView.
+   */
+  explicit GlViewImpl( GlView::BackendMode backendMode ) :
+    Control(ControlBehaviour(0u | ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS)),
+    mBackendMode(backendMode)
+  {
+  }
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RegisterGlCallbacks()
+   */
+  virtual void RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback) = 0;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetResizeCallback()
+   */
+  virtual void SetResizeCallback(CallbackBase* resizeCallback) = 0;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetGraphisConfig()
+   */
+  virtual bool SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version) = 0;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetRenderingMode()
+   */
+  virtual void SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode) = 0;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::GetRenderingMode()
+   */
+  virtual Dali::Toolkit::GlView::RenderingMode GetRenderingMode() const = 0;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::GetBackendMode()
+   */
+  [[nodiscard]] Dali::Toolkit::GlView::BackendMode GetBackendMode() const
+  {
+    return mBackendMode;
+  }
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RenderOnce()
+   */
+  virtual void RenderOnce() = 0;
+
+private: // From Control
+  /**
+   * @copydoc Toolkit::Control::OnInitialize()
+   */
+  virtual void OnInitialize() override = 0;
+
+  /**
+   * @copydoc Toolkit::Control::OnSceneConnection()
+   */
+  virtual void OnSceneConnection(int depth) override = 0;
+
+  /**
+   * @copydoc Toolkit::Control::OnSceneDisconnection()
+   */
+  virtual void OnSceneDisconnection() override = 0;
+
+protected:
+
+  GlView::BackendMode mBackendMode { GlView::BackendMode::DEFAULT }; ///< Implementation backend mode (DirectRendering, EGL image)
+};
+
+} // namespace Internal
+
+inline Dali::Toolkit::Internal::GlViewImpl& GetImpl(Dali::Toolkit::GlView& handle)
+{
+  DALI_ASSERT_ALWAYS(handle);
+  Dali::RefObject& impl = handle.GetImplementation();
+  return static_cast<Dali::Toolkit::Internal::GlViewImpl&>(impl);
+}
+
+inline const Dali::Toolkit::Internal::GlViewImpl& GetImpl(const Dali::Toolkit::GlView& handle)
+{
+  DALI_ASSERT_ALWAYS(handle);
+  const Dali::RefObject& impl = handle.GetImplementation();
+  return static_cast<const Dali::Toolkit::Internal::GlViewImpl&>(impl);
+}
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_GL_VIEW_IMPL_H
index d6bae90..b44029c 100644 (file)
@@ -37,6 +37,7 @@
 #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/controls/text-controls/text-editor-property-handler.h>
+#include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/text/rendering/text-backend.h>
 #include <dali-toolkit/internal/text/text-effects-style.h>
@@ -158,6 +159,7 @@ DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "strikethrough",
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "inputStrikethrough",                   MAP,       INPUT_STRIKETHROUGH                 )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "characterSpacing",                     FLOAT,     CHARACTER_SPACING                   )
 DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "relativeLineSize",                     FLOAT,     RELATIVE_LINE_SIZE                  )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit,           TextEditor, "verticalAlignment",                    STRING,    VERTICAL_ALIGNMENT                  )
 
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "textChanged",           SIGNAL_TEXT_CHANGED           )
 DALI_SIGNAL_REGISTRATION(Toolkit, TextEditor, "inputStyleChanged",     SIGNAL_INPUT_STYLE_CHANGED    )
@@ -672,6 +674,31 @@ void TextEditor::ResizeActor(Actor& actor, const Vector2& size)
   }
 }
 
+void TextEditor::OnPropertySet(Property::Index index, const Property::Value& propertyValue)
+{
+  DALI_LOG_INFO(gTextEditorLogFilter, Debug::Verbose, "TextEditor::OnPropertySet index[%d]\n", index);
+
+  switch(index)
+  {
+    case DevelActor::Property::USER_INTERACTION_ENABLED:
+    {
+      const bool enabled = propertyValue.Get<bool>();
+      mController->SetUserInteractionEnabled(enabled);
+      if(mStencil)
+      {
+        float opacity = enabled ? 1.0f : mController->GetDisabledColorOpacity();
+        mStencil.SetProperty(Actor::Property::OPACITY, opacity);
+      }
+      break;
+    }
+    default:
+    {
+      Control::OnPropertySet(index, propertyValue); // up call to control for non-handled properties
+      break;
+    }
+  }
+}
+
 void TextEditor::OnRelayout(const Vector2& size, RelayoutContainer& container)
 {
   DALI_LOG_INFO(gTextEditorLogFilter, Debug::Verbose, "TextEditor OnRelayout\n");
@@ -810,7 +837,10 @@ void TextEditor::OnKeyInputFocusGained()
     notifier.ContentSelectedSignal().Connect(this, &TextEditor::OnClipboardTextSelected);
   }
 
-  mController->KeyboardFocusGainEvent(); // Called in the case of no virtual keyboard to trigger this event
+  if(IsEditable() && mController->IsUserInteractionEnabled())
+  {
+    mController->KeyboardFocusGainEvent(); // Called in the case of no virtual keyboard to trigger this event
+  }
 
   EmitKeyInputFocusSignal(true); // Calls back into the Control hence done last.
 }
@@ -862,6 +892,11 @@ void TextEditor::OnTap(const TapGesture& gesture)
   mController->TapEvent(gesture.GetNumberOfTaps(), localPoint.x - padding.start, localPoint.y - padding.top);
   mController->AnchorEvent(localPoint.x - padding.start, localPoint.y - padding.top);
 
+  Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+  if (keyboardFocusManager)
+  {
+    keyboardFocusManager.SetCurrentFocusActor(Self());
+  }
   SetKeyInputFocus();
 }
 
@@ -893,6 +928,11 @@ bool TextEditor::OnKeyEvent(const KeyEvent& event)
     // Make sure ClearKeyInputFocus when only key is up
     if(event.GetState() == KeyEvent::UP)
     {
+      Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+      if (keyboardFocusManager)
+      {
+        keyboardFocusManager.ClearFocus();
+      }
       ClearKeyInputFocus();
     }
 
index 5739ac2..c62988a 100644 (file)
@@ -221,6 +221,11 @@ private: // From Control
   void OnSceneConnection(int depth) override;
 
   /**
+   * @copydoc Control::OnPropertySet()
+   */
+  void OnPropertySet(Property::Index index, const Property::Value& propertyValue) override;
+
+  /**
    * @copydoc Dali::CustomActorImpl::OnKeyEvent(const KeyEvent&)
    */
   bool OnKeyEvent(const KeyEvent& event) override;
index 5ea2cf1..361607a 100644 (file)
@@ -126,6 +126,16 @@ void TextEditor::PropertyHandler::SetProperty(Toolkit::TextEditor textEditor, Pr
       }
       break;
     }
+    case Toolkit::DevelTextEditor::Property::VERTICAL_ALIGNMENT:
+    {
+      Toolkit::Text::VerticalAlignment::Type alignment(static_cast<Text::VerticalAlignment::Type>(-1)); // Set to invalid value to ensure a valid mode does get set
+      if(Text::GetVerticalAlignmentEnumeration(value, alignment))
+      {
+        impl.mController->SetVerticalAlignment(alignment);
+        DALI_LOG_INFO(gTextEditorLogFilter, Debug::General, "TextEditor %p VERTICAL_ALIGNMENT %d\n", impl.mController.Get(), alignment);
+      }
+      break;
+    }
     case Toolkit::TextEditor::Property::SCROLL_THRESHOLD:
     {
       const float threshold = value.Get<float>();
@@ -786,6 +796,16 @@ Property::Value TextEditor::PropertyHandler::GetProperty(Toolkit::TextEditor tex
       }
       break;
     }
+    case Toolkit::DevelTextEditor::Property::VERTICAL_ALIGNMENT:
+    {
+      const char* name = Text::GetVerticalAlignmentString(impl.mController->GetVerticalAlignment());
+
+      if(name)
+      {
+        value = std::string(name);
+      }
+      break;
+    }
     case Toolkit::TextEditor::Property::SCROLL_THRESHOLD:
     {
       value = impl.mDecorator->GetScrollThreshold();
index 3cbd0b9..b7a6eb4 100644 (file)
@@ -36,6 +36,7 @@
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/internal/controls/text-controls/text-field-property-handler.h>
+#include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/text/rendering/text-backend.h>
 #include <dali-toolkit/internal/text/text-effects-style.h>
@@ -607,6 +608,31 @@ void TextField::ResizeActor(Actor& actor, const Vector2& size)
   }
 }
 
+void TextField::OnPropertySet(Property::Index index, const Property::Value& propertyValue)
+{
+  DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField::OnPropertySet index[%d]\n", index);
+
+  switch(index)
+  {
+    case DevelActor::Property::USER_INTERACTION_ENABLED:
+    {
+      const bool enabled = propertyValue.Get<bool>();
+      mController->SetUserInteractionEnabled(enabled);
+      if(mStencil)
+      {
+        float opacity = enabled ? 1.0f : mController->GetDisabledColorOpacity();
+        mStencil.SetProperty(Actor::Property::OPACITY, opacity);
+      }
+      break;
+    }
+    default:
+    {
+      Control::OnPropertySet(index, propertyValue); // up call to control for non-handled properties
+      break;
+    }
+  }
+}
+
 void TextField::OnRelayout(const Vector2& size, RelayoutContainer& container)
 {
   DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField OnRelayout\n");
@@ -745,7 +771,10 @@ void TextField::OnKeyInputFocusGained()
     notifier.ContentSelectedSignal().Connect(this, &TextField::OnClipboardTextSelected);
   }
 
-  mController->KeyboardFocusGainEvent(); // Called in the case of no virtual keyboard to trigger this event
+  if(IsEditable() && mController->IsUserInteractionEnabled())
+  {
+    mController->KeyboardFocusGainEvent(); // Called in the case of no virtual keyboard to trigger this event
+  }
 
   EmitKeyInputFocusSignal(true); // Calls back into the Control hence done last.
 }
@@ -796,6 +825,11 @@ void TextField::OnTap(const TapGesture& gesture)
   mController->TapEvent(gesture.GetNumberOfTaps(), localPoint.x - padding.start, localPoint.y - padding.top);
   mController->AnchorEvent(localPoint.x - padding.start, localPoint.y - padding.top);
 
+  Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+  if (keyboardFocusManager)
+  {
+    keyboardFocusManager.SetCurrentFocusActor(Self());
+  }
   SetKeyInputFocus();
 }
 
@@ -827,6 +861,11 @@ bool TextField::OnKeyEvent(const KeyEvent& event)
     // Make sure ClearKeyInputFocus when only key is up
     if(event.GetState() == KeyEvent::UP)
     {
+      Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+      if (keyboardFocusManager)
+      {
+        keyboardFocusManager.ClearFocus();
+      }
       ClearKeyInputFocus();
     }
 
index d2d7fdf..6c4faa6 100644 (file)
@@ -212,6 +212,11 @@ private: // From Control
   void OnSceneConnection(int depth) override;
 
   /**
+   * @copydoc Control::OnPropertySet()
+   */
+  void OnPropertySet(Property::Index index, const Property::Value& propertyValue) override;
+
+  /**
    * @copydoc Dali::CustomActorImpl::OnKeyEvent(const KeyEvent&)
    */
   bool OnKeyEvent(const KeyEvent& event) override;
index 5d41058..ffa258d 100644 (file)
@@ -38,6 +38,7 @@
 #include <dali-toolkit/devel-api/controls/text-controls/text-selection-popup-callback-interface.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.h>
 #include <dali-toolkit/internal/helpers/color-conversion.h>
 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
@@ -149,104 +150,8 @@ void TextSelectionPopup::SetProperty(BaseObject* object, Property::Index index,
 
   if(selectionPopup)
   {
-    TextSelectionPopup& impl(GetImpl(selectionPopup));
-
-    switch(index)
-    {
-      case Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE:
-      {
-        impl.SetDimensionToCustomise(POPUP_MAXIMUM_SIZE, value.Get<Vector2>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_MAX_SIZE:
-      {
-        impl.SetDimensionToCustomise(OPTION_MAXIMUM_SIZE, value.Get<Vector2>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_MIN_SIZE:
-      {
-        impl.SetDimensionToCustomise(OPTION_MINIMUM_SIZE, value.Get<Vector2>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_SIZE:
-      {
-        impl.SetDimensionToCustomise(OPTION_DIVIDER_SIZE, value.Get<Vector2>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_PADDING:
-      {
-        Vector4 padding(value.Get<Vector4>());
-        impl.SetOptionDividerPadding(Padding(padding.x, padding.y, padding.z, padding.w));
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE:
-      {
-        impl.SetButtonImage(Toolkit::TextSelectionPopup::CLIPBOARD, value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE:
-      {
-        impl.SetButtonImage(Toolkit::TextSelectionPopup::CUT, value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_COPY_BUTTON_ICON_IMAGE:
-      {
-        impl.SetButtonImage(Toolkit::TextSelectionPopup::COPY, value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE:
-      {
-        impl.SetButtonImage(Toolkit::TextSelectionPopup::PASTE, value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE:
-      {
-        impl.SetButtonImage(Toolkit::TextSelectionPopup::SELECT, value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE:
-      {
-        impl.SetButtonImage(Toolkit::TextSelectionPopup::SELECT_ALL, value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_DIVIDER_COLOR:
-      {
-        impl.mDividerColor = value.Get<Vector4>();
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_ICON_COLOR:
-      {
-        impl.mIconColor = value.Get<Vector4>();
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_COLOR:
-      {
-        impl.mPressedColor = value.Get<Vector4>();
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_IMAGE:
-      {
-        impl.SetPressedImage(value.Get<std::string>());
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_FADE_IN_DURATION:
-      {
-        impl.mFadeInDuration = value.Get<float>();
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION:
-      {
-        impl.mFadeOutDuration = value.Get<float>();
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER:
-      {
-        Property::Map map = value.Get<Property::Map>();
-        impl.CreateBackgroundBorder(map);
-        break;
-      }
-    } // switch
-  }   // TextSelectionPopup
+    PropertyHandler::SetProperty(selectionPopup, index, value);
+  }
 }
 
 Property::Value TextSelectionPopup::GetProperty(BaseObject* object, Property::Index index)
@@ -257,93 +162,7 @@ Property::Value TextSelectionPopup::GetProperty(BaseObject* object, Property::In
 
   if(selectionPopup)
   {
-    TextSelectionPopup& impl(GetImpl(selectionPopup));
-
-    switch(index)
-    {
-      case Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE:
-      {
-        value = impl.GetDimensionToCustomise(POPUP_MAXIMUM_SIZE);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_MAX_SIZE:
-      {
-        value = impl.GetDimensionToCustomise(OPTION_MAXIMUM_SIZE);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_MIN_SIZE:
-      {
-        value = impl.GetDimensionToCustomise(OPTION_MINIMUM_SIZE);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_SIZE:
-      {
-        value = impl.GetDimensionToCustomise(OPTION_DIVIDER_SIZE);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_PADDING:
-      {
-        Padding padding = impl.GetOptionDividerPadding();
-        value           = Vector4(padding.left, padding.right, padding.top, padding.bottom);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE:
-      {
-        value = impl.GetButtonImage(Toolkit::TextSelectionPopup::CLIPBOARD);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE:
-      {
-        value = impl.GetButtonImage(Toolkit::TextSelectionPopup::CUT);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_COPY_BUTTON_ICON_IMAGE:
-      {
-        value = impl.GetButtonImage(Toolkit::TextSelectionPopup::COPY);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE:
-      {
-        value = impl.GetButtonImage(Toolkit::TextSelectionPopup::PASTE);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE:
-      {
-        value = impl.GetButtonImage(Toolkit::TextSelectionPopup::SELECT);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE:
-      {
-        value = impl.GetButtonImage(Toolkit::TextSelectionPopup::SELECT_ALL);
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_IMAGE:
-      {
-        value = impl.GetPressedImage();
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_FADE_IN_DURATION:
-      {
-        value = impl.mFadeInDuration;
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION:
-      {
-        value = impl.mFadeOutDuration;
-        break;
-      }
-      case Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER:
-      {
-        Property::Map         map;
-        Toolkit::Visual::Base visual = DevelControl::GetVisual(impl, Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER);
-        if(visual)
-        {
-          visual.CreatePropertyMap(map);
-        }
-        value = map;
-        break;
-      }
-    } // switch
+    value = PropertyHandler::GetProperty(selectionPopup, index);
   }
   return value;
 }
index 41686ee..db51100 100644 (file)
@@ -273,6 +273,8 @@ private: // Implementation
   virtual ~TextSelectionPopup();
 
 protected:
+  struct PropertyHandler;
+
   class TextSelectionPopupAccessible : public DevelControl::ControlAccessible
   {
   public:
diff --git a/dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.cpp b/dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.cpp
new file mode 100644 (file)
index 0000000..fe6e93f
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2022 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/internal/controls/text-controls/text-selection-popup-property-handler.h>
+#include <dali/integration-api/debug.h>
+
+#if defined(DEBUG_ENABLED)
+extern Debug::Filter* gLogFilter;
+#endif
+
+namespace Dali::Toolkit::Internal
+{
+void TextSelectionPopup::PropertyHandler::SetProperty(Toolkit::TextSelectionPopup selectionPopup, Property::Index index, const Property::Value& value)
+{
+  TextSelectionPopup& impl(GetImpl(selectionPopup));
+
+  switch(index)
+  {
+    case Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE:
+    {
+      impl.SetDimensionToCustomise(POPUP_MAXIMUM_SIZE, value.Get<Vector2>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_MAX_SIZE:
+    {
+      impl.SetDimensionToCustomise(OPTION_MAXIMUM_SIZE, value.Get<Vector2>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_MIN_SIZE:
+    {
+      impl.SetDimensionToCustomise(OPTION_MINIMUM_SIZE, value.Get<Vector2>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_SIZE:
+    {
+      impl.SetDimensionToCustomise(OPTION_DIVIDER_SIZE, value.Get<Vector2>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_PADDING:
+    {
+      Vector4 padding(value.Get<Vector4>());
+      impl.SetOptionDividerPadding(Padding(padding.x, padding.y, padding.z, padding.w));
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE:
+    {
+      impl.SetButtonImage(Toolkit::TextSelectionPopup::CLIPBOARD, value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE:
+    {
+      impl.SetButtonImage(Toolkit::TextSelectionPopup::CUT, value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_COPY_BUTTON_ICON_IMAGE:
+    {
+      impl.SetButtonImage(Toolkit::TextSelectionPopup::COPY, value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE:
+    {
+      impl.SetButtonImage(Toolkit::TextSelectionPopup::PASTE, value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE:
+    {
+      impl.SetButtonImage(Toolkit::TextSelectionPopup::SELECT, value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE:
+    {
+      impl.SetButtonImage(Toolkit::TextSelectionPopup::SELECT_ALL, value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_DIVIDER_COLOR:
+    {
+      impl.mDividerColor = value.Get<Vector4>();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_ICON_COLOR:
+    {
+      impl.mIconColor = value.Get<Vector4>();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_COLOR:
+    {
+      impl.mPressedColor = value.Get<Vector4>();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_IMAGE:
+    {
+      impl.SetPressedImage(value.Get<std::string>());
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_FADE_IN_DURATION:
+    {
+      impl.mFadeInDuration = value.Get<float>();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION:
+    {
+      impl.mFadeOutDuration = value.Get<float>();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER:
+    {
+      Property::Map map = value.Get<Property::Map>();
+      impl.CreateBackgroundBorder(map);
+      break;
+    }
+  }
+}
+
+Property::Value TextSelectionPopup::PropertyHandler::GetProperty(Toolkit::TextSelectionPopup selectionPopup, Property::Index index)
+{
+  Property::Value value;
+  TextSelectionPopup& impl(GetImpl(selectionPopup));
+
+  switch(index)
+  {
+    case Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE:
+    {
+      value = impl.GetDimensionToCustomise(POPUP_MAXIMUM_SIZE);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_MAX_SIZE:
+    {
+      value = impl.GetDimensionToCustomise(OPTION_MAXIMUM_SIZE);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_MIN_SIZE:
+    {
+      value = impl.GetDimensionToCustomise(OPTION_MINIMUM_SIZE);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_SIZE:
+    {
+      value = impl.GetDimensionToCustomise(OPTION_DIVIDER_SIZE);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_PADDING:
+    {
+      Padding padding = impl.GetOptionDividerPadding();
+      value           = Vector4(padding.left, padding.right, padding.top, padding.bottom);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE:
+    {
+      value = impl.GetButtonImage(Toolkit::TextSelectionPopup::CLIPBOARD);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE:
+    {
+      value = impl.GetButtonImage(Toolkit::TextSelectionPopup::CUT);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_COPY_BUTTON_ICON_IMAGE:
+    {
+      value = impl.GetButtonImage(Toolkit::TextSelectionPopup::COPY);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE:
+    {
+      value = impl.GetButtonImage(Toolkit::TextSelectionPopup::PASTE);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE:
+    {
+      value = impl.GetButtonImage(Toolkit::TextSelectionPopup::SELECT);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE:
+    {
+      value = impl.GetButtonImage(Toolkit::TextSelectionPopup::SELECT_ALL);
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_DIVIDER_COLOR:
+    {
+      value = impl.mDividerColor;
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_ICON_COLOR:
+    {
+      value = impl.mIconColor;
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_COLOR:
+    {
+      value = impl.mPressedColor;
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_IMAGE:
+    {
+      value = impl.GetPressedImage();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_FADE_IN_DURATION:
+    {
+      value = impl.mFadeInDuration;
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION:
+    {
+      value = impl.mFadeOutDuration;
+      break;
+    }
+    case Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER:
+    {
+      Property::Map         map;
+      Toolkit::Visual::Base visual = DevelControl::GetVisual(impl, Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER);
+      if(visual)
+      {
+        visual.CreatePropertyMap(map);
+      }
+      value = map;
+      break;
+    }
+  }
+
+  return value;
+}
+
+} // namespace Dali::Toolkit::Internal
diff --git a/dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.h b/dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.h
new file mode 100644 (file)
index 0000000..805e3d8
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef DALI_TOOLKIT_INTERNAL_TEXT_SELECTION_POPUP_PROPERTY_HANDLER_H
+#define DALI_TOOLKIT_INTERNAL_TEXT_SELECTION_POPUP_PROPERTY_HANDLER_H
+
+/*
+ * Copyright (c) 2022 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/internal/controls/text-controls/text-selection-popup-impl.h>
+
+namespace Dali::Toolkit::Internal
+{
+/**
+ * Class to manage properties for the TextSelectionPopup
+ */
+struct TextSelectionPopup::PropertyHandler
+{
+  /**
+   * Set properties on the text selection popup
+   *
+   * @param[in] selectionPopup The handle for the text selection popup
+   * @param[in] index The property index of the property to set
+   * @param[in] value The value to set
+   */
+  static void SetProperty(Toolkit::TextSelectionPopup selectionPopup, Property::Index index, const Property::Value& value);
+
+  /**
+   * Get properties from the text selection popup
+   *
+   * @param[in] selectionPopup The handle for the text selection popup
+   * @param[in] index The property index of the property to set
+   * @return the value
+   */
+  static Property::Value GetProperty(Toolkit::TextSelectionPopup selectionPopup, Property::Index index);
+};
+
+} // namespace Dali::Toolkit::Internal
+
+#endif //DALI_TOOLKIT_INTERNAL_TEXT_SELECTION_POPUP_PROPERTY_HANDLER_H
index 0a8c129..7fc2117 100755 (executable)
@@ -204,6 +204,13 @@ void WebView::OnInitialize()
     mWebSettings        = std::unique_ptr<Dali::Toolkit::WebSettings>(new WebSettings(mWebEngine.GetSettings()));
     mWebBackForwardList = std::unique_ptr<Dali::Toolkit::WebBackForwardList>(new WebBackForwardList(mWebEngine.GetBackForwardList()));
   }
+
+  self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::FILLER);
+}
+
+DevelControl::ControlAccessible* WebView::CreateAccessibleObject()
+{
+  return new WebViewAccessible(Self(), mWebEngine);
 }
 
 Dali::Toolkit::WebSettings* WebView::GetSettings() const
@@ -1236,6 +1243,58 @@ bool WebView::SetVisibility(bool visible)
   return mWebEngine ? mWebEngine.SetVisibility(visible) : false;
 }
 
+WebView::WebViewAccessible::WebViewAccessible(Dali::Actor self, Dali::WebEngine& webEngine)
+: ControlAccessible(self), mRemoteChild{}, mWebEngine{webEngine}
+{
+  Dali::Accessibility::Bridge::EnabledSignal().Connect(this, &WebViewAccessible::OnAccessibilityEnabled);
+  Dali::Accessibility::Bridge::DisabledSignal().Connect(this, &WebViewAccessible::OnAccessibilityDisabled);
+
+  if(Dali::Accessibility::IsUp())
+  {
+    OnAccessibilityEnabled();
+  }
+  else
+  {
+    OnAccessibilityDisabled();
+  }
+}
+
+void WebView::WebViewAccessible::DoGetChildren(std::vector<Dali::Accessibility::Accessible*>& children)
+{
+  if(mRemoteChild.GetAddress())
+  {
+    children.push_back(&mRemoteChild);
+  }
+}
+
+void WebView::WebViewAccessible::OnAccessibilityEnabled()
+{
+  if(!mWebEngine)
+  {
+    return;
+  }
+
+  mWebEngine.ActivateAccessibility(true);
+  SetRemoteChildAddress(mWebEngine.GetAccessibilityAddress());
+}
+
+void WebView::WebViewAccessible::OnAccessibilityDisabled()
+{
+  if(!mWebEngine)
+  {
+    return;
+  }
+
+  SetRemoteChildAddress({});
+  mWebEngine.ActivateAccessibility(false);
+}
+
+void WebView::WebViewAccessible::SetRemoteChildAddress(Dali::Accessibility::Address address)
+{
+  mRemoteChild.SetAddress(std::move(address));
+  OnChildrenChanged();
+}
+
 #undef GET_ENUM_STRING
 #undef GET_ENUM_VALUE
 
index 2d8a523..6a0da6e 100755 (executable)
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/adaptor-framework/proxy-accessible.h>
 #include <dali/devel-api/adaptor-framework/web-engine.h>
 #include <dali/public-api/images/image-operations.h>
 #include <dali/public-api/object/property-notification.h>
 #include <memory>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-accessible.h>
 #include <dali-toolkit/devel-api/controls/web-view/web-view.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-base.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
@@ -423,6 +425,11 @@ private: // From Control
   void OnInitialize() override;
 
   /**
+   * @copydoc Toolkit::Internal::Control::CreateAccessibleObject()
+   */
+  DevelControl::ControlAccessible* CreateAccessibleObject() override;
+
+  /**
    * @copydoc Toolkit::Control::GetNaturalSize
    */
   Vector3 GetNaturalSize() override;
@@ -663,6 +670,29 @@ private:
    */
   void OnScreenshotCaptured(Dali::PixelData pixel);
 
+protected:
+  class WebViewAccessible : public DevelControl::ControlAccessible
+  {
+  public:
+    WebViewAccessible() = delete;
+
+    WebViewAccessible(Dali::Actor self, Dali::WebEngine& webEngine);
+
+  protected:
+    /**
+     * @copydoc Dali::Accessibility::ActorAccessible::DoGetChildren()
+     */
+    void DoGetChildren(std::vector<Dali::Accessibility::Accessible*>& children) override;
+
+  private:
+    void OnAccessibilityEnabled();
+    void OnAccessibilityDisabled();
+    void SetRemoteChildAddress(Dali::Accessibility::Address address);
+
+    Dali::Accessibility::ProxyAccessible mRemoteChild;
+    Dali::WebEngine&                     mWebEngine;
+  };
+
 private:
   Dali::Toolkit::Visual::Base mVisual;
   Dali::Size                  mWebViewSize;
index 355667e..15640b7 100644 (file)
@@ -111,12 +111,14 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/controls/text-controls/text-field-property-handler.cpp
    ${toolkit_src_dir}/controls/text-controls/text-label-impl.cpp
    ${toolkit_src_dir}/controls/text-controls/text-selection-popup-impl.cpp
+   ${toolkit_src_dir}/controls/text-controls/text-selection-popup-property-handler.cpp
    ${toolkit_src_dir}/controls/text-controls/text-selection-toolbar-impl.cpp
    ${toolkit_src_dir}/controls/tool-bar/tool-bar-impl.cpp
    ${toolkit_src_dir}/controls/tooltip/tooltip.cpp
    ${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/gl-view-impl.cpp
    ${toolkit_src_dir}/controls/gl-view/gl-view-render-thread.cpp
    ${toolkit_src_dir}/accessibility-manager/accessibility-manager-impl.cpp
index 977e2e0..a64690e 100644 (file)
@@ -220,7 +220,7 @@ bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
     }
   }
 
-  if(actor && actor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) && actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+  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))
   {
     Integration::SceneHolder currentWindow = Integration::SceneHolder::Get(actor);
 
@@ -244,18 +244,13 @@ bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
   }
 
   // Check whether the actor is in the stage and is keyboard focusable.
-  if(actor && actor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) && actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+  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))
   {
     if((mIsFocusIndicatorShown == SHOW) && (mEnableFocusIndicator == ENABLE))
     {
       actor.Add(GetFocusIndicatorActor());
     }
 
-    // Send notification for the change of focus actor
-    if(!mFocusChangedSignal.Empty())
-    {
-      mFocusChangedSignal.Emit(currentFocusedActor, actor);
-    }
 
     Toolkit::Control currentlyFocusedControl = Toolkit::Control::DownCast(currentFocusedActor);
     if(currentlyFocusedControl)
@@ -265,8 +260,6 @@ bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
       currentlyFocusedControl.ClearKeyInputFocus();
     }
 
-    DALI_LOG_INFO(gLogFilter, Debug::General, "[%s:%d] Focus Changed\n", __FUNCTION__, __LINE__);
-
     // Save the current focused actor
     mCurrentFocusActor = actor;
 
@@ -303,6 +296,11 @@ bool KeyboardFocusManager::DoSetCurrentFocusActor(Actor actor)
       mFocusHistory.erase(beginPos);
     }
 
+    // Send notification for the change of focus actor
+    if(!mFocusChangedSignal.Empty())
+    {
+      mFocusChangedSignal.Emit(currentFocusedActor, actor);
+    }
     DALI_LOG_INFO(gLogFilter, Debug::General, "[%s:%d] SUCCEED\n", __FUNCTION__, __LINE__);
     success = true;
   }
@@ -554,7 +552,7 @@ bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocus::Direction
       }
     }
 
-    if(nextFocusableActor && nextFocusableActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))
+    if(nextFocusableActor && nextFocusableActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) && nextFocusableActor.GetProperty<bool>(DevelActor::Property::USER_INTERACTION_ENABLED))
     {
       // Whether the next focusable actor is a layout control
       if(IsLayoutControl(nextFocusableActor))
@@ -580,7 +578,7 @@ bool KeyboardFocusManager::DoMoveFocusWithinLayoutControl(Toolkit::Control contr
   Actor nextFocusableActor = GetImplementation(control).GetNextKeyboardFocusableActor(actor, direction, mFocusGroupLoopEnabled);
   if(nextFocusableActor)
   {
-    if(!nextFocusableActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))
+    if(!(nextFocusableActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) || nextFocusableActor.GetProperty<bool>(DevelActor::Property::USER_INTERACTION_ENABLED)))
     {
       // If the actor is not focusable, ask the same layout control for the next actor to focus
       return DoMoveFocusWithinLayoutControl(control, nextFocusableActor, direction);
@@ -599,7 +597,7 @@ bool KeyboardFocusManager::DoMoveFocusWithinLayoutControl(Toolkit::Control contr
         mIsWaitingKeyboardFocusChangeCommit = false;
       }
 
-      if(committedFocusActor && committedFocusActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE))
+      if(committedFocusActor && committedFocusActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) && committedFocusActor.GetProperty<bool>(DevelActor::Property::USER_INTERACTION_ENABLED))
       {
         // Whether the commited focusable actor is a layout control
         if(IsLayoutControl(committedFocusActor))
@@ -681,14 +679,10 @@ void KeyboardFocusManager::DoKeyboardEnter(Actor actor)
 
 void KeyboardFocusManager::ClearFocus()
 {
+  ClearFocusIndicator();
   Actor actor = GetCurrentFocusActor();
   if(actor)
   {
-    if(mFocusIndicatorActor)
-    {
-      actor.Remove(mFocusIndicatorActor);
-    }
-
     // Send notification for the change of focus actor
     if(!mFocusChangedSignal.Empty())
     {
@@ -702,8 +696,19 @@ void KeyboardFocusManager::ClearFocus()
       currentlyFocusedControl.ClearKeyInputFocus();
     }
   }
-
   mCurrentFocusActor.Reset();
+}
+
+void KeyboardFocusManager::ClearFocusIndicator()
+{
+  Actor actor = GetCurrentFocusActor();
+  if(actor)
+  {
+    if(mFocusIndicatorActor)
+    {
+      actor.Remove(mFocusIndicatorActor);
+    }
+  }
   mIsFocusIndicatorShown = (mAlwaysShowIndicator == ALWAYS_SHOW) ? SHOW : HIDE;
 }
 
@@ -1005,18 +1010,30 @@ void KeyboardFocusManager::OnTouch(const TouchEvent& touch)
   // We only do this on a Down event, otherwise the clear action may override a manually focused actor.
   if(((touch.GetPointCount() < 1) || (touch.GetState(0) == PointState::DOWN)))
   {
-    // If mClearFocusOnTouch is false, do not clear the focus even if user touch the screen.
-    if(mClearFocusOnTouch)
+    // If you touch the currently focused actor again, you don't need to do SetCurrentFocusActor again.
+    Actor hitActor = touch.GetHitActor(0);
+    if(hitActor && hitActor == GetCurrentFocusActor())
     {
-      ClearFocus();
+      return;
     }
-
     // If KEYBOARD_FOCUSABLE and TOUCH_FOCUSABLE is true, set focus actor
-    Actor hitActor = touch.GetHitActor(0);
     if(hitActor && hitActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) && hitActor.GetProperty<bool>(DevelActor::Property::TOUCH_FOCUSABLE))
     {
+      // If mClearFocusOnTouch is false, do not clear the focus
+      if(mClearFocusOnTouch)
+      {
+        ClearFocus();
+      }
       SetCurrentFocusActor(hitActor);
     }
+    else
+    {
+      // If mClearFocusOnTouch is false, do not clear the focus indicator even if user touch the screen.
+      if(mClearFocusOnTouch)
+      {
+        ClearFocusIndicator();
+      }
+    }
   }
 }
 
index 3b09623..ae06097 100644 (file)
@@ -323,6 +323,11 @@ private:
    */
   bool EmitCustomWheelSignals(Actor actor, const WheelEvent& event);
 
+  /**
+   * Clear the focus indicator actor.
+   */
+  void ClearFocusIndicator();
+
 private:
   // Undefined
   KeyboardFocusManager(const KeyboardFocusManager&);
index ec15ff4..4b3150e 100644 (file)
@@ -48,13 +48,17 @@ struct BoundedParagraphRun
   BoundedParagraphRun()
   : characterRun{},
     horizontalAlignment(Text::HorizontalAlignment::BEGIN),
-    horizontalAlignmentDefined{false}
+    relativeLineSize(1),
+    horizontalAlignmentDefined{false},
+    relativeLineSizeDefined(false)
   {
   }
 
   CharacterRun                    characterRun;                   ///< The initial character index within the whole text and the number of characters of the run.
   Text::HorizontalAlignment::Type horizontalAlignment;            ///< The paragraph horizontal alignment. Values "BEGIN" "CENTER" "END".
+  float                           relativeLineSize;               ///< The relative line height to be used for this paragaraph.
   bool                            horizontalAlignmentDefined : 1; ///< Whether the horizontal alignment is defined.
+  bool                            relativeLineSizeDefined : 1;    ///< Whether the relative line height is defined for this paragraph.
 };
 
 } // namespace Text
index f4d2ca4..d89adf9 100644 (file)
@@ -2109,6 +2109,27 @@ int Decorator::GetCursorWidth() const
 void Decorator::SetEditable(bool editable)
 {
   mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
+  // If editable is false, all decorators should be disabled.
+  if(!editable)
+  {
+    if(IsHighlightActive())
+    {
+      SetHighlightActive(false);
+    }
+    if(IsHandleActive(LEFT_SELECTION_HANDLE))
+    {
+      SetHandleActive(LEFT_SELECTION_HANDLE, false);
+    }
+    if(IsHandleActive(RIGHT_SELECTION_HANDLE))
+    {
+      SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+    }
+    if(IsPopupActive())
+    {
+      SetPopupActive(false);
+    }
+  }
+
   mImpl->Relayout(mImpl->mControlSize);
 }
 /** Handles **/
index 3fcc82c..41e41a9 100644 (file)
@@ -97,7 +97,8 @@ struct LineLayout
     glyphIndexInSecondHalfLine{0u},
     characterIndexInSecondHalfLine{0u},
     numberOfGlyphsInSecondHalfLine{0u},
-    numberOfCharactersInSecondHalfLine{0u}
+    numberOfCharactersInSecondHalfLine{0u},
+    relativeLineSize{1.0f}
 
   {
   }
@@ -120,6 +121,7 @@ struct LineLayout
     characterIndexInSecondHalfLine     = 0u;
     numberOfGlyphsInSecondHalfLine     = 0u;
     numberOfCharactersInSecondHalfLine = 0u;
+    relativeLineSize                   = 1.0f;
   }
 
   GlyphIndex         glyphIndex;                ///< Index of the first glyph to be laid-out.
@@ -140,6 +142,8 @@ struct LineLayout
   CharacterIndex characterIndexInSecondHalfLine;     ///< Index of the first character to be laid-out for the second half of line.
   Length         numberOfGlyphsInSecondHalfLine;     ///< The number of glyph which fit in one line for the second half of line.
   Length         numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
+
+  float relativeLineSize; ///< The relative line size to be applied for this line.
 };
 
 struct LayoutBidiParameters
@@ -173,9 +177,10 @@ struct Engine::Impl
    * @brief get the line spacing.
    *
    * @param[in] textSize The text size.
+   * @param[in] relativeLineSize The relative line size to be applied.
    * @return the line spacing value.
    */
-  float GetLineSpacing(float textSize)
+  float GetLineSpacing(float textSize, float relativeLineSize)
   {
     float lineSpacing;
     float relTextSize;
@@ -188,10 +193,10 @@ struct Engine::Impl
     lineSpacing += mDefaultLineSpacing;
 
     //subtract line spcaing if relativeLineSize < 1 & larger than min height
-    relTextSize = textSize * mRelativeLineSize;
+    relTextSize = textSize * relativeLineSize;
     if(relTextSize > mDefaultLineSize)
     {
-      if(mRelativeLineSize < 1)
+      if(relativeLineSize < 1)
       {
         //subtract the difference (always will be positive)
         lineSpacing -= (textSize - relTextSize);
@@ -240,7 +245,7 @@ struct Engine::Impl
     // Sets the minimum descender.
     lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
 
-    lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender);
+    lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender, lineLayout.relativeLineSize);
   }
 
   /**
@@ -740,6 +745,8 @@ struct Engine::Impl
     // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
     tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
 
+    tmpLineLayout.relativeLineSize = lineLayout.relativeLineSize;
+
     // Calculate the line height if there is no characters.
     FontId lastFontId = glyphMetrics.fontId;
     UpdateLineHeight(glyphMetrics, tmpLineLayout);
@@ -1268,6 +1275,9 @@ struct Engine::Impl
 
       LineRun*   lineRun = nullptr;
       LineLayout ellipsisLayout;
+
+      ellipsisLayout.relativeLineSize = layout.relativeLineSize;
+
       if(0u != numberOfLines)
       {
         // Get the last line and layout it again with the 'completelyFill' flag to true.
@@ -1424,7 +1434,7 @@ struct Engine::Impl
     lineRun.direction = layout.direction;
     lineRun.ellipsis  = false;
 
-    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
+    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
 
     // Update the actual size.
     if(lineRun.width > layoutSize.width)
@@ -1478,7 +1488,11 @@ struct Engine::Impl
     lineRun.direction                       = LTR;
     lineRun.ellipsis                        = false;
 
-    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
+    BoundedParagraphRun currentParagraphRun;
+    LineLayout          tempLineLayout;
+    (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
+
+    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
 
     layoutSize.height += GetLineHeight(lineRun, true);
   }
@@ -1538,6 +1552,51 @@ struct Engine::Impl
     }
   }
 
+  /**
+   * @brief Sets the relative line size for the LineLayout
+   *
+   * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
+   * @param[in,out] lineLayout The line layout to be updated.
+   */
+  void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
+  {
+    lineLayout.relativeLineSize = mRelativeLineSize;
+
+    if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
+    {
+      lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
+    }
+  }
+
+  /**
+   * @brief Get the bounded paragraph for the characterIndex if exists.
+   *
+   * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
+   * @param[in] characterIndex The character index to get bounded paragraph for.
+   * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
+   *
+   * @return returns true if a bounded paragraph was found.
+   */
+  bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
+  {
+    for(Vector<BoundedParagraphRun>::Iterator it    = boundedParagraphRuns.Begin(),
+                                              endIt = boundedParagraphRuns.End();
+        it != endIt;
+        ++it)
+    {
+      BoundedParagraphRun& tempParagraphRun = *it;
+
+      if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
+         characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
+      {
+        currentParagraphRun = tempParagraphRun;
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   bool LayoutText(Parameters&                       layoutParameters,
                   Size&                             layoutSize,
                   bool                              elideTextEnabled,
@@ -1556,7 +1615,8 @@ struct Engine::Impl
     layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
     layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
 
-    Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
+    Vector<LineRun>&                   lines                = layoutParameters.textModel->mVisualModel->mLines;
+    const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
 
     if(0u == layoutParameters.numberOfGlyphs)
     {
@@ -1616,7 +1676,7 @@ struct Engine::Impl
     // Retrieve BiDi info.
     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
 
-    const CharacterIndex* const                  glyphsToCharactersBuffer    = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
+    const CharacterIndex* const                  glyphsToCharactersBuffer    = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
     const Vector<BidirectionalLineInfoRun>&      bidirectionalLinesInfo      = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
 
@@ -1720,6 +1780,10 @@ struct Engine::Impl
       LineLayout layout;
       layout.direction  = layoutBidiParameters.paragraphDirection;
       layout.glyphIndex = index;
+
+      BoundedParagraphRun currentParagraphRun;
+      (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
+
       GetLineLayoutForBox(layoutParameters,
                           layoutBidiParameters,
                           layout,
@@ -1734,6 +1798,14 @@ struct Engine::Impl
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters);
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                length %f\n", layout.length);
 
+      CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
+
+      //check if this is the last line in paragraph, if false we should use the default relative line size (the one set using the property)
+      if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex+layout.numberOfCharacters)
+      {
+        layout.relativeLineSize = mRelativeLineSize;
+      }
+
       if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
       {
         // The width is too small and no characters are laid-out.
@@ -1877,7 +1949,7 @@ struct Engine::Impl
         }
 
         // Updates the vertical pen's position.
-        penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
+        penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
 
         // Increase the glyph index.
         index = nextIndex;
index 4c4bb29..771ff4e 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/anchor.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -32,18 +33,13 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_HREF_ATTRIBUTE("href");
-} // namespace
-
 void ProcessAnchor(const Tag& tag, Anchor& anchor)
 {
   anchor.href = nullptr;
 
   for(auto&& attribute : tag.attributes)
   {
-    if(TokenComparison(XHTML_HREF_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::ANCHOR_ATTRIBUTES::HREF, attribute.nameBuffer, attribute.nameLength))
     {
       Length hrefLength = attribute.valueLength + 1;
       anchor.href       = new char[hrefLength];
index 8a9910e..7f0e8ae 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/color-run.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -32,16 +33,11 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_COLOR_ATTRIBUTE("color");
-} // namespace
-
 void ProcessBackground(const Tag& tag, ColorRun& colorRun)
 {
   for(auto&& attribute : tag.attributes)
   {
-    if(TokenComparison(XHTML_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::BACKGROUND_ATTRIBUTES::COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       ColorStringToVector4(attribute.valueBuffer, attribute.valueLength, colorRun.color);
     }
index f0ef362..cce30dd 100644 (file)
@@ -25,6 +25,7 @@
 #include <dali-toolkit/internal/text/character-spacing-character-run.h>
 #include <dali-toolkit/internal/text/markup-processor-attribute-helper-functions.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -32,12 +33,6 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_VALUE_ATTRIBUTE("value");
-
-} // namespace
-
 void ProcessValueAttribute(const Attribute& attribute, CharacterSpacingCharacterRun& characterSpacingCharacterRun)
 {
   characterSpacingCharacterRun.value = ProcessFloatAttribute(attribute);
@@ -52,7 +47,7 @@ void ProcessCharacterSpacingTag(const Tag& tag, CharacterSpacingCharacterRun& ch
   {
     const Attribute& attribute(*it);
 
-    if(TokenComparison(XHTML_VALUE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::CHARACTER_SPACING_ATTRIBUTES::VALUE, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessValueAttribute(attribute, characterSpacingCharacterRun);
     }
index d5fce04..4fb95ef 100644 (file)
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/color-run.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -31,11 +32,6 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_VALUE_ATTRIBUTE("value");
-}
-
 void ProcessColor(const Attribute& attribute, ColorRun& colorRun)
 {
   ColorStringToVector4(attribute.valueBuffer, attribute.valueLength, colorRun.color);
@@ -49,7 +45,7 @@ void ProcessColorTag(const Tag& tag, ColorRun& colorRun)
       ++it)
   {
     const Attribute& attribute(*it);
-    if(TokenComparison(XHTML_VALUE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::COLOR_ATTRIBUTES::VALUE, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessColor(attribute, colorRun);
     }
index 1bfa3f4..94d6fd4 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/embedded-item.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -34,11 +35,6 @@ namespace Text
 {
 namespace
 {
-const std::string XHTML_URL_ATTRIBUTE("url");
-const std::string XHTML_WIDTH_ATTRIBUTE("width");
-const std::string XHTML_HEIGHT_ATTRIBUTE("height");
-const std::string XHTML_COLOR_BLENDING_ATTRIBUTE("color-blending");
-
 const std::string NONE("none");
 const std::string MULTIPLY("multiply");
 } // namespace
@@ -57,22 +53,22 @@ void ProcessEmbeddedItem(const Tag& tag, EmbeddedItem& embeddedItem)
       ++it)
   {
     const Attribute& attribute(*it);
-    if(TokenComparison(XHTML_URL_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::EMBEDDED_ITEM_ATTRIBUTES::URL, attribute.nameBuffer, attribute.nameLength))
     {
       embeddedItem.urlLength = attribute.valueLength;
       embeddedItem.url       = new char[embeddedItem.urlLength];
       memcpy(embeddedItem.url, attribute.valueBuffer, embeddedItem.urlLength);
       // The memory is freed when the font run is removed from the logical model.
     }
-    else if(TokenComparison(XHTML_WIDTH_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::EMBEDDED_ITEM_ATTRIBUTES::WIDTH, attribute.nameBuffer, attribute.nameLength))
     {
       embeddedItem.width = StringToUint(attribute.valueBuffer);
     }
-    else if(TokenComparison(XHTML_HEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::EMBEDDED_ITEM_ATTRIBUTES::HEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       embeddedItem.height = StringToUint(attribute.valueBuffer);
     }
-    else if(TokenComparison(XHTML_COLOR_BLENDING_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::EMBEDDED_ITEM_ATTRIBUTES::COLOR_BLENDING, attribute.nameBuffer, attribute.nameLength))
     {
       if(TokenComparison(MULTIPLY, attribute.valueBuffer, attribute.valueLength))
       {
index 0c469b8..802f9e9 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali-toolkit/internal/text/font-description-run.h>
 #include <dali-toolkit/internal/text/markup-processor-attribute-helper-functions.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
 
 namespace Dali
@@ -36,12 +37,6 @@ namespace Text
 {
 namespace
 {
-const std::string XHTML_FAMILY_ATTRIBUTE("family");
-const std::string XHTML_SIZE_ATTRIBUTE("size");
-const std::string XHTML_WEIGHT_ATTRIBUTE("weight");
-const std::string XHTML_WIDTH_ATTRIBUTE("width");
-const std::string XHTML_SLANT_ATTRIBUTE("slant");
-
 const std::string  FONT_PREFIX("font-");
 const unsigned int FONT_PREFIX_LENGTH      = 5u;
 const unsigned int MIN_FONT_ATTRIBUTE_SIZE = 4u;   ///< The minimum length of any of the possible 'weight', 'width' , 'slant' or 'size' values.
@@ -97,23 +92,23 @@ void ProcessFontTag(const Tag& tag, FontDescriptionRun& fontRun)
   {
     const Attribute& attribute(*it);
 
-    if(TokenComparison(XHTML_FAMILY_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::FONT_ATTRIBUTES::FAMILY, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessFontFamily(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_SIZE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::FONT_ATTRIBUTES::SIZE, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessFontSize(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_WEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::FONT_ATTRIBUTES::WEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessFontWeight(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_WIDTH_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::FONT_ATTRIBUTES::WIDTH, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessFontWidth(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_SLANT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::FONT_ATTRIBUTES::SLANT, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessFontSlant(attribute, fontRun);
     }
index 35a1cc5..e470bec 100644 (file)
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -31,11 +32,6 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_ALIGN_ATTRIBUTE("align");
-}
-
 void ProcessHorizontalAlignment(const Attribute& attribute, BoundedParagraphRun& boundedParagraphRun)
 {
   boundedParagraphRun.horizontalAlignmentDefined = HorizontalAlignmentTypeStringToTypeValue(attribute.valueBuffer,
@@ -43,6 +39,12 @@ void ProcessHorizontalAlignment(const Attribute& attribute, BoundedParagraphRun&
                                                                                             boundedParagraphRun.horizontalAlignment);
 }
 
+void ProcessRelativeLineHeight(const Attribute& attribute, BoundedParagraphRun& boundedParagraphRun)
+{
+  boundedParagraphRun.relativeLineSize        = StringToFloat(attribute.valueBuffer);
+  boundedParagraphRun.relativeLineSizeDefined = true;
+}
+
 void ProcessAttributesOfParagraphTag(const Tag& tag, BoundedParagraphRun& boundedParagraphRun)
 {
   // By default the align attribute is not defined until it's parsed.
@@ -54,13 +56,16 @@ void ProcessAttributesOfParagraphTag(const Tag& tag, BoundedParagraphRun& bounde
       ++it)
   {
     const Attribute& attribute(*it);
-    if(TokenComparison(XHTML_ALIGN_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::PARAGRAPH_ATTRIBUTES::ALIGN, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessHorizontalAlignment(attribute, boundedParagraphRun);
     }
+    else if(TokenComparison(MARKUP::PARAGRAPH_ATTRIBUTES::RELATIVE_LINE_HEIGHT, attribute.nameBuffer, attribute.nameLength))
+    {
+      ProcessRelativeLineHeight(attribute, boundedParagraphRun);
+    }
   }
 }
-
 } // namespace Text
 
 } // namespace Toolkit
index 484b3a5..2859794 100644 (file)
@@ -44,6 +44,14 @@ void ProcessHorizontalAlignment(const Attribute& attribute, BoundedParagraphRun&
  */
 void ProcessAttributesOfParagraphTag(const Tag& tag, BoundedParagraphRun& boundedParagraphRun);
 
+/**
+ * @brief Retrieves the relative line height value from the paragraph tag and sets it to the bounded paragraph run.
+ *
+ * @param[in] attribute the relative line height attribute.
+ * @param[in,out] boundedParagraphRun The bounded paragraph run.
+ */
+void ProcessRelativeLineHeight(const Attribute& attribute, BoundedParagraphRun& boundedParagraphRun);
+
 } // namespace Text
 
 } // namespace Toolkit
index 19810ee..3e20c6f 100644 (file)
@@ -29,6 +29,7 @@
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
 #include <dali-toolkit/internal/text/markup-processor-strikethrough.h>
 #include <dali-toolkit/internal/text/markup-processor-underline.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 
 namespace Dali
 {
@@ -36,35 +37,6 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_FAMILY_ATTRIBUTE("font-family");
-const std::string XHTML_SIZE_ATTRIBUTE("font-size");
-const std::string XHTML_WEIGHT_ATTRIBUTE("font-weight");
-const std::string XHTML_WIDTH_ATTRIBUTE("font-width");
-const std::string XHTML_SLANT_ATTRIBUTE("font-slant");
-
-const std::string XHTML_COLOR_ATTRIBUTE("text-color");
-const std::string XHTML_BACKGROUND_COLOR_ATTRIBUTE("background-color");
-
-//the underlined character's attributes
-const std::string XHTML_UNDERLINE_COLOR_ATTRIBUTE("u-color");
-const std::string XHTML_UNDERLINE_HEIGHT_ATTRIBUTE("u-height");
-const std::string XHTML_UNDERLINE_TYPE_ATTRIBUTE("u-type");
-const std::string XHTML_UNDERLINE_DASH_GAP_ATTRIBUTE("u-dash-gap");
-const std::string XHTML_UNDERLINE_DASH_WIDTH_ATTRIBUTE("u-dash-width");
-
-//the strikethroughed character's attributes
-const std::string XHTML_STRIKETHROUGH_COLOR_ATTRIBUTE("s-color");
-const std::string XHTML_STRIKETHROUGH_HEIGHT_ATTRIBUTE("s-height");
-
-//the character-spacing character's attributes
-const std::string XHTML_CHARACTER_SPACING_VALUE_ATTRIBUTE("char-space-value");
-
-//NOTE: the MAX_NUM_OF_ATTRIBUTES in "markup-processor.cpp" should be updated when add a new attribute for span tag.
-
-} // namespace
-
 void ProcessSpanTag(const Tag&                    tag,
                     ColorRun&                     colorRun,
                     FontDescriptionRun&           fontRun,
@@ -86,77 +58,77 @@ void ProcessSpanTag(const Tag&                    tag,
   {
     const Attribute& attribute(*it);
 
-    if(TokenComparison(XHTML_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::TEXT_COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       isColorDefined = true;
       ProcessColor(attribute, colorRun);
     }
-    else if(TokenComparison(XHTML_BACKGROUND_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::BACKGROUND_COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       isBackgroundColorDefined = true;
       ProcessColor(attribute, backgroundColorRun);
     }
-    else if(TokenComparison(XHTML_FAMILY_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::FONT_FAMILY, attribute.nameBuffer, attribute.nameLength))
     {
       isFontDefined = true;
       ProcessFontFamily(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_SIZE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::FONT_SIZE, attribute.nameBuffer, attribute.nameLength))
     {
       isFontDefined = true;
       ProcessFontSize(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_WEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::FONT_WEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       isFontDefined = true;
       ProcessFontWeight(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_WIDTH_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::FONT_WIDTH, attribute.nameBuffer, attribute.nameLength))
     {
       isFontDefined = true;
       ProcessFontWidth(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_SLANT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::FONT_SLANT, attribute.nameBuffer, attribute.nameLength))
     {
       isFontDefined = true;
       ProcessFontSlant(attribute, fontRun);
     }
-    else if(TokenComparison(XHTML_UNDERLINE_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::UNDERLINE_COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       isUnderlinedCharacterDefined = true;
       ProcessColorAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_UNDERLINE_HEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::UNDERLINE_HEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       isUnderlinedCharacterDefined = true;
       ProcessHeightAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_UNDERLINE_TYPE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::UNDERLINE_TYPE, attribute.nameBuffer, attribute.nameLength))
     {
       isUnderlinedCharacterDefined = true;
       ProcessTypeAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_UNDERLINE_DASH_GAP_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::UNDERLINE_DASH_GAP, attribute.nameBuffer, attribute.nameLength))
     {
       isUnderlinedCharacterDefined = true;
       ProcessDashGapAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_UNDERLINE_DASH_WIDTH_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::UNDERLINE_DASH_WIDTH, attribute.nameBuffer, attribute.nameLength))
     {
       isUnderlinedCharacterDefined = true;
       ProcessDashWidthAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_STRIKETHROUGH_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::STRIKETHROUGH_COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       isStrikethroughDefined = true;
       ProcessColorAttribute(attribute, strikethroughRun);
     }
-    else if(TokenComparison(XHTML_STRIKETHROUGH_HEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::STRIKETHROUGH_HEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       isStrikethroughDefined = true;
       ProcessHeightAttribute(attribute, strikethroughRun);
     }
-    else if(TokenComparison(XHTML_CHARACTER_SPACING_VALUE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::SPAN_ATTRIBUTES::CHARACTER_SPACING_VALUE, attribute.nameBuffer, attribute.nameLength))
     {
       isCharacterSpacingDefined = true;
       ProcessValueAttribute(attribute, characterSpacingCharacterRun);
index 2f0ea5d..10e104c 100644 (file)
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/markup-processor-attribute-helper-functions.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 #include <dali-toolkit/internal/text/strikethrough-character-run.h>
 
 namespace Dali
@@ -32,12 +33,6 @@ namespace Toolkit
 {
 namespace Text
 {
-namespace
-{
-const std::string XHTML_COLOR_ATTRIBUTE("color");
-const std::string XHTML_HEIGHT_ATTRIBUTE("height");
-} // namespace
-
 void ProcessColorAttribute(const Attribute& attribute, StrikethroughCharacterRun& strikethroughRun)
 
 {
@@ -60,11 +55,11 @@ void ProcessStrikethroughTag(const Tag& tag, StrikethroughCharacterRun& striketh
   {
     const Attribute& attribute(*it);
 
-    if(TokenComparison(XHTML_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::STRIKETHROUGH_ATTRIBUTES::COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessColorAttribute(attribute, strikethroughRun);
     }
-    else if(TokenComparison(XHTML_HEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::STRIKETHROUGH_ATTRIBUTES::HEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessHeightAttribute(attribute, strikethroughRun);
     }
index 8f19b5a..08b9f1e 100644 (file)
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/markup-processor-attribute-helper-functions.h>
 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/underlined-character-run.h>
 
@@ -35,12 +36,6 @@ namespace Text
 {
 namespace
 {
-const std::string XHTML_COLOR_ATTRIBUTE("color");
-const std::string XHTML_HEIGHT_ATTRIBUTE("height");
-const std::string XHTML_TYPE_ATTRIBUTE("type");
-const std::string XHTML_DASH_GAP_ATTRIBUTE("dash-gap");
-const std::string XHTML_DASH_WIDTH_ATTRIBUTE("dash-width");
-
 const unsigned int MAX_TYPE_ATTRIBUTE_SIZE = 7u; ///< The maximum length of any of the possible 'type' values.
 
 } // namespace
@@ -85,23 +80,23 @@ void ProcessUnderlineTag(const Tag& tag, UnderlinedCharacterRun& underlinedChara
   {
     const Attribute& attribute(*it);
 
-    if(TokenComparison(XHTML_COLOR_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    if(TokenComparison(MARKUP::UNDERLINE_ATTRIBUTES::COLOR, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessColorAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_HEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::UNDERLINE_ATTRIBUTES::HEIGHT, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessHeightAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_TYPE_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::UNDERLINE_ATTRIBUTES::TYPE, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessTypeAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_DASH_GAP_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::UNDERLINE_ATTRIBUTES::DASH_GAP, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessDashGapAttribute(attribute, underlinedCharacterRun);
     }
-    else if(TokenComparison(XHTML_DASH_WIDTH_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    else if(TokenComparison(MARKUP::UNDERLINE_ATTRIBUTES::DASH_WIDTH, attribute.nameBuffer, attribute.nameLength))
     {
       ProcessDashWidthAttribute(attribute, underlinedCharacterRun);
     }
index 1df19ca..60eb03b 100644 (file)
@@ -36,6 +36,7 @@
 #include <dali-toolkit/internal/text/markup-processor-span.h>
 #include <dali-toolkit/internal/text/markup-processor-strikethrough.h>
 #include <dali-toolkit/internal/text/markup-processor-underline.h>
+#include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
 #include <dali-toolkit/internal/text/xhtml-entities.h>
 
 namespace Dali
@@ -46,25 +47,6 @@ namespace Text
 {
 namespace
 {
-// HTML-ISH tag and attribute constants.
-// Note they must be lower case in order to make the comparison to work
-// as the parser converts all the read tags to lower case.
-const std::string XHTML_COLOR_TAG("color");
-const std::string XHTML_FONT_TAG("font");
-const std::string XHTML_B_TAG("b");
-const std::string XHTML_I_TAG("i");
-const std::string XHTML_U_TAG("u");
-const std::string XHTML_SHADOW_TAG("shadow");
-const std::string XHTML_GLOW_TAG("glow");
-const std::string XHTML_OUTLINE_TAG("outline");
-const std::string XHTML_ITEM_TAG("item");
-const std::string XHTML_ANCHOR_TAG("a");
-const std::string XHTML_BACKGROUND_TAG("background");
-const std::string XHTML_SPAN_TAG("span");
-const std::string XHTML_STRIKETHROUGH_TAG("s");
-const std::string XHTML_PARAGRAPH_TAG("p");
-const std::string XHTML_CHARACTER_SPACING_TAG("char-spacing");
-
 const char LESS_THAN      = '<';
 const char GREATER_THAN   = '>';
 const char EQUAL          = '=';
@@ -1117,12 +1099,12 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
              markupStringEndBuffer,
              tag))
     {
-      if(TokenComparison(XHTML_COLOR_TAG, tag.buffer, tag.length))
+      if(TokenComparison(MARKUP::TAG::COLOR, tag.buffer, tag.length))
       {
         ProcessTagForRun<ColorRun>(
           markupProcessData.colorRuns, styleStack, tag, characterIndex, colorRunIndex, colorTagReference, [](const Tag& tag, ColorRun& run) { ProcessColorTag(tag, run); });
       } // <color></color>
-      else if(TokenComparison(XHTML_I_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::ITALIC, tag.buffer, tag.length))
       {
         ProcessTagForRun<FontDescriptionRun>(
           markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, iTagReference, [](const Tag&, FontDescriptionRun& fontRun) {
@@ -1130,12 +1112,12 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
             fontRun.slantDefined = true;
           });
       } // <i></i>
-      else if(TokenComparison(XHTML_U_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::UNDERLINE, tag.buffer, tag.length))
       {
         ProcessTagForRun<UnderlinedCharacterRun>(
           markupProcessData.underlinedCharacterRuns, styleStack, tag, characterIndex, underlinedCharacterRunIndex, uTagReference, [](const Tag& tag, UnderlinedCharacterRun& run) { ProcessUnderlineTag(tag, run); });
       } // <u></u>
-      else if(TokenComparison(XHTML_B_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::BOLD, tag.buffer, tag.length))
       {
         ProcessTagForRun<FontDescriptionRun>(
           markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, bTagReference, [](const Tag&, FontDescriptionRun& fontRun) {
@@ -1143,12 +1125,12 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
             fontRun.weightDefined = true;
           });
       } // <b></b>
-      else if(TokenComparison(XHTML_FONT_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::FONT, tag.buffer, tag.length))
       {
         ProcessTagForRun<FontDescriptionRun>(
           markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, fontTagReference, [](const Tag& tag, FontDescriptionRun& fontRun) { ProcessFontTag(tag, fontRun); });
       } // <font></font>
-      else if(TokenComparison(XHTML_ANCHOR_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::ANCHOR, tag.buffer, tag.length))
       {
         /* Anchor */
         ProcessAnchorTag(markupProcessData, tag, characterIndex);
@@ -1166,31 +1148,31 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
             ProcessUnderlineTag(tag, run);
           });
       } // <a href=https://www.tizen.org>tizen</a>
-      else if(TokenComparison(XHTML_SHADOW_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::SHADOW, tag.buffer, tag.length))
       {
         // TODO: If !tag.isEndTag, then create a new shadow run.
         //       else Pop the top of the stack and set the number of characters of the run.
       } // <shadow></shadow>
-      else if(TokenComparison(XHTML_GLOW_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::GLOW, tag.buffer, tag.length))
       {
         // TODO: If !tag.isEndTag, then create a new glow run.
         //       else Pop the top of the stack and set the number of characters of the run.
       } // <glow></glow>
-      else if(TokenComparison(XHTML_OUTLINE_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::OUTLINE, tag.buffer, tag.length))
       {
         // TODO: If !tag.isEndTag, then create a new outline run.
         //       else Pop the top of the stack and set the number of characters of the run.
       } // <outline></outline>
-      else if(TokenComparison(XHTML_ITEM_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::EMBEDDED_ITEM, tag.buffer, tag.length))
       {
         ProcessItemTag(markupProcessData, tag, characterIndex);
       }
-      else if(TokenComparison(XHTML_BACKGROUND_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::BACKGROUND, tag.buffer, tag.length))
       {
         ProcessTagForRun<ColorRun>(
           markupProcessData.backgroundColorRuns, styleStack, tag, characterIndex, backgroundRunIndex, backgroundTagReference, [](const Tag& tag, ColorRun& run) { ProcessBackground(tag, run); });
       }
-      else if(TokenComparison(XHTML_SPAN_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::SPAN, tag.buffer, tag.length))
       {
         ProcessSpanForRun(tag,
                           spanStack,
@@ -1209,18 +1191,18 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
                           characterIndex,
                           spanTagReference);
       }
-      else if(TokenComparison(XHTML_STRIKETHROUGH_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::STRIKETHROUGH, tag.buffer, tag.length))
       {
         ProcessTagForRun<StrikethroughCharacterRun>(
           markupProcessData.strikethroughCharacterRuns, styleStack, tag, characterIndex, strikethroughCharacterRunIndex, sTagReference, [](const Tag& tag, StrikethroughCharacterRun& run) { ProcessStrikethroughTag(tag, run); });
       } // <s></s>
-      else if(TokenComparison(XHTML_PARAGRAPH_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::PARAGRAPH, tag.buffer, tag.length))
       {
         ProcessParagraphTag(markupProcessData, tag, (markupStringBuffer == markupStringEndBuffer), characterIndex);
         ProcessTagForRun<BoundedParagraphRun>(
           markupProcessData.boundedParagraphRuns, styleStack, tag, characterIndex, boundedParagraphRunIndex, pTagReference, [](const Tag& tag, BoundedParagraphRun& run) { ProcessAttributesOfParagraphTag(tag, run); });
       } // <p></p>
-      else if(TokenComparison(XHTML_CHARACTER_SPACING_TAG, tag.buffer, tag.length))
+      else if(TokenComparison(MARKUP::TAG::CHARACTER_SPACING, tag.buffer, tag.length))
       {
         ProcessTagForRun<CharacterSpacingCharacterRun>(
           markupProcessData.characterSpacingCharacterRuns, styleStack, tag, characterIndex, characterSpacingCharacterRunIndex, characterSpacingTagReference, [](const Tag& tag, CharacterSpacingCharacterRun& run) { ProcessCharacterSpacingTag(tag, run); });
diff --git a/dali-toolkit/internal/text/markup-tags-and-attributes.h b/dali-toolkit/internal/text/markup-tags-and-attributes.h
new file mode 100644 (file)
index 0000000..0f75d6a
--- /dev/null
@@ -0,0 +1,719 @@
+#ifndef DALI_TOOLKIT_TEXT_MARKUPS_AND_ATTRIBUTES_H
+#define DALI_TOOLKIT_TEXT_MARKUPS_AND_ATTRIBUTES_H
+
+/*
+ * Copyright (c) 2022 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 <string>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+ * @brief Use markup styling to style Text-Controller.
+ * The markup tag is opend by <> and closed by </>
+ *
+ * @details You can use markup elements to change the style of the text.
+ * Since the text controls do not process markup elements by default, you must first set the EnableMarkup property of the Text-Controller  to true:
+ *
+ * @note The markup processor does not check for markup validity, and styles are rendered in priority order.
+ * Incorrect or incompatible elements can cause the text to be rendered incorrectly.
+ */
+namespace MARKUP
+{
+namespace TAG
+{
+// HTML-ISH tag and attribute constants.
+// Note they must be lower case in order to make the comparison to work
+// as the parser converts all the read tags to lower case.
+
+/**
+ * @brief Sets the color for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<color value='red'>Hello world</color>";
+ *
+ * @endcode
+ *
+ * @see COLOR_ATTRIBUTES
+ *
+ */
+static const std::string COLOR("color");
+
+/**
+ * @brief Sets the font values for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<font family='DejaVuSerif' size='18'>Hello world</font>";
+ *
+ * @endcode
+ *
+ * @see FONT_ATTRIBUTES
+ */
+static const std::string FONT("font");
+
+/**
+ * @brief Sets Bold decoration for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<b>Hello world</b>";
+ *
+ * @endcode
+ *
+ * @see
+ */
+static const std::string BOLD("b");
+
+/**
+ * @brief Sets Italic decoration for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<i>Hello world</i>";
+ *
+ * @endcode
+ *
+ */
+static const std::string ITALIC("i");
+
+/**
+ * @brief Sets the underlined values for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<u>Hello world</u>";
+ *
+ * @endcode
+ *
+ * @see UNDERLINE_ATTRIBUTES
+ */
+static const std::string UNDERLINE("u");
+
+/**
+ * @todo Sets the shadow for the characters inside the element.
+ *
+ */
+static const std::string SHADOW("shadow"); ///< This tag under construction.
+
+/**
+ * @todo Sets the glow for the characters inside the element.
+ *
+ */
+static const std::string GLOW("glow"); ///< This tag under construction.
+
+/**
+ * @todo Sets the outline for the characters inside the element.
+ *
+ */
+static const std::string OUTLINE("outline"); ///< This tag under construction.
+
+/**
+ * @brief Defines an embedded item within the text.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<item 'width'=26 'height'=26 'url'='path/image.png' 'color-blending'='multiply'/>";
+ *
+ * @endcode
+ *
+ * @see EMBEDDED_ITEM_ATTRIBUTES
+ */
+static const std::string EMBEDDED_ITEM("item");
+
+/**
+ * @brief Defines a hyperlink for the text inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<a href='https://www.tizen.org'>TIZEN</a>";
+ *
+ * @endcode
+ *
+ * @see ANCHOR_ATTRIBUTES
+ */
+static const std::string ANCHOR("a");
+
+/**
+ * @brief Sets the background color for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<background color='yellow'>Hello world</background>";
+ *
+ * @endcode
+ *
+ * @see BACKGROUND_ATTRIBUTES
+ */
+static const std::string BACKGROUND("background");
+
+/**
+ * @brief Use span tag to set many styles on character's level for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<span font-size='20' font-family='DejaVu Sans' font-width='condensed' font-slant='italic' text-color='green' char-space-value='10.0f'>Hello world</span>";
+ *
+ * @endcode
+ *
+ * @see SPAN_ATTRIBUTES
+ */
+static const std::string SPAN("span");
+
+/**
+ * @brief Sets the strikethrough values for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<s>Hello world</s>";
+ *
+ * @endcode
+ *
+ * @see STRIKETHROUGH_ATTRIBUTES
+ */
+static const std::string STRIKETHROUGH("s");
+
+/**
+ * @brief Use paragraph tag to set many styles on paragraph's level for the lines inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "test before paragraph tag <p>test paragraph tag </p>test after paragraph tag ";
+ *
+ * @endcode
+ *
+ * @see PARAGRAPH_ATTRIBUTES
+ */
+static const std::string PARAGRAPH("p");
+
+/**
+ * @brief Sets the character spacing values for the characters inside the element.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<char-spacing value='3.0f'>Hello world</char-spacing>";
+ *
+ * @endcode
+ *
+ * @see CHARACTER_SPACING_ATTRIBUTES
+ */
+static const std::string CHARACTER_SPACING("char-spacing");
+} // namespace TAG
+
+namespace COLOR_ATTRIBUTES
+{
+/**
+ * @brief Use the value attribute to define the color.
+ * The supported attribute values are red, green, blue, yellow, magenta, cyan, white, black, and transparent.
+ * Web colors and colors represented in 32-bit hexadecimal 0xAARRGGBB format are also supported.
+ *
+ * The following examples show text in red color:
+ * @code
+ *
+ * textController.Text = "<color value='red'>Hello world</color>"; /// Color coded with a text constant
+ *
+ * @endcode
+ *
+ * @code
+ *
+ * textController.Text = "<color value='0xFFFF0000'>Hello world</color>"); /// Color packed inside an ARGB hexadecimal value
+ *
+ * @endcode
+ */
+static const std::string VALUE("value");
+} // namespace COLOR_ATTRIBUTES
+
+namespace FONT_ATTRIBUTES
+{
+/**
+ * @brief Use the family attribute to define the font name.
+ *
+ * Example:
+ * @code
+ *
+ * textController.Text = "<font family='SamsungSans' >Hello world</font>";
+ *
+ * @endcode
+ *
+ */
+static const std::string FAMILY("family");
+
+/**
+ * @brief Use the size attribute to define the font size in points.
+ *
+ * Example:
+ * @code
+ *
+ * textController.Text = "<font size='50' >Hello world</font>";
+ *
+ * @endcode
+ *
+ */
+static const std::string SIZE("size");
+
+/**
+ * @brief Use the weight attribute to define the font weight.
+ *
+ * Example:
+ * @code
+ *
+ * textController.Text = "<font weight='bold' >Hello world</font>";
+ *
+ * @endcode
+ *
+ */
+static const std::string WEIGHT("weight");
+
+/**
+ * @brief Use the width attribute to define the font width.
+ *
+ * Example:
+ * @code
+ *
+ * textController.Text = "<font width='condensed' >Hello world</font>";
+ *
+ * @endcode
+ *
+ */
+static const std::string WIDTH("width");
+
+/**
+ * @brief Use the slant attribute to define the font slant.
+ *
+ * Example:
+ * @code
+ *
+ * textController.Text = "<font slant='italic' >Hello world</font>";
+ *
+ * @endcode
+ *
+ */
+static const std::string SLANT("slant");
+} // namespace FONT_ATTRIBUTES
+
+namespace UNDERLINE_ATTRIBUTES
+{
+/**
+ * @brief Use the color attribute to define the color of underline.
+ * The supported attribute values are red, green, blue, yellow, magenta, cyan, white, black, and transparent.
+ * Web colors and colors represented in 32-bit hexadecimal 0xAARRGGBB format are also supported.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<u color='green'>Hello world</u>";
+ *
+ * @endcode
+ */
+static const std::string COLOR("color");
+
+/**
+ * @brief Use the height attribute to define the height of underline.
+ * It is float value.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<u height='2.0f'>Hello world</u>";
+ *
+ * @endcode
+ */
+static const std::string HEIGHT("height");
+
+/**
+ * @brief Use the type attribute to define the type of underline.
+ * The supported attribute values are solid, dashed and double
+ * The default value is solid
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<u type='double'>Hello world</u>";
+ *
+ * @endcode
+ */
+static const std::string TYPE("type");
+
+/**
+ * @brief Use the dash-gap attribute to define the dash-gap of underline.
+ * The gap in pixels between the dashes of the dashed underline. Only valid when "DASHED" underline type is used.
+ *
+ * It is float value.
+ * @note If not provided then the default gap is used (1 pixel).
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<u type='dashed' dash-gap='2.0f'>Hello world</u>";
+ *
+ * @endcode
+ */
+static const std::string DASH_GAP("dash-gap");
+
+/**
+ * @brief Use the dash-width attribute to define the dash-width of underline.
+ * The width in pixels of the dashes of the dashed underline. Only valid when "DASHED" underline type is used.
+ * It is float value.
+ * @note If not provided then the default width is used (2 pixel).
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<u type='dashed' dash-width='3.0f'>Hello world</u>";
+ *
+ * @endcode
+ */
+static const std::string DASH_WIDTH("dash-width");
+
+} // namespace UNDERLINE_ATTRIBUTES
+
+namespace SPAN_ATTRIBUTES
+{
+//NOTE: the MAX_NUM_OF_ATTRIBUTES in "markup-processor.cpp" should be updated when add a new attribute for span tag.
+
+/**
+ * @brief The font family attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span font-family='SamsungSans' >Hello world</span>";
+ * @endcode
+ * @see FONT_ATTRIBUTES::FAMILY
+ */
+static const std::string FONT_FAMILY("font-family");
+
+/**
+ * @brief The font size attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span font-size='50' >Hello world</span>";
+ * @endcode
+ * @see FONT_ATTRIBUTES::SIZE
+ */
+static const std::string FONT_SIZE("font-size");
+
+/**
+ * @brief The font weight attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span font-weight='bold' >Hello world</span>";
+ * @endcode
+ * @see FONT_ATTRIBUTES::WEIGHT
+ */
+static const std::string FONT_WEIGHT("font-weight");
+
+/**
+ * @brief The font width attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span font-width='condensed' >Hello world</span>";
+ * @endcode
+ * @see FONT_ATTRIBUTES::WIDTH
+ */
+static const std::string FONT_WIDTH("font-width");
+
+/**
+ * @brief The font slant attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span font-slant='italic' >Hello world</span>";
+ * @endcode
+ * @see FONT_ATTRIBUTES::SLANT
+ */
+static const std::string FONT_SLANT("font-slant");
+
+/**
+ * @brief The color value attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span text-color='blue' >Hello world</span>";
+ * @endcode
+ * @see COLOR_ATTRIBUTES::VALUE
+ */
+static const std::string TEXT_COLOR("text-color");
+
+/**
+ * @brief The background color attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span background-color='yellow' >Hello world</span>";
+ * @endcode
+ * @see BACKGROUND_ATTRIBUTES::COLOR
+ */
+static const std::string BACKGROUND_COLOR("background-color");
+
+/**
+ * @brief The undeline color attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span u-color='green' >Hello world</span>";
+ * @endcode
+ * @see UNDERLINE_ATTRIBUTES::COLOR
+ */
+static const std::string UNDERLINE_COLOR("u-color");
+
+/**
+ * @brief The undeline height attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span u-height='3.0f' >Hello world</span>";
+ * @endcode
+ * @see UNDERLINE_ATTRIBUTES::HEIGHT
+ */
+static const std::string UNDERLINE_HEIGHT("u-height");
+
+/**
+ * @brief The undeline type attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span u-type='solid' >Hello world</span>";
+ * @endcode
+ * @see UNDERLINE_ATTRIBUTES::TYPE
+ */
+static const std::string UNDERLINE_TYPE("u-type");
+
+/**
+ * @brief The undeline dash-gap attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span u-type='dashed' u-dash-gap='2.0f' >Hello world</span>";
+ * @endcode
+ * @see UNDERLINE_ATTRIBUTES::DASH_GAP
+ */
+static const std::string UNDERLINE_DASH_GAP("u-dash-gap");
+
+/**
+ * @brief The undeline dash-width attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span u-type='dashed' u-dash-width='4.0f' >Hello world</span>";
+ * @endcode
+ * @see UNDERLINE_ATTRIBUTES::DASH_WIDTH
+ */
+static const std::string UNDERLINE_DASH_WIDTH("u-dash-width");
+
+/**
+ * @brief The strikethrough color attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span s-color='green' >Hello world</span>";
+ * @endcode
+ * @see STRIKETHROUGH_ATTRIBUTES::COLOR
+ */
+static const std::string STRIKETHROUGH_COLOR("s-color");
+
+/**
+ * @brief The strikethrough height attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span s-height='3.0f' >Hello world</span>";
+ * @endcode
+ * @see STRIKETHROUGH_ATTRIBUTES::HEIGHT
+ */
+static const std::string STRIKETHROUGH_HEIGHT("s-height");
+
+/**
+ * @brief The character-spacing value attribute.
+ *
+ * Example:
+ * @code
+ * textController.Text = "<span char-space-value='5.0f' >Hello world</span>";
+ * @endcode
+ * @see CHARACTER_SPACING_ATTRIBUTES::VALUE
+ */
+static const std::string CHARACTER_SPACING_VALUE("char-space-value");
+} // namespace SPAN_ATTRIBUTES
+
+namespace STRIKETHROUGH_ATTRIBUTES
+{
+/**
+ * @brief Use the color attribute to define the color of strikethrough.
+ * The supported attribute values are red, green, blue, yellow, magenta, cyan, white, black, and transparent.
+ * Web colors and colors represented in 32-bit hexadecimal 0xAARRGGBB format are also supported.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<s color='green'>Hello world</s>";
+ *
+ * @endcode
+ */
+static const std::string COLOR("color");
+
+/**
+ * @brief Use the height attribute to define the height of strikethrough.
+ * It is float value.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<s height='2.0f'>Hello world</s>";
+ *
+ * @endcode
+ */
+static const std::string HEIGHT("height");
+} // namespace STRIKETHROUGH_ATTRIBUTES
+
+namespace PARAGRAPH_ATTRIBUTES
+{
+/**
+ * @brief Use the align attribute to define the horizontal alignment of paragraph.
+ * The supported attribute values are begin, center and end .
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = ""text outside<p align='end'>Paragraph end</p>text outside<p align='center'>Paragraph center</p>text outside<p align='begin' >Paragraph begin</p>";
+ *
+ * @endcode
+ */
+static const std::string ALIGN("align");
+
+/**
+ * @brief Use the rrel-line-height attribute to define the relative height of the line (a factor that will be multiplied by text height).
+ * It is float value.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<p>line 1</p><p rel-line-height=0.5>line\n2</p>line 3<p rel-line-height=3>line\n4</p>line 5";
+ *
+ * @endcode
+ * @note If the value is less than 1, the lines could to be overlapped.
+ */
+static const std::string RELATIVE_LINE_HEIGHT("rel-line-height");
+
+} // namespace PARAGRAPH_ATTRIBUTES
+
+namespace CHARACTER_SPACING_ATTRIBUTES
+{
+/**
+ * @brief Use the value attribute to define the spaces between characters in Pixels.
+ *  A positive value will make the characters far apart (expanded) and a negative value will bring them closer (condensed).
+ *
+ * Examples:
+ * @code
+ *
+ * textController.Text = "<char-spacing value='5.0f'>Hello world</char-spacing>"; /// Expanded
+ *
+ * @endcode
+ *
+ * @code
+ *
+ * textController.Text = "<char-spacing value='-5.0f'>Hello world</char-spacing>"); /// Condensed
+ *
+ * @endcode
+ */
+static const std::string VALUE("value");
+} // namespace CHARACTER_SPACING_ATTRIBUTES
+namespace BACKGROUND_ATTRIBUTES
+{
+/**
+ * @brief Use the value attribute to define the color of background.
+ * The supported attribute values are red, green, blue, yellow, magenta, cyan, white, black, and transparent.
+ * Web colors and colors represented in 32-bit hexadecimal 0xAARRGGBB format are also supported.
+ *
+ * The following example explains how to apply it:
+ * @code
+ *
+ * textController.Text = "<background color='green'>Hello world</background>";
+ *
+ * @endcode
+ */
+static const std::string COLOR("color");
+
+} // namespace BACKGROUND_ATTRIBUTES
+
+namespace EMBEDDED_ITEM_ATTRIBUTES
+{
+/**
+ * @brief Use the url attribute to define url path of the image.
+ *
+ * @note  The url of the image is optional. If there is no image
+ * the layout engine will use the width and height to
+ * create a space inside the text. This gap can be filled later.
+ */
+static const std::string URL("url");
+
+/**
+ * @brief Use the width attribute to define the width of the item.
+ */
+static const std::string WIDTH("width");
+
+/**
+ * @brief Use the height attribute to define the height of the item.
+ */
+static const std::string HEIGHT("height");
+
+/**
+ * @brief Use the color-blending attribute to define whether the color of the image is multiplied by the color of the text.
+ *
+ * @note A color blending mode can be set. The default is NONE, the image will use its own color. If MULTIPLY is set, the color
+ * of the image will be multiplied by the color of the text.
+ */
+static const std::string COLOR_BLENDING("color-blending");
+} // namespace EMBEDDED_ITEM_ATTRIBUTES
+
+namespace ANCHOR_ATTRIBUTES
+{
+/**
+ * @brief Use the href attribute to define the url of hyperlink.
+ */
+static const std::string HREF("href");
+
+} // namespace ANCHOR_ATTRIBUTES
+
+} // namespace MARKUP
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_MARKUPS_AND_ATTRIBUTES_H
index 628434e..500a081 100644 (file)
@@ -848,12 +848,6 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth,
     // Increases the vertical offset with the line's ascender.
     glyphData.verticalOffset += static_cast<int>(line.ascender);
 
-    // Include line spacing after first line
-    if(lineIndex > 0u)
-    {
-      glyphData.verticalOffset += static_cast<int>(line.lineSpacing);
-    }
-
     // Retrieves the glyph's outline width
     float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
 
@@ -1158,8 +1152,8 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth,
       DrawStrikethrough(bufferWidth, bufferHeight, glyphData, baseline, strikethroughStartingYPosition, maxStrikethroughHeight, lineExtentLeft, lineExtentRight, modelStrikethroughProperties, currentStrikethroughProperties, line);
     }
 
-    // Increases the vertical offset with the line's descender.
-    glyphData.verticalOffset += static_cast<int>(-line.descender);
+    // Increases the vertical offset with the line's descender & line spacing.
+    glyphData.verticalOffset += static_cast<int>(-line.descender+line.lineSpacing);
   }
 
   return glyphData.bitmapBuffer;
index 7494cb6..d5c41a1 100644 (file)
@@ -117,6 +117,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
 
   bool textChanged    = false;
   bool relayoutNeeded = false;
+  bool isEditable     = controller.IsEditable() && controller.IsUserInteractionEnabled();
 
   if((NULL != controller.mImpl->mEventData) &&
      (keyEvent.GetState() == KeyEvent::DOWN))
@@ -148,7 +149,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
             (Dali::DALI_KEY_CURSOR_DOWN == keyCode))
     {
       // If don't have any text, do nothing.
-      if(!controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters)
+      if(!controller.mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters || !isEditable)
       {
         return false;
       }
@@ -207,7 +208,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
       // Do nothing
       return false;
     }
-    else if(keyEvent.IsCtrlModifier() && !keyEvent.IsShiftModifier())
+    else if(keyEvent.IsCtrlModifier() && !keyEvent.IsShiftModifier() && isEditable)
     {
       bool consumed = false;
       if(keyName == KEY_C_NAME || keyName == KEY_INSERT_NAME || logicalKey == KEY_C_NAME || logicalKey == KEY_INSERT_NAME)
@@ -273,7 +274,7 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
     else
     {
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", &controller, keyString.c_str());
-      if(!controller.IsEditable()) return false;
+      if(!isEditable) return false;
 
       std::string refinedKey = keyString;
       if(controller.mImpl->mInputFilter != NULL && !refinedKey.empty())
index cee6a88..1ed8c7f 100644 (file)
@@ -379,14 +379,15 @@ void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const
       impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
 
       // Get the line below.
-      const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
+      const LineRun& nextline = *(visualModel->mLines.Begin() + lineIndex + 1u);
+      const LineRun& currline = *(visualModel->mLines.Begin() + lineIndex);
 
       // Get last line index
       const LineIndex lastLineIndex = (visualModel->mLines.Size() > 0 ? visualModel->mLines.Size() - 1u : 0);
       const bool      isLastLine    = (lineIndex + 1u == lastLineIndex);
 
       // Get the next hit 'y' point.
-      const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line, isLastLine);
+      const float hitPointY = cursorInfo.lineOffset + GetLineHeight(currline, false) + 0.5f * GetLineHeight(nextline, isLastLine);
 
       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
       bool matchedCharacter = false;
index 318c196..46d83dd 100644 (file)
@@ -362,6 +362,27 @@ void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState
   }
 }
 
+void UpdateCursorPositionForAlignment(Controller::Impl& impl, bool needFullAlignment)
+{
+  EventData* eventData = impl.mEventData;
+
+  // Set the flag to redo the alignment operation
+  impl.mOperationsPending = static_cast<Controller::OperationsMask>(impl.mOperationsPending | Controller::OperationsMask::ALIGN);
+
+  if(eventData)
+  {
+    // Note: mUpdateAlignment is currently only needed for horizontal alignment
+    eventData->mUpdateAlignment = needFullAlignment;
+
+    // Update the cursor if it's in editing mode
+    if(EventData::IsEditingState(eventData->mState))
+    {
+      impl.ChangeState(EventData::EDITING);
+      eventData->mUpdateCursorPosition = true;
+    }
+  }
+}
+
 } // unnamed Namespace
 
 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
@@ -909,7 +930,8 @@ void Controller::Impl::SetEditable(bool editable)
 
     if(mEventData->mDecorator)
     {
-      mEventData->mDecorator->SetEditable(editable);
+      bool decoratorEditable = editable && mIsUserInteractionEnabled;
+      mEventData->mDecorator->SetEditable(decoratorEditable);
     }
   }
 }
@@ -1439,6 +1461,10 @@ void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, floa
     {
       mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
     }
+    else if(mModel->mLogicalModel->mText.Count() == 0u)
+    {
+      Relayouter::CalculateVerticalOffset(*this, mModel->mVisualModel->mControlSize);
+    }
   }
 }
 
@@ -1801,22 +1827,7 @@ void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type al
   {
     // Set the alignment.
     mModel->mHorizontalAlignment = alignment;
-
-    // Set the flag to redo the alignment operation.
-    mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
-
-    if(mEventData)
-    {
-      mEventData->mUpdateAlignment = true;
-
-      // Update the cursor if it's in editing mode
-      if(EventData::IsEditingState(mEventData->mState))
-      {
-        ChangeState(EventData::EDITING);
-        mEventData->mUpdateCursorPosition = true;
-      }
-    }
-
+    UpdateCursorPositionForAlignment(*this, true);
     RequestRelayout();
   }
 }
@@ -1827,7 +1838,7 @@ void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
   {
     // Set the alignment.
     mModel->mVerticalAlignment = alignment;
-    mOperationsPending         = static_cast<OperationsMask>(mOperationsPending | ALIGN);
+    UpdateCursorPositionForAlignment(*this, false);
     RequestRelayout();
   }
 }
@@ -1874,6 +1885,17 @@ void Controller::Impl::SetDefaultColor(const Vector4& color)
   }
 }
 
+void Controller::Impl::SetUserInteractionEnabled(bool enabled)
+{
+  mIsUserInteractionEnabled = enabled;
+
+  if(mEventData && mEventData->mDecorator)
+  {
+    bool editable = mEventData->mEditingEnabled && enabled;
+    mEventData->mDecorator->SetEditable(editable);
+  }
+}
+
 void Controller::Impl::ClearFontData()
 {
   if(mFontDefaults)
index 8be1767..5944039 100644 (file)
@@ -41,6 +41,7 @@ const float DEFAULT_TEXTFIT_MIN     = 10.f;
 const float DEFAULT_TEXTFIT_MAX     = 100.f;
 const float DEFAULT_TEXTFIT_STEP    = 1.f;
 const float DEFAULT_FONT_SIZE_SCALE = 1.f;
+const float DEFAULT_DISABLED_COLOR_OPACITY = 0.3f;
 
 //Forward declarations
 struct CursorInfo;
@@ -358,10 +359,12 @@ struct Controller::Impl
     mTextFitMaxSize(DEFAULT_TEXTFIT_MAX),
     mTextFitStepSize(DEFAULT_TEXTFIT_STEP),
     mFontSizeScale(DEFAULT_FONT_SIZE_SCALE),
+    mDisabledColorOpacity(DEFAULT_DISABLED_COLOR_OPACITY),
     mFontSizeScaleEnabled(true),
     mTextFitEnabled(false),
     mTextFitChanged(false),
-    mIsLayoutDirectionChanged(false)
+    mIsLayoutDirectionChanged(false),
+    mIsUserInteractionEnabled(true)
   {
     mModel = Model::New();
 
@@ -874,6 +877,11 @@ struct Controller::Impl
   void SetDefaultColor(const Vector4& color);
 
   /**
+   * @copydoc Controller::SetUserInteractionEnabled()
+   */
+  void SetUserInteractionEnabled(bool enabled);
+
+  /**
    * @brief Helper to clear font-specific data (only).
    */
   void ClearFontData();
@@ -1014,10 +1022,12 @@ public:
   float mTextFitMaxSize;               ///< Maximum Font Size for text fit. Default 100
   float mTextFitStepSize;              ///< Step Size for font intervalse. Default 1
   float mFontSizeScale;                ///< Scale value for Font Size. Default 1.0
+  float mDisabledColorOpacity;         ///< Color opacity when disabled.
   bool  mFontSizeScaleEnabled : 1;     ///< Whether the font size scale is enabled.
   bool  mTextFitEnabled : 1;           ///< Whether the text's fit is enabled.
   bool  mTextFitChanged : 1;           ///< Whether the text fit property has changed.
   bool  mIsLayoutDirectionChanged : 1; ///< Whether the layout has changed.
+  bool  mIsUserInteractionEnabled : 1; ///< Whether the user interaction is enabled.
 
 private:
   friend ControllerImplEventHandler;
index 6f4763e..42199ad 100644 (file)
@@ -67,6 +67,8 @@ Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Control
                                                                         SHAPE_TEXT |
                                                                         GET_GLYPH_METRICS);
 
+  const OperationsMask sizeOperations     = static_cast<OperationsMask>(LAYOUT | ALIGN | REORDER);
+
   // Set the update info to relayout the whole text.
   TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo;
   if((0 == textUpdateInfo.mNumberOfCharactersToAdd) &&
@@ -78,55 +80,68 @@ Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Control
   textUpdateInfo.mParagraphCharacterIndex     = 0u;
   textUpdateInfo.mRequestedNumberOfCharacters = model->mLogicalModel->mText.Count();
 
-  // This is to keep Index to the first character to be updated.
-  // Then restore it after calling Clear method.
-  auto updateInfoCharIndexBackup = textUpdateInfo.mCharacterIndex;
-
   // Get a reference to the pending operations member
   OperationsMask& operationsPending = impl.mOperationsPending;
 
-  // Layout the text for the new width.
-  // Apply the pending operations, requested operations and the only once operations.
-  // Then remove onlyOnceOperations
-  operationsPending = static_cast<OperationsMask>(operationsPending | requestedOperationsMask | onlyOnceOperations);
-
-  // Make sure the model is up-to-date before layouting
-  impl.UpdateModel(static_cast<OperationsMask>(operationsPending & ~UPDATE_LAYOUT_SIZE));
-
   // Store the actual control's size to restore later.
   const Size actualControlSize = visualModel->mControlSize;
 
-  DoRelayout(impl,
-             requestedControllerSize,
-             static_cast<OperationsMask>(operationsPending & ~UPDATE_LAYOUT_SIZE),
-             calculatedLayoutSize);
+  // Whether the text control is editable
+  const bool isEditable = NULL != impl.mEventData;
 
-  // Clear the update info. This info will be set the next time the text is updated.
-  textUpdateInfo.Clear();
+  if(!isEditable)
+  {
+    impl.UpdateModel(onlyOnceOperations);
 
-  //TODO: Refactor "DoRelayout" and extract common code of size calculation without modifying attributes of mVisualModel,
-  //TODO: then calculate GlyphPositions. Lines, Size, Layout for Natural-Size
-  //TODO: and utilize the values in OperationsPending and TextUpdateInfo without changing the original one.
-  //TODO: Also it will improve performance because there is no need todo FullRelyout on the next need for layouting.
+    DoRelayout(impl,
+               requestedControllerSize,
+               static_cast<OperationsMask>(onlyOnceOperations | requestedOperationsMask),
+               calculatedLayoutSize);
 
-  // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
-  // By this no need to take backup and restore it.
-  textUpdateInfo.mFullRelayoutNeeded = true;
+    textUpdateInfo.Clear();
+    textUpdateInfo.mClearAll = true;
 
-  // Restore mCharacterIndex. Because "Clear" set it to the maximum integer.
-  // The "CalculateTextUpdateIndices" does not work proprely because the mCharacterIndex will be greater than mPreviousNumberOfCharacters.
-  // Which apply an assumption to update only the last  paragraph. That could cause many of out of index crashes.
-  textUpdateInfo.mCharacterIndex = updateInfoCharIndexBackup;
+    // Do not do again the only once operations.
+    operationsPending = static_cast<OperationsMask>(operationsPending & ~onlyOnceOperations);
+  }
+  else
+  {
+    // This is to keep Index to the first character to be updated.
+    // Then restore it after calling Clear method.
+    auto updateInfoCharIndexBackup = textUpdateInfo.mCharacterIndex;
 
-  // Do not do again the only once operations.
-  operationsPending = static_cast<OperationsMask>(operationsPending & ~onlyOnceOperations);
+    // Layout the text for the new width.
+    // Apply the pending operations, requested operations and the only once operations.
+    // Then remove onlyOnceOperations
+    operationsPending = static_cast<OperationsMask>(operationsPending | requestedOperationsMask | onlyOnceOperations);
 
-  // Do the size related operations again.
+    // Make sure the model is up-to-date before layouting
+    impl.UpdateModel(static_cast<OperationsMask>(operationsPending & ~UPDATE_LAYOUT_SIZE));
+
+    DoRelayout(impl,
+               requestedControllerSize,
+               static_cast<OperationsMask>(operationsPending & ~UPDATE_LAYOUT_SIZE),
+               calculatedLayoutSize);
+
+    // Clear the update info. This info will be set the next time the text is updated.
+    textUpdateInfo.Clear();
+
+    //TODO: Refactor "DoRelayout" and extract common code of size calculation without modifying attributes of mVisualModel,
+    //TODO: then calculate GlyphPositions. Lines, Size, Layout for Natural-Size
+    //TODO: and utilize the values in OperationsPending and TextUpdateInfo without changing the original one.
+    //TODO: Also it will improve performance because there is no need todo FullRelyout on the next need for layouting.
+
+    // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
+    // By this no need to take backup and restore it.
+    textUpdateInfo.mFullRelayoutNeeded = true;
 
-  const OperationsMask sizeOperations = static_cast<OperationsMask>(LAYOUT |
-                                                                    ALIGN |
-                                                                    REORDER);
+    // Restore mCharacterIndex. Because "Clear" set it to the maximum integer.
+    // The "CalculateTextUpdateIndices" does not work proprely because the mCharacterIndex will be greater than mPreviousNumberOfCharacters.
+    // Which apply an assumption to update only the last  paragraph. That could cause many of out of index crashes.
+    textUpdateInfo.mCharacterIndex = updateInfoCharIndexBackup;
+  }
 
+  // Do the size related operations again.
   operationsPending = static_cast<OperationsMask>(operationsPending | sizeOperations);
 
   // Restore the actual control's size.
@@ -447,7 +462,19 @@ Controller::UpdateTextType Controller::Relayouter::Relayout(Controller& controll
   if(!isEditable || !controller.IsMultiLineEnabled())
   {
     // After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
-    CalculateVerticalOffset(controller, size);
+    CalculateVerticalOffset(impl, size);
+  }
+  else // TextEditor
+  {
+    // If layoutSize is bigger than size, vertical align has no meaning.
+    if(layoutSize.y < size.y)
+    {
+      CalculateVerticalOffset(impl, size);
+      if(impl.mEventData)
+      {
+        impl.mEventData->mScrollAfterDelete = false;
+      }
+    }
   }
 
   if(isEditable)
@@ -769,9 +796,8 @@ void Controller::Relayouter::DoRelayoutHorizontalAlignment(Controller::Impl&
   }
 }
 
-void Controller::Relayouter::CalculateVerticalOffset(Controller& controller, const Size& controlSize)
+void Controller::Relayouter::CalculateVerticalOffset(Controller::Impl& impl, const Size& controlSize)
 {
-  Controller::Impl& impl                  = *controller.mImpl;
   ModelPtr&         model                 = impl.mModel;
   VisualModelPtr&   visualModel           = model->mVisualModel;
   Size              layoutSize            = model->mVisualModel->GetLayoutSize();
index c0ab806..a98ed53 100644 (file)
@@ -97,10 +97,10 @@ struct Controller::Relayouter
   /**
    * @brief Called by the Controller to calculate the veritcal offset give the control size.
    *
-   * @param[in] controller A reference to the controller class
+   * @param[in] impl A reference to the controller impl class
    * @param[in] controlSize The control size
    */
-  static void CalculateVerticalOffset(Controller& controller, const Size& controlSize);
+  static void CalculateVerticalOffset(Controller::Impl& impl, const Size& controlSize);
 
   /**
   * @brief Calculates the layout size of control according to @p requestedControllerSize and @p requestedOperationsMask
index b0f740d..bf92f6a 100644 (file)
@@ -747,6 +747,26 @@ const Vector4& Controller::GetDefaultColor() const
   return mImpl->mTextColor;
 }
 
+void Controller::SetDisabledColorOpacity(float opacity)
+{
+  mImpl->mDisabledColorOpacity = opacity;
+}
+
+float Controller::GetDisabledColorOpacity() const
+{
+  return mImpl->mDisabledColorOpacity;
+}
+
+void Controller::SetUserInteractionEnabled(bool enabled)
+{
+  mImpl->SetUserInteractionEnabled(enabled);
+}
+
+bool Controller::IsUserInteractionEnabled() const
+{
+  return mImpl->mIsUserInteractionEnabled;
+}
+
 void Controller::SetPlaceholderTextColor(const Vector4& textColor)
 {
   PlaceholderHandler::SetPlaceholderTextColor(*this, textColor);
index 81a0318..3e987dd 100644 (file)
@@ -504,6 +504,20 @@ public: // Configure the text controller.
   bool IsTextFitChanged() const;
 
   /**
+   * @brief Sets disabled color opacity.
+   *
+   * @param[in] opacity The color opacity value in disabled state.
+   */
+  void SetDisabledColorOpacity(float opacity);
+
+  /**
+   * @brief Retrieves the disabled color opacity.
+   *
+   * @return The disabled color opacity value for disabled state.
+   */
+  float GetDisabledColorOpacity() const;
+
+  /**
    * @brief Enable or disable the placeholder text elide.
    * @param enabled Whether to enable the placeholder text elide.
    */
@@ -1004,6 +1018,20 @@ public: // Default style & Input style
   const Vector4& GetDefaultColor() const;
 
   /**
+   * @brief Sets the user interaction enabled.
+   *
+   * @param enabled whether to enable the user interaction.
+   */
+  void SetUserInteractionEnabled(bool enabled);
+
+  /**
+   * @brief Whether the user interaction is enabled.
+   *
+   * @return true if the user interaction is enabled, false otherwise.
+   */
+  bool IsUserInteractionEnabled() const;
+
+  /**
    * @brief Set the text color
    *
    * @param textColor The text color
index bdc0b6a..9524c44 100644 (file)
@@ -398,7 +398,8 @@ TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
   const Dali::SamplingMode::Type&            samplingMode,
   const TextureCacheManager::UseAtlas&       useAtlas,
   const TextureCacheManager::TextureId&      maskTextureId,
-  const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad)
+  const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
+  bool                                       isAnimatedImage)
 {
   // Default to an invalid ID, in case we do not find a match.
   TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX;
@@ -416,6 +417,7 @@ TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
          (useAtlas == textureInfo.useAtlas) &&
          (maskTextureId == textureInfo.maskTextureId) &&
          (size == textureInfo.desiredSize) &&
+         (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
          ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
           (fittingMode == textureInfo.fittingMode &&
            samplingMode == textureInfo.samplingMode)))
@@ -449,7 +451,6 @@ void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& text
   if(textureInfoIndex != INVALID_CACHE_INDEX)
   {
     TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]);
-
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n  cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex, GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount);
 
     // Decrement the reference count and check if this is the last user of this Texture.
index 5e82d47..7d941d1 100644 (file)
@@ -204,7 +204,8 @@ public:
    * @param[in] samplingMode      The SamplingMode to use
    * @param[in] useAtlas          True if atlased
    * @param[in] maskTextureId     Optional texture ID to use to mask this image
-   * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
+   * @param[in] preMultiplyOnLoad If the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
+   * @param[in] isAnimatedImage   True if the texture is from animated image.
    * @return                      A TextureCacheId of a cached Texture if found. Or INVALID_CACHE_INDEX if not found.
    */
   TextureCacheManager::TextureCacheIndex FindCachedTexture(
@@ -215,7 +216,8 @@ public:
     const Dali::SamplingMode::Type&            samplingMode,
     const TextureCacheManager::UseAtlas&       useAtlas,
     const TextureCacheManager::TextureId&      maskTextureId,
-    const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad);
+    const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
+    bool                                       isAnimatedImage);
 
   /**
    * @brief Append a Texture to the TextureCacheManager.
index 31c57c6..ff81b22 100644 (file)
@@ -137,15 +137,15 @@ TextureManager::~TextureManager()
   }
 }
 
-TextureSet TextureManager::LoadAnimatedImageTexture(
-  Dali::AnimatedImageLoading      animatedImageLoading,
-  const std::uint32_t&            frameIndex,
-  const Dali::SamplingMode::Type& samplingMode,
-  const bool&                     synchronousLoading,
-  TextureManager::TextureId&      textureId,
-  const Dali::WrapMode::Type&     wrapModeU,
-  const Dali::WrapMode::Type&     wrapModeV,
-  TextureUploadObserver*          textureObserver)
+TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading      animatedImageLoading,
+                                                    const uint32_t&                 frameIndex,
+                                                    TextureManager::TextureId&      textureId,
+                                                    const Dali::SamplingMode::Type& samplingMode,
+                                                    const Dali::WrapMode::Type&     wrapModeU,
+                                                    const Dali::WrapMode::Type&     wrapModeV,
+                                                    const bool&                     synchronousLoading,
+                                                    const bool&                     useCache,
+                                                    TextureUploadObserver*          textureObserver)
 {
   TextureSet textureSet;
 
@@ -175,7 +175,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture(
   else
   {
     auto preMultiply                    = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false);
+    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false, useCache);
     TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
     if(loadState == TextureManager::LoadState::UPLOADED)
     {
@@ -229,7 +229,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   }
   else
   {
-    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
+    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false, false);
   }
 
   return pixelBuffer;
@@ -415,7 +415,7 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
   const bool&                         synchronousLoading)
 {
-  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading, true);
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
@@ -433,7 +433,7 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
   const bool&                         synchronousLoading)
 {
-  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading, true);
 }
 
 TextureManager::TextureId TextureManager::RequestMaskLoad(
@@ -442,7 +442,7 @@ TextureManager::TextureId TextureManager::RequestMaskLoad(
 {
   // Use the normal load procedure to get the alpha mask.
   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading, true);
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
@@ -461,19 +461,17 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
   Dali::AnimatedImageLoading          animatedImageLoading,
   const std::uint32_t&                frameIndex,
-  const bool&                         synchronousLoading)
+  const bool&                         synchronousLoading,
+  const bool&                         useCache)
 {
-  // First check if the requested Texture is cached.
-  bool isAnimatedImage = (animatedImageLoading) ? true : false;
-
   TextureHash       textureHash = INITIAL_HASH_NUMBER;
   TextureCacheIndex cacheIndex  = INVALID_CACHE_INDEX;
-  if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage)
+  if(storageType != StorageType::RETURN_PIXEL_BUFFER && useCache)
   {
     textureHash = mTextureCacheManager.GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId);
 
     // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
-    cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad);
+    cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false);
   }
 
   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
@@ -602,12 +600,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   {
     // If the image is already finished to load, use cached texture.
     // We don't need to consider Observer because this is synchronous loading.
-    if(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
-       textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)
-    {
-      return textureId;
-    }
-    else
+    if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
+         textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED))
     {
       Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);
 
@@ -635,6 +629,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
             {
               pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask);
             }
+            else
+            {
+              DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n");
+            }
           }
           else
           {
@@ -648,8 +646,6 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
       }
     }
   }
-
-  // Return the TextureId for which this Texture can now be referenced by externally.
   return textureId;
 }
 
@@ -746,7 +742,7 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo
       {
         // The Texture has already loaded. The other observers have already been notified.
         // We need to send a "late" loaded notification for this observer.
-        observer->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied));
+        EmitLoadComplete(observer, textureInfo, true);
       }
       break;
     }
@@ -806,13 +802,9 @@ void TextureManager::ProcessQueuedTextures()
     if(cacheIndex != INVALID_CACHE_INDEX)
     {
       TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
-      if(textureInfo.loadState == LoadState::UPLOADED)
+      if((textureInfo.loadState == LoadState::UPLOADED) || (textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER))
       {
-        element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied));
-      }
-      else if(textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
-      {
-        element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied));
+        EmitLoadComplete(element.mObserver, textureInfo, true);
       }
       else
       {
@@ -895,6 +887,13 @@ void TextureManager::PostLoad(TextureManager::TextureInfo& textureInfo, Devel::P
             // Send New Task to Thread
             ApplyMask(textureInfo, textureInfo.maskTextureId);
           }
+          else // maskLoadState == LoadState::LOAD_FAILED
+          {
+            // Url texture load success, But alpha mask texture load failed. Run as normal image upload.
+            DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n");
+            UploadTexture(pixelBuffer, textureInfo);
+            NotifyObservers(textureInfo, true);
+          }
         }
       }
       else
@@ -923,8 +922,16 @@ void TextureManager::PostLoad(TextureManager::TextureInfo& textureInfo, Devel::P
   else
   {
     textureInfo.loadState = LoadState::LOAD_FAILED;
-    CheckForWaitingTexture(textureInfo);
-    NotifyObservers(textureInfo, false);
+    if(textureInfo.storageType != StorageType::KEEP_PIXEL_BUFFER)
+    {
+      NotifyObservers(textureInfo, false);
+    }
+    else // if(textureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER) // image mask case
+    {
+      // Check if there was another texture waiting for this load to complete
+      // (e.g. if this was an image mask, and its load is on a different thread)
+      CheckForWaitingTexture(textureInfo);
+    }
   }
 }
 
@@ -934,6 +941,8 @@ void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTex
   // maskTextureId:
   const TextureCacheIndex size = static_cast<TextureCacheIndex>(mTextureCacheManager.size());
 
+  const bool maskLoadSuccess = maskTextureInfo.loadState == LoadState::LOAD_FINISHED ? true : false;
+
   for(TextureCacheIndex cacheIndex = 0; cacheIndex < size; ++cacheIndex)
   {
     if(mTextureCacheManager[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
@@ -941,16 +950,17 @@ void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTex
     {
       TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
 
-      if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED)
+      if(maskLoadSuccess)
       {
         // Send New Task to Thread
         ApplyMask(textureInfo, maskTextureInfo.textureId);
       }
       else
       {
-        textureInfo.pixelBuffer.Reset();
-        textureInfo.loadState = LoadState::LOAD_FAILED;
-        NotifyObservers(textureInfo, false);
+        // Url texture load success, But alpha mask texture load failed. Run as normal image upload.
+        DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n");
+        UploadTexture(textureInfo.pixelBuffer, textureInfo);
+        NotifyObservers(textureInfo, true);
       }
     }
   }
@@ -1017,6 +1027,17 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
   // and erase it from the list
   TextureInfo* info = &textureInfo;
 
+  if(info->animatedImageLoading)
+  {
+    // If loading failed, we don't need to get frameCount and frameInterval.
+    if(success)
+    {
+      info->frameCount    = info->animatedImageLoading.GetImageCount();
+      info->frameInterval = info->animatedImageLoading.GetFrameInterval(info->frameIndex);
+    }
+    info->animatedImageLoading.Reset();
+  }
+
   mQueueLoadFlag = true;
 
   while(info->observerList.Count())
@@ -1039,14 +1060,7 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
 
     info->observerList.Erase(info->observerList.Begin());
 
-    if(info->storageType == StorageType::RETURN_PIXEL_BUFFER)
-    {
-      observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, info->pixelBuffer, info->url.GetUrl(), info->preMultiplied));
-    }
-    else
-    {
-      observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, info->textureId, info->textureSet, (info->useAtlas == UseAtlas::USE_ATLAS) ? true : false, info->atlasRect, info->preMultiplied));
-    }
+    EmitLoadComplete(observer, *info, success);
 
     // Get the textureInfo from the container again as it may have been invalidated.
     TextureCacheIndex textureInfoIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
@@ -1101,6 +1115,22 @@ Dali::Geometry TextureManager::GetRenderGeometry(const TextureManager::TextureId
   return RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().GetGeometry(textureId, frontElements, backElements) : Geometry();
 }
 
+void TextureManager::EmitLoadComplete(TextureUploadObserver* observer, TextureManager::TextureInfo& textureInfo, const bool& success)
+{
+  if(textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+  {
+    observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied));
+  }
+  else if(textureInfo.isAnimatedImageFormat)
+  {
+    observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::ANIMATED_IMAGE_TEXTURE, textureInfo.textureId, textureInfo.frameCount, textureInfo.frameInterval));
+  }
+  else
+  {
+    observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied));
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index ee06c53..01f62ee 100644 (file)
@@ -112,27 +112,28 @@ public:
    * The parameters are used to specify how the animated image is loaded.
    * The observer has the LoadComplete method called when the load is ready.
    *
-   * @param[in] animatedImageLoading  The AnimatedImageLoading that contain the animated image information
-   * @param[in] frameIndex            The frame index to load.
-   * @param[in] samplingMode          The SamplingMode to use
-   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
-   * @param[out] textureId            The textureId of the frame
-   * @param[in] wrapModeU             Horizontal Wrap mode
-   * @param[in] wrapModeV             Vertical Wrap mode
-   * @param[in] textureObserver       The client object should inherit from this and provide the "UploadCompleted" virtual.
-   *                                  This is called when an image load completes (or fails).
+   * @param[in]  animatedImageLoading  The AnimatedImageLoading that contain the animated image information
+   * @param[in]  frameIndex            The frame index to load.
+   * @param[out] textureId             The textureId of the frame
+   * @param[in]  samplingMode          The SamplingMode to use
+   * @param[in]  wrapModeU             Horizontal Wrap mode
+   * @param[in]  wrapModeV             Vertical Wrap mode
+   * @param[in]  synchronousLoading    true if the frame should be loaded synchronously
+   * @param[in]  useCache              true if this frame loading uses cache.
+   * @param[in]  textureObserver       The client object should inherit from this and provide the "LoadCompleted" virtual.
+   *                                   This is called when an image load completes (or fails).
    *
-   * @return                          The texture set containing the frame of animated image, or empty if still loading.
+   * @return                           The texture set containing the frame of animated image, or empty if still loading.
    */
-  TextureSet LoadAnimatedImageTexture(
-    Dali::AnimatedImageLoading      animatedImageLoading,
-    const std::uint32_t&            frameIndex,
-    const Dali::SamplingMode::Type& samplingMode,
-    const bool&                     synchronousLoading,
-    TextureManager::TextureId&      textureId,
-    const Dali::WrapMode::Type&     wrapModeU,
-    const Dali::WrapMode::Type&     wrapModeV,
-    TextureUploadObserver*          textureObserver);
+  TextureSet LoadAnimatedImageTexture(Dali::AnimatedImageLoading      animatedImageLoading,
+                                      const uint32_t&                 frameIndex,
+                                      TextureManager::TextureId&      textureId,
+                                      const Dali::SamplingMode::Type& samplingMode,
+                                      const Dali::WrapMode::Type&     wrapModeU,
+                                      const Dali::WrapMode::Type&     wrapModeV,
+                                      const bool&                     synchronousLoading,
+                                      const bool&                     useCache,
+                                      TextureUploadObserver*          textureObserver);
 
   /**
    * @brief Requests an image load of the given URL to get PixelBuffer.
@@ -145,7 +146,7 @@ public:
    * @param[in] fittingMode           The FittingMode to use
    * @param[in] samplingMode          The SamplingMode to use
    * @param[in] synchronousLoading    true if the URL should be loaded synchronously
-   * @param[in] textureObserver       The client object should inherit from this and provide the "UploadCompleted" virtual.
+   * @param[in] textureObserver       The client object should inherit from this and provide the "LoadCompleted" virtual.
    *                                  This is called when an image load completes (or fails).
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
    * @param[in,out] preMultiplyOnLoad True if the image color should be multiplied by it's alpha. Set to false if the
@@ -188,10 +189,10 @@ public:
    * @param[out] loadingStatus        The loading status of the texture
    * @param[in] wrapModeU             Horizontal Wrap mode
    * @param[in] wrapModeV             Vertical Wrap mode
-   * @param[in] textureObserver       The client object should inherit from this and provide the "UploadCompleted" virtual.
+   * @param[in] textureObserver       The client object should inherit from this and provide the "LoadCompleted" virtual.
    *                                  This is called when an image load completes (or fails).
    * @param[in] atlasObserver         This is used if the texture is atlased, and will be called instead of
-   *                                  textureObserver.UploadCompleted
+   *                                  textureObserver.LoadCompleted
    * @param[in] imageAtlasManager     The atlas manager to use for atlasing textures
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
@@ -335,8 +336,8 @@ public: // Load Request API
    * @param[in] fittingMode           The FittingMode to use
    * @param[in] samplingMode          The SamplingMode to use
    * @param[in] useAtlasing           Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
-   *                                  but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
-   * @param[in] observer              The client object should inherit from this and provide the "UploadCompleted" virtual.
+   *                                  but "useAtlasing" will be set to false in the "LoadCompleted" callback from the TextureManagerUploadObserver.
+   * @param[in] observer              The client object should inherit from this and provide the "LoadCompleted" virtual.
    *                                  This is called when an image load completes (or fails).
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
@@ -345,7 +346,7 @@ public: // Load Request API
    *                                  default is false.
    * @return                          A TextureId to use as a handle to reference this Texture
    */
-  TextureManager::TextureId RequestLoad(
+  TextureId RequestLoad(
     const VisualUrl&                    url,
     const ImageDimensions&              desiredSize,
     const Dali::FittingMode::Type&      fittingMode,
@@ -376,11 +377,11 @@ public: // Load Request API
    * @param[in] samplingMode          The SamplingMode to use
    * @param[in] useAtlasing           Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still
    *                                  be loaded, and marked successful,
-   *                                  but "useAtlasing" will be set to false in the "UploadCompleted" callback from
+   *                                  but "useAtlasing" will be set to false in the "LoadCompleted" callback from
    *                                  the TextureManagerUploadObserver.
    * @param[in] cropToMask            Only used with masking, this will crop the scaled image to the mask size.
    *                                  If false, then the mask will be scaled to fit the image before being applied.
-   * @param[in] observer              The client object should inherit from this and provide the "UploadCompleted"
+   * @param[in] observer              The client object should inherit from this and provide the "LoadCompleted"
    *                                  virtual.
    *                                  This is called when an image load completes (or fails).
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
@@ -391,7 +392,7 @@ public: // Load Request API
    *                                  default is false.
    * @return                          A TextureId to use as a handle to reference this Texture
    */
-  TextureManager::TextureId RequestLoad(
+  TextureId RequestLoad(
     const VisualUrl&                    url,
     const TextureManager::TextureId&    maskTextureId,
     const float&                        contentScale,
@@ -414,7 +415,7 @@ public: // Load Request API
    *                               default is false.
    * @return                       A TextureId to use as a handle to reference this mask Texture
    */
-  TextureManager::TextureId RequestMaskLoad(
+  TextureId RequestMaskLoad(
     const VisualUrl& maskUrl,
     const bool&      synchronousLoading = false);
 
@@ -438,11 +439,11 @@ private:
    * @param[in] samplingMode          The SamplingMode to use
    * @param[in] useAtlasing           Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be
    *                                  loaded, and marked successful, but "useAtlasing" will be set to false in the
-   *                                  "UploadCompleted" callback from the TextureManagerUploadObserver.
+   *                                  "LoadCompleted" callback from the TextureManagerUploadObserver.
    * @param[in] cropToMask            Whether to crop the target after masking, or scale the mask to the image before
    *                                  masking.
    * @param[in] storageType,          Whether the pixel data is stored in the cache or uploaded to the GPU
-   * @param[in] observer              The client object should inherit from this and provide the "UploadCompleted"
+   * @param[in] observer              The client object should inherit from this and provide the "LoadCompleted"
    *                                  virtual.
    *                                  This is called when an image load completes (or fails).
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
@@ -453,9 +454,10 @@ private:
    * @param[in] frameIndex            The frame index of a frame to be loaded frame
    * @param[in] synchronousLoading    True if the frame should be loaded synchronously. If you skip this parameter,
    *                                  default is false.
+   * @param[in] useCache              True if the texture will be cached.
    * @return                          A TextureId to use as a handle to reference this Texture
    */
-  TextureManager::TextureId RequestLoadInternal(
+  TextureId RequestLoadInternal(
     const VisualUrl&                    url,
     const TextureManager::TextureId&    maskTextureId,
     const float&                        contentScale,
@@ -471,7 +473,8 @@ private:
     TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
     Dali::AnimatedImageLoading          animatedImageLoading,
     const std::uint32_t&                frameIndex,
-    const bool&                         synchronousLoading);
+    const bool&                         synchronousLoading,
+    const bool&                         useCache);
 
   /**
    * @brief Load a new image synchronously.
@@ -580,6 +583,14 @@ private:
    */
   void NotifyObservers(TextureManager::TextureInfo& textureInfo, const bool& success);
 
+  /**
+   * Call LoadComplete to the observer.
+   * @param[in] observer    The client object should inherit from this and provide the "LoadCompleted"
+   * @param[in] textureInfo The struct associated with this Texture
+   * @param[in] success     If the pixel data was retrieved successfully and uploaded to GPU
+   */
+  void EmitLoadComplete(TextureUploadObserver* observer, TextureManager::TextureInfo& textureInfo, const bool& success);
+
 public:
   /**
    * @brief Common method to handle loading completion.
index 5f07c1f..514e544 100644 (file)
@@ -153,6 +153,8 @@ struct TextureInfo
     storageType(StorageType::UPLOAD_TO_TEXTURE),
     animatedImageLoading(animatedImageLoading),
     frameIndex(frameIndex),
+    frameCount(0u),
+    frameInterval(0u),
     useAtlas(useAtlas),
     loadSynchronously(loadSynchronously),
     cropToMask(cropToMask),
@@ -160,6 +162,7 @@ struct TextureInfo
     preMultiplyOnLoad(preMultiplyOnLoad),
     preMultiplied(false)
   {
+    isAnimatedImageFormat = (animatedImageLoading) ? true : false;
   }
 
   /**
@@ -185,7 +188,9 @@ struct TextureInfo
   Dali::SamplingMode::Type   samplingMode : 3;     ///< The requested SamplingMode
   StorageType                storageType;          ///< CPU storage / GPU upload;
   Dali::AnimatedImageLoading animatedImageLoading; ///< AnimatedImageLoading that contains animated image information.
-  std::uint32_t              frameIndex;           ///< frame index that be loaded, in case of animated image
+  uint32_t                   frameIndex;           ///< Frame index that be loaded, in case of animated image
+  uint32_t                   frameCount;           ///< Total frame count of input animated image. If this variable is not 0, this textureInfo is for animated image file format.
+  uint32_t                   frameInterval;        ///< Time interval between this frame and next frame of animated image.
   UseAtlas                   useAtlas;             ///< USE_ATLAS if an atlas was requested.
 
   bool loadSynchronously : 1;     ///< True if synchronous loading was requested
@@ -193,6 +198,7 @@ struct TextureInfo
   bool orientationCorrection : 1; ///< True if the image should be rotated to match exif orientation data
   bool preMultiplyOnLoad : 1;     ///< True if the image's color should be multiplied by it's alpha
   bool preMultiplied : 1;         ///< True if the image's color was multiplied by it's alpha
+  bool isAnimatedImageFormat : 1; ///< true if the image is requested from animated image visual.
 };
 
 } // namespace TextureManagerType
index 524ef67..a4cdc64 100644 (file)
@@ -49,6 +49,20 @@ TextureUploadObserver::TextureInformation::TextureInformation(ReturnType returnT
 {
 }
 
+TextureUploadObserver::TextureInformation::TextureInformation(ReturnType returnType, int32_t textureId, uint32_t frameCount, uint32_t interval)
+: returnType(returnType),
+  textureId(textureId),
+  textureSet(),
+  useAtlasing(false),
+  atlasRect(Vector4::ZERO),
+  preMultiplied(false),
+  pixelBuffer(),
+  url(),
+  frameCount(frameCount),
+  interval(interval)
+{
+}
+
 TextureUploadObserver::TextureUploadObserver()
 {
 }
index 211cad7..5848654 100644 (file)
@@ -44,14 +44,16 @@ public:
 
   enum class ReturnType
   {
-    TEXTURE = 0,
-    PIXEL_BUFFER
+    PIXEL_BUFFER = 0,
+    TEXTURE,
+    ANIMATED_IMAGE_TEXTURE
   };
 
   struct TextureInformation
   {
     TextureInformation(ReturnType returnType, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied);
     TextureInformation(ReturnType returnType, Devel::PixelBuffer pixelBuffer, const std::string& url, bool preMultiplied);
+    TextureInformation(ReturnType returnType, int32_t textureId, uint32_t frameCount, uint32_t interval);
 
     TextureInformation();
 
@@ -63,6 +65,8 @@ public:
     bool               preMultiplied; ///< True if the image had pre-multiplied alpha applied
     Devel::PixelBuffer pixelBuffer;   ///< The PixelBuffer of the loaded image.
     std::string_view   url;           ///< The url address of the loaded image.
+    uint32_t           frameCount{0}; ///< The frameCount of the animated image
+    uint32_t           interval{0};   ///< Time interval between currently loaded frame and next frame.
   };
 
 public:
index b8eb497..d5dcdaa 100644 (file)
@@ -63,8 +63,25 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(WRAP_MODE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, MIRRORED_REPEAT)
 DALI_ENUM_TO_STRING_TABLE_END(WRAP_MODE)
 
-const Vector4  FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
-constexpr auto LOOP_FOREVER = -1;
+// load policies
+DALI_ENUM_TO_STRING_TABLE_BEGIN(LOAD_POLICY)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED)
+DALI_ENUM_TO_STRING_TABLE_END(LOAD_POLICY)
+
+// release policies
+DALI_ENUM_TO_STRING_TABLE_BEGIN(RELEASE_POLICY)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER)
+DALI_ENUM_TO_STRING_TABLE_END(RELEASE_POLICY)
+
+static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u;
+static constexpr uint32_t FIRST_FRAME_INDEX  = 0u;
+static constexpr uint16_t MINIMUM_CACHESIZE  = 1;
+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;
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
@@ -76,18 +93,15 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
  *
  *   | New
  *   |   DoSetProperties()
- *   |   LoadFirstBatch()
- *   |     new cache
- *   |       cache->LoadBatch()
+ *   |   OnInitialize()
+ *   |     CreateImageCache()
  *   |
  *   | DoSetOnScene()
  *   |   PrepareTextureSet()
  *   |     cache->FirstFrame()
- *   |   CreateRenderer()    (Doesn't become ready until first frame loads)
- *   |   StartFirstFrame()
  *   |
  *   | FrameReady(textureSet)
- *   |   start first frame:
+ *   |   StartFirstFrame:
  *   |     actor.AddRenderer
  *   |     start timer
  *   |   mRenderer.SetTextures(textureSet)
@@ -97,8 +111,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
  *   |     if front frame is ready,
  *   |       mRenderer.SetTextures( front frame's texture )
  *   |     else
- *   |       mWaitingForTexture=true
- *   |     cache->LoadBatch()
+ *   |       Waiting for frame ready.
  *   |
  *   | FrameReady(textureSet)
  *   |   mRenderer.SetTextures(textureSet)
@@ -112,11 +125,6 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
   visual->InitializeAnimatedImage(imageUrl);
   visual->SetProperties(properties);
 
-  if(visual->mFrameCount > 0)
-  {
-    visual->LoadFirstBatch();
-  }
-
   visual->Initialize();
 
   return visual;
@@ -138,11 +146,6 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
   visual->mFrameCount = imageUrls.Count();
   visual->SetProperties(properties);
 
-  if(visual->mFrameCount > 0)
-  {
-    visual->LoadFirstBatch();
-  }
-
   visual->Initialize();
 
   return visual;
@@ -153,11 +156,6 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
   AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory));
   visual->InitializeAnimatedImage(imageUrl);
 
-  if(visual->mFrameCount > 0)
-  {
-    visual->LoadFirstBatch();
-  }
-
   visual->Initialize();
 
   return visual;
@@ -165,9 +163,41 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
 
 void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl)
 {
-  mImageUrl             = imageUrl;
+  mImageUrl = imageUrl;
   mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
-  mFrameCount           = mAnimatedImageLoading.GetImageCount();
+}
+
+void AnimatedImageVisual::CreateImageCache()
+{
+  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::CreateImageCache()  batchSize:%d  cacheSize:%d\n", mBatchSize, mCacheSize);
+
+  TextureManager& textureManager = mFactoryCache.GetTextureManager();
+
+  if(mAnimatedImageLoading)
+  {
+    mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, *this, mCacheSize, mBatchSize, IsSynchronousLoadingRequired());
+  }
+  else if(mImageUrls)
+  {
+    // Ensure the batch size and cache size are no bigger than the number of URLs,
+    // and that the cache is at least as big as the batch size.
+    uint16_t numUrls   = mImageUrls->size();
+    uint16_t batchSize = std::max(std::min(mBatchSize, numUrls), MINIMUM_CACHESIZE);
+    uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE);
+    if(cacheSize < numUrls)
+    {
+      mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, cacheSize, batchSize, mFrameDelay);
+    }
+    else
+    {
+      mImageCache = new FixedImageCache(textureManager, *mImageUrls, *this, batchSize, mFrameDelay);
+    }
+  }
+
+  if(!mImageCache)
+  {
+    DALI_LOG_ERROR("mImageCache is null\n");
+  }
 }
 
 AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory)
@@ -179,14 +209,16 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image
   mImageUrl(),
   mAnimatedImageLoading(),
   mFrameIndexForJumpTo(0),
+  mCurrentFrameIndex(FIRST_FRAME_INDEX),
   mImageUrls(NULL),
   mImageCache(NULL),
   mCacheSize(2),
   mBatchSize(2),
   mFrameDelay(100),
   mLoopCount(LOOP_FOREVER),
-  mCurrentLoopIndex(0),
-  mUrlIndex(0),
+  mCurrentLoopIndex(FIRST_LOOP),
+  mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED),
+  mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
   mFrameCount(0),
   mImageSize(),
   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
@@ -200,6 +232,12 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image
 
 AnimatedImageVisual::~AnimatedImageVisual()
 {
+  // AnimatedImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
+  // 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();
+  }
   delete mImageCache;
   delete mImageUrls;
 }
@@ -255,9 +293,13 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay));
   map.Insert(Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount));
   map.Insert(Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1);
-  map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetTotalFrameCount()) : -1);
+  map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>((mAnimatedImageLoading) ? mAnimatedImageLoading.GetImageCount() : 
+                                                                                                                                     mImageCache->GetTotalFrameCount()) : -1);
 
   map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior);
+
+  map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
+  map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
 }
 
 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -296,6 +338,7 @@ void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const
       // STOP reset functionality will actually be done in a future change
       // Stop will be executed on next timer tick
       mActionStatus = DevelAnimatedImageVisual::Action::STOP;
+      mCurrentLoopIndex = FIRST_LOOP;
       if(IsOnScene())
       {
         DisplayNextFrame();
@@ -370,8 +413,25 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second);
       }
+      else if(keyValue.first == LOAD_POLICY_NAME)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
+      }
+      else if(keyValue.first == RELEASE_POLICY_NAME)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
+      }
+      else if(keyValue.first == SYNCHRONOUS_LOADING)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
+      }
     }
   }
+  // Load image immediately if LOAD_POLICY requires it
+  if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
+  {
+    PrepareTextureSet();
+  }
 }
 
 void AnimatedImageVisual::DoSetProperty(Property::Index        index,
@@ -451,6 +511,10 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       if(value.Get(frameDelay))
       {
         mFrameDelay = frameDelay;
+        if(mImageCache)
+        {
+          mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
+        }
       }
       break;
     }
@@ -489,29 +553,30 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       }
       break;
     }
+
+    case Toolkit::ImageVisual::Property::RELEASE_POLICY:
+    {
+      int releasePolicy = 0;
+      Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
+      mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::LOAD_POLICY:
+    {
+      int loadPolicy = 0;
+      Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
+      mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
+      break;
+    }
   }
 }
 
 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
 {
+  mStartFirstFrame      = true;
   mPlacementActor       = actor;
-  TextureSet textureSet = PrepareTextureSet();
-
-  // Loading animated image file is failed.
-  if(!mImageCache ||
-     (mAnimatedImageLoading && !mAnimatedImageLoading.HasLoadingSucceeded()))
-  {
-    textureSet = SetLoadingFailed();
-  }
-
-  if(textureSet) // if the image loading is successful
-  {
-    StartFirstFrame(textureSet);
-  }
-  else
-  {
-    mStartFirstFrame = true;
-  }
+  PrepareTextureSet();
 }
 
 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
@@ -525,8 +590,19 @@ 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
+    mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
+
+    TextureSet textureSet = TextureSet::New();
+    mImpl->mRenderer.SetTextures(textureSet);
+  }
+
   mPlacementActor.Reset();
   mStartFirstFrame = false;
+  mCurrentFrameIndex = FIRST_FRAME_INDEX;
+  mCurrentLoopIndex = FIRST_LOOP;
 }
 
 void AnimatedImageVisual::OnSetTransform()
@@ -546,8 +622,23 @@ void AnimatedImageVisual::UpdateShader()
   }
 }
 
+Shader AnimatedImageVisual::GenerateShader() const
+{
+  bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
+  Shader shader;
+  shader = mImageVisualShaderFactory.GetShader(
+    mFactoryCache,
+    ImageVisualShaderFeature::FeatureBuilder()
+      .ApplyDefaultTextureWrapMode(defaultWrapMode)
+      .EnableRoundedCorner(IsRoundedCornerRequired())
+      .EnableBorderline(IsBorderlineRequired()));
+  return shader;
+}
+
 void AnimatedImageVisual::OnInitialize()
 {
+  CreateImageCache();
+
   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
   Shader shader          = GenerateShader();
 
@@ -572,61 +663,7 @@ void AnimatedImageVisual::OnInitialize()
   }
 }
 
-void AnimatedImageVisual::LoadFirstBatch()
-{
-  // Ensure the batch size and cache size are no bigger than the number of URLs,
-  // and that the cache is at least as big as the batch size.
-  uint16_t numUrls   = 0;
-  uint16_t batchSize = 1;
-  uint16_t cacheSize = 1;
-
-  if(mImageUrls)
-  {
-    numUrls = mImageUrls->size();
-  }
-  else
-  {
-    numUrls = mFrameCount;
-  }
-
-  batchSize = std::min(mBatchSize, numUrls);
-  cacheSize = std::min(std::max(batchSize, mCacheSize), numUrls);
-
-  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadFirstBatch()  batchSize:%d  cacheSize:%d\n", batchSize, cacheSize);
-
-  mUrlIndex                      = 0;
-  TextureManager& textureManager = mFactoryCache.GetTextureManager();
-
-  if(mAnimatedImageLoading)
-  {
-    mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize, IsSynchronousLoadingRequired());
-  }
-  else if(mImageUrls)
-  {
-    if(batchSize > 0 && cacheSize > 0)
-    {
-      if(cacheSize < numUrls)
-      {
-        mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, cacheSize, batchSize);
-      }
-      else
-      {
-        mImageCache = new FixedImageCache(textureManager, *mImageUrls, *this, batchSize);
-      }
-    }
-    else
-    {
-      mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, 1, 1);
-    }
-  }
-
-  if(!mImageCache)
-  {
-    DALI_LOG_ERROR("mImageCache is null\n");
-  }
-}
-
-void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet)
+void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
 {
   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n");
 
@@ -643,26 +680,23 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet)
     }
   }
 
-  if(mFrameCount > 1)
+  if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
   {
-    int frameDelay = mImageCache->GetFrameInterval(0);
-    if(frameDelay == 0u)
+    if(mFrameCount > SINGLE_IMAGE_COUNT)
     {
-      frameDelay = mFrameDelay; // from URL array
+      mFrameDelayTimer = Timer::New(firstInterval);
+      mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
+      mFrameDelayTimer.Start();
     }
-    mFrameDelayTimer = Timer::New(frameDelay);
-    mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
-    mFrameDelayTimer.Start();
-  }
 
-  if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
-  {
     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n");
     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
   }
+
+  mCurrentFrameIndex = FIRST_FRAME_INDEX;
 }
 
-TextureSet AnimatedImageVisual::PrepareTextureSet()
+void AnimatedImageVisual::PrepareTextureSet()
 {
   TextureSet textureSet;
   if(mImageCache)
@@ -670,12 +704,11 @@ TextureSet AnimatedImageVisual::PrepareTextureSet()
     textureSet = mImageCache->FirstFrame();
   }
 
+  // Check whether synchronous loading is true or false for the first frame.
   if(textureSet)
   {
     SetImageSize(textureSet);
   }
-
-  return textureSet;
 }
 
 void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
@@ -691,24 +724,28 @@ void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
   }
 }
 
-void AnimatedImageVisual::FrameReady(TextureSet textureSet)
+void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
 {
   // When image visual requested to load new frame to mImageCache and it is failed.
-  if(!textureSet)
+  if(!mImageCache || !textureSet)
   {
     textureSet = SetLoadingFailed();
   }
-
   SetImageSize(textureSet);
 
   if(mStartFirstFrame)
   {
-    StartFirstFrame(textureSet);
+    mFrameCount = mImageCache->GetTotalFrameCount();
+    StartFirstFrame(textureSet, interval);
   }
   else
   {
     if(mImpl->mRenderer)
     {
+      if(mFrameDelayTimer && interval > 0u)
+      {
+        mFrameDelayTimer.SetInterval(interval);
+      }
       mImpl->mRenderer.SetTextures(textureSet);
     }
   }
@@ -721,7 +758,6 @@ bool AnimatedImageVisual::DisplayNextFrame()
 
   if(mImageCache)
   {
-    bool     nextFrame  = false;
     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
 
     if(mIsJumpTo)
@@ -735,10 +771,10 @@ bool AnimatedImageVisual::DisplayNextFrame()
     }
     else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP)
     {
-      frameIndex = 0;
+      mCurrentLoopIndex = FIRST_LOOP;
       if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME)
       {
-        frameIndex = 0;
+        frameIndex = FIRST_FRAME_INDEX;
       }
       else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME)
       {
@@ -751,13 +787,12 @@ bool AnimatedImageVisual::DisplayNextFrame()
     }
     else
     {
-      if(mFrameCount > 1)
+      if(mFrameCount > SINGLE_IMAGE_COUNT)
       {
-        nextFrame = true;
         frameIndex++;
         if(frameIndex >= mFrameCount)
         {
-          frameIndex %= mFrameCount;
+          frameIndex = FIRST_FRAME_INDEX;
           ++mCurrentLoopIndex;
         }
 
@@ -768,38 +803,24 @@ bool AnimatedImageVisual::DisplayNextFrame()
           return DisplayNextFrame();
         }
       }
-
-      unsigned int delay = mImageCache->GetFrameInterval(frameIndex);
-      if(delay > 0u)
-      {
-        if(mFrameDelayTimer.GetInterval() != delay)
-        {
-          mFrameDelayTimer.SetInterval(delay);
-        }
-      }
     }
 
     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
 
-    if(nextFrame)
-    {
-      textureSet = mImageCache->NextFrame();
-    }
-    else
-    {
-      textureSet = mImageCache->Frame(frameIndex);
-    }
-
-    continueTimer = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY) ? true : false;
-  }
+    textureSet = mImageCache->Frame(frameIndex);
 
-  if(textureSet)
-  {
-    SetImageSize(textureSet);
-    if(mImpl->mRenderer)
+    if(textureSet)
     {
-      mImpl->mRenderer.SetTextures(textureSet);
+      SetImageSize(textureSet);
+      if(mImpl->mRenderer)
+      {
+        mImpl->mRenderer.SetTextures(textureSet);
+      }
+      mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
     }
+
+    mCurrentFrameIndex = frameIndex;
+    continueTimer      = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false;
   }
 
   return continueTimer;
@@ -830,19 +851,6 @@ TextureSet AnimatedImageVisual::SetLoadingFailed()
   return textureSet;
 }
 
-Shader AnimatedImageVisual::GenerateShader() const
-{
-  bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
-  Shader shader;
-  shader = mImageVisualShaderFactory.GetShader(
-    mFactoryCache,
-    ImageVisualShaderFeature::FeatureBuilder()
-      .ApplyDefaultTextureWrapMode(defaultWrapMode)
-      .EnableRoundedCorner(IsRoundedCornerRequired())
-      .EnableBorderline(IsBorderlineRequired()));
-  return shader;
-}
-
 } // namespace Internal
 
 } // namespace Toolkit
index ad8b5ac..5e70964 100644 (file)
@@ -193,54 +193,51 @@ protected:
 
 private:
   /**
-   * Creates the renderer for the animated image
+   * @brief Initialize the animated image variables.
+   * @param[in] imageUrl The url of the animated image
    */
-  void CreateRenderer();
+  void InitializeAnimatedImage(const VisualUrl& imageUrl);
 
   /**
-   * Starts the Load of the first batch of URLs
+   * @brief Create image cache for animated image or image array.
    */
-  void LoadFirstBatch();
+  void CreateImageCache();
 
   /**
-   * Adds the texture set to the renderer, and the renderer to the
+   * @brief Adds the texture set to the renderer, and the renderer to the
    * placement actor, and starts the frame timer
-   * @param[in] textureSet The texture set to apply
+   * @param[in] textureSet    The texture set to apply
+   * @param[in] firstInterval frame interval(ms) for the first frame.
    */
-  void StartFirstFrame(TextureSet& textureSet);
+  void StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval);
 
   /**
-   * Prepares the texture set for displaying
+   * @brief Prepares the texture set for displaying
    */
-  TextureSet PrepareTextureSet();
+  void PrepareTextureSet();
 
   /**
-   * Set the image size from the texture set
+   * @brief Set the image size from the texture set
    * @param[in] textureSet The texture set to get the size from
    */
   void SetImageSize(TextureSet& textureSet);
 
   /**
-   * Called when the next frame is ready.
+   * @brief Called when the next frame is ready.
    * @param[in] textureSet the texture set to apply
+   * @param[in] interval interval(ms) for the frame
    */
-  void FrameReady(TextureSet textureSet) override;
+  void FrameReady(TextureSet textureSet, uint32_t interval) override;
 
   /**
-   * Display the next frame. It is called when the mFrameDelayTimer ticks.
-   * Returns true to ensure the timer continues running.
+   * @brief Display the next frame. It is called when the mFrameDelayTimer ticks.
+   * @return true to ensure the timer continues running.
    */
   bool DisplayNextFrame();
 
   /**
-   * Initialize the animated image variables.
-   * @param[in] imageUrl The url of the animated image
-   */
-  void InitializeAnimatedImage(const VisualUrl& imageUrl);
-
-  /**
-   * Set the state of loading fail of an image or a frame.
-   * Returns TextureSet of broken image.
+   * @brief Set the state of loading fail of an image or a frame.
+   * @return TextureSet of broken image.
    */
   TextureSet SetLoadingFailed();
 
@@ -260,6 +257,7 @@ private:
   VisualUrl                  mImageUrl;
   Dali::AnimatedImageLoading mAnimatedImageLoading; // Only needed for animated image
   uint32_t                   mFrameIndexForJumpTo;  // Frame index into textureRects
+  uint32_t                   mCurrentFrameIndex;
 
   // Variables for Multi-Image player
   ImageCache::UrlList* mImageUrls;
@@ -269,7 +267,10 @@ private:
   uint16_t             mFrameDelay;
   int16_t              mLoopCount;
   int16_t              mCurrentLoopIndex;
-  uint16_t             mUrlIndex;
+
+  // Variables for image visual policy.
+  Dali::Toolkit::ImageVisual::LoadPolicy::Type    mLoadPolicy;
+  Dali::Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;
 
   // Shared variables
   uint32_t        mFrameCount; // Number of frames
index dcb6621..192a3c4 100644 (file)
@@ -20,6 +20,9 @@
 // INTERNAL HEADERS
 #include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
 
+// EXTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+
 namespace Dali
 {
 namespace Toolkit
@@ -28,79 +31,60 @@ namespace Internal
 {
 namespace
 {
-const bool ENABLE_ORIENTATION_CORRECTION(true);
+constexpr bool ENABLE_ORIENTATION_CORRECTION(true);
+constexpr uint32_t FIRST_FRAME_INDEX = 0u;
 } // namespace
 
 FixedImageCache::FixedImageCache(
-  TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, unsigned int batchSize)
-: ImageCache(textureManager, observer, batchSize),
+  TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint32_t batchSize, uint32_t interval)
+: ImageCache(textureManager, observer, batchSize, interval),
   mImageUrls(urlList),
-  mFront(0u)
+  mFront(FIRST_FRAME_INDEX)
 {
   mReadyFlags.reserve(mImageUrls.size());
-  LoadBatch();
 }
 
 FixedImageCache::~FixedImageCache()
 {
-  if(mTextureManagerAlive)
-  {
-    for(std::size_t i = 0; i < mImageUrls.size(); ++i)
-    {
-      mTextureManager.Remove(mImageUrls[i].mTextureId, this);
-    }
-  }
+  ClearCache();
 }
 
 TextureSet FixedImageCache::Frame(uint32_t frameIndex)
 {
-  while(frameIndex > mFront)
+  TextureSet textureSet;
+  if(frameIndex >= mImageUrls.size())
+  {
+    DALI_LOG_ERROR("Wrong frameIndex requested.\n");
+    return textureSet;
+  }
+
+  while(mReadyFlags.size() < mImageUrls.size() &&
+        (frameIndex > mFront || mReadyFlags.empty()))
   {
     ++mFront;
-    if(mFront >= mImageUrls.size())
-    {
-      mFront = 0;
-    }
     LoadBatch();
   }
 
   mFront = frameIndex;
 
-  TextureSet textureSet;
-  if(IsFrontReady() == true)
+  if(IsFrontReady() && mLoadState != TextureManager::LoadState::LOAD_FAILED)
   {
     textureSet = GetFrontTextureSet();
   }
-  else
-  {
-    mWaitingForReadyFrame = true;
-  }
 
   return textureSet;
 }
 
 TextureSet FixedImageCache::FirstFrame()
 {
-  TextureSet textureSet = GetFrontTextureSet();
-
-  if(!textureSet)
-  {
-    mWaitingForReadyFrame = true;
-  }
-
-  return textureSet;
-}
-
-TextureSet FixedImageCache::NextFrame()
-{
-  TextureSet textureSet = Frame((mFront + 1) % mImageUrls.size());
+  TextureSet textureSet = Frame(FIRST_FRAME_INDEX);
 
   return textureSet;
 }
 
 uint32_t FixedImageCache::GetFrameInterval(uint32_t frameIndex) const
 {
-  return 0u;
+  return mInterval;
 }
 
 int32_t FixedImageCache::GetCurrentFrameIndex() const
@@ -121,13 +105,11 @@ bool FixedImageCache::IsFrontReady() const
 void FixedImageCache::LoadBatch()
 {
   // Try and load up to mBatchSize images, until the cache is filled.
-  // Once the cache is filled, mUrlIndex exceeds mImageUrls size and
-  // no more images are loaded.
-  bool frontFrameReady = IsFrontReady();
-
-  for(unsigned int i = 0; i < mBatchSize && mUrlIndex < mImageUrls.size(); ++i)
+  // Once the cache is filled, no more images are loaded.
+  for(unsigned int i = 0; i < mBatchSize && mReadyFlags.size() < mImageUrls.size(); ++i)
   {
-    std::string& url = mImageUrls[mUrlIndex].mUrl;
+    uint32_t frameIndex = mReadyFlags.size();
+    std::string& url = mImageUrls[frameIndex].mUrl;
 
     mReadyFlags.push_back(false);
 
@@ -135,6 +117,7 @@ void FixedImageCache::LoadBatch()
     // from within this method. This means it won't yet have a texture id, so we
     // need to account for this inside the LoadComplete method using mRequestingLoad.
     mRequestingLoad = true;
+    mLoadState = TextureManager::LoadState::LOADING;
 
     bool                               synchronousLoading = false;
     bool                               atlasingStatus     = false;
@@ -147,28 +130,9 @@ void FixedImageCache::LoadBatch()
     auto                               preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
     mTextureManager.LoadTexture(
-      url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, mImageUrls[mUrlIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply);
+      url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, mImageUrls[frameIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply);
 
-    if(loadingStatus == false) // not loading, means it's already ready.
-    {
-      SetImageFrameReady(mImageUrls[mUrlIndex].mTextureId);
-    }
     mRequestingLoad = false;
-    ++mUrlIndex;
-  }
-
-  CheckFrontFrame(frontFrameReady);
-}
-
-void FixedImageCache::SetImageFrameReady(TextureManager::TextureId textureId)
-{
-  for(std::size_t i = 0; i < mImageUrls.size(); ++i)
-  {
-    if(mImageUrls[i].mTextureId == textureId)
-    {
-      mReadyFlags[i] = true;
-      break;
-    }
   }
 }
 
@@ -179,29 +143,53 @@ TextureSet FixedImageCache::GetFrontTextureSet() const
 
 void FixedImageCache::CheckFrontFrame(bool wasReady)
 {
-  if(mWaitingForReadyFrame && wasReady == false && IsFrontReady())
+  if(wasReady == false && IsFrontReady())
   {
-    mWaitingForReadyFrame = false;
-    mObserver.FrameReady(GetFrontTextureSet());
+    mObserver.FrameReady(GetFrontTextureSet(), mInterval);
   }
 }
 
-void FixedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
+void FixedImageCache::ClearCache()
 {
-  bool frontFrameReady = IsFrontReady();
-
-  if(!mRequestingLoad)
+  if(mTextureManagerAlive)
   {
-    SetImageFrameReady(textureInformation.textureId);
+    for(std::size_t i = 0; i < mImageUrls.size(); ++i)
+    {
+      mTextureManager.Remove(mImageUrls[i].mTextureId, this);
+      mImageUrls[i].mTextureId = TextureManager::INVALID_TEXTURE_ID;
+    }
+  }
+  mReadyFlags.clear();
+  mLoadState = TextureManager::LoadState::NOT_STARTED;
+}
 
+void FixedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
+{
+  if(loadSuccess)
+  {
+    mLoadState = TextureManager::LoadState::LOAD_FINISHED;
+    bool frontFrameReady = IsFrontReady();
+    if(!mRequestingLoad)
+    {
+      for(std::size_t i = 0; i < mImageUrls.size(); ++i)
+      {
+        if(mImageUrls[i].mTextureId == textureInformation.textureId)
+        {
+          mReadyFlags[i] = true;
+          break;
+        }
+      }
+    }
+    else
+    {
+      mReadyFlags.back() = true;
+    }
     CheckFrontFrame(frontFrameReady);
   }
   else
   {
-    // LoadComplete has been called from within RequestLoad. TextureManager must
-    // therefore already have the texture cached, so make the texture ready.
-    // (Use the last texture, as the texture id hasn't been assigned yet)
-    mReadyFlags.back() = true;
+    mLoadState = TextureManager::LoadState::LOAD_FAILED;
+    mObserver.FrameReady(TextureSet(), 0);
   }
 }
 
index 6a2394a..2d446f0 100644 (file)
@@ -36,6 +36,7 @@ public:
    * @param[in] urlList List of urls to cache
    * @param[in] observer FrameReady observer
    * @param[in] batchSize The size of a batch to load
+   * @param[in] interval Time interval between each frame
    *
    * This will start loading textures immediately, according to the
    * batch and cache sizes. The cache is as large as the number of urls.
@@ -43,79 +44,79 @@ public:
   FixedImageCache(TextureManager&                 textureManager,
                   UrlList&                        urlList,
                   ImageCache::FrameReadyObserver& observer,
-                  unsigned int                    batchSize);
+                  uint32_t                        batchSize,
+                  uint32_t                        interval);
 
   ~FixedImageCache() override;
 
   /**
-   * Get the Nth frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
+   * @copydoc Internal::ImageCache::Frame()
    */
   TextureSet Frame(uint32_t frameIndex) override;
 
   /**
-   * Get the first frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
+   * @copydoc Internal::ImageCache::FirstFrame()
    */
   TextureSet FirstFrame() override;
 
   /**
-   * Get the next frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
-   */
-  TextureSet NextFrame() override;
-
-  /**
-   * Get the interval of Nth frame.
+   * @copydoc Internal::ImageCache::GetFrameInterval()
    */
   uint32_t GetFrameInterval(uint32_t frameIndex) const override;
 
   /**
-   * Get the current rendered frame index.
-   * If there isn't any loaded frame, returns -1.
+   * @copydoc Internal::ImageCache::GetCurrentFrameIndex()
    */
   int32_t GetCurrentFrameIndex() const override;
 
   /**
-   * Get total frame count of the animated image file.
+   * @copydoc Internal::ImageCache::GetTotalFrameCount()
    */
   int32_t GetTotalFrameCount() const override;
 
+  /**
+   * @copydoc Internal::ImageCache::ClearCache()
+   */
+  void ClearCache() override;
+
 private:
   /**
+   * @brief Check whether the front frame is ready or not.
+   *
    * @return true if the front frame is ready
    */
   bool IsFrontReady() const;
 
   /**
-   * Load the next batch of images
+   * @brief Load the next batch of images
    */
   void LoadBatch();
 
   /**
-   * Find the matching image frame, and set it to ready
-   */
-  void SetImageFrameReady(TextureManager::TextureId textureId);
-
-  /**
-   * Get the texture set of the front frame.
-   * @return the texture set
+   * @brief Get the texture set of the front frame.
+   *
+   * @return the texture set of the front of Cache.
    */
   TextureSet GetFrontTextureSet() const;
 
   /**
-   * Check if the front frame has become ready - if so, inform observer
+   * @brief Check if the front frame has become ready - if so, inform observer
+   *
    * @param[in] wasReady Readiness before call.
    */
   void CheckFrontFrame(bool wasReady);
 
 protected:
+  /**
+   * @copydoc Toolkit::TextureUploadObserver::LoadComplete()
+   */
   void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override;
 
 private:
-  std::vector<UrlStore>& mImageUrls;
-  std::vector<bool>      mReadyFlags;
-  unsigned int           mFront;
+  std::vector<UrlStore>&                 mImageUrls;
+  std::vector<bool>                      mReadyFlags;
+  std::vector<TextureManager::LoadState> mLoadStates;
+  uint32_t                               mFront;
 };
 
 } //namespace Internal
index cccb085..27c3ef2 100644 (file)
@@ -24,12 +24,13 @@ namespace Internal
 {
 ImageCache::ImageCache(TextureManager&                 textureManager,
                        ImageCache::FrameReadyObserver& observer,
-                       unsigned int                    batchSize)
+                       uint32_t                        batchSize,
+                       uint32_t                        interval)
 : mTextureManager(textureManager),
   mObserver(observer),
   mBatchSize(batchSize),
-  mUrlIndex(0u),
-  mWaitingForReadyFrame(false),
+  mInterval(interval),
+  mLoadState(TextureManager::LoadState::NOT_STARTED),
   mRequestingLoad(false),
   mTextureManagerAlive(true)
 {
@@ -49,6 +50,11 @@ void ImageCache::TextureManagerDestroyed()
   mTextureManagerAlive = false;
 }
 
+void ImageCache::SetInterval(uint32_t interval)
+{
+  mInterval = interval;
+}
+
 } //namespace Internal
 } //namespace Toolkit
 } //namespace Dali
index 3200902..19a8f52 100644 (file)
@@ -37,10 +37,11 @@ public:
   {
   public:
     /**
-     * Informs observer when the next texture set is ready to display
+     * @brief Informs observer when the next texture set is ready to display
      * @param[in] textureSet The ready texture set
+     * @param[in] interval interval(ms) for the frame
      */
-    virtual void FrameReady(TextureSet textureSet) = 0;
+    virtual void FrameReady(TextureSet textureSet, uint32_t interval) = 0;
   };
 
   struct UrlStore
@@ -56,69 +57,89 @@ public:
 
 public:
   /**
-   * Constructor.
+   * @brief Constructor.
    * @param[in] textureManager The texture manager
    * @param[in] urlList List of urls to cache
    * @param[in] observer FrameReady observer
    * @param[in] batchSize The size of a batch to load
+   * @param[in] interval Time interval(ms) between each frame 
    *
    * This will start loading textures immediately, according to the
    * batch and cache sizes. The cache is as large as the number of urls.
    */
   ImageCache(TextureManager&                 textureManager,
              ImageCache::FrameReadyObserver& observer,
-             unsigned int                    batchSize);
+             uint32_t                        batchSize,
+             uint32_t                        interval);
 
   virtual ~ImageCache();
 
   /**
-   * Get the first frame. If it's not ready, this will trigger the
+   * @brief Get the first frame. If it's not ready, this will trigger the
    * sending of FrameReady() when the image becomes ready.
+   *
+   * @return TextureSet of the first frame.
    */
   virtual TextureSet FirstFrame() = 0;
 
   /**
-   * Get the next frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
-   */
-  virtual TextureSet NextFrame() = 0;
-
-  /**
-   * Get the Nth frame. If it's not ready, this will trigger the
+   * @brief Get the Nth frame. If it's not ready, this will trigger the
    * sending of FrameReady() when the image becomes ready.
+   *
+   * @param[in] frameIndex required frame index to be returned.
+   * @return TextureSet of the frame index.
    */
   virtual TextureSet Frame(uint32_t frameIndex) = 0;
 
   /**
-   * Get the interval of Nth frame.
+   * @brief Get the interval(ms) of Nth frame.
+   *
+   * @param[in] frameIndex frame index to get frame interval.
+   * @return Time interval in millisecond between frames of frameIndex and frameIndex + 1.
    */
   virtual uint32_t GetFrameInterval(uint32_t frameIndex) const = 0;
 
   /**
-   * Get the current rendered frame index.
+   * @brief Get the current rendered frame index.
    * If there isn't any loaded frame, returns -1.
+   *
+   * @return Frame index of currently showing frame.
    */
   virtual int32_t GetCurrentFrameIndex() const = 0;
 
   /**
-   * Get total frame count of the animated image file.
+   * @brief Get total frame count of the animated image file.
+   *
+   * @return Total frame count of the animated image file.
    */
   virtual int32_t GetTotalFrameCount() const = 0;
 
+  /**
+   * @brief Clears animated image cache and remove loaded textures.
+   */
+  virtual void ClearCache() = 0;
+
+  /**
+   * @brief Set default interval(ms) between each frame.
+   *
+   * @param[in] interval time interval in millisecond to be used as default interval.
+   */
+  virtual void SetInterval(uint32_t interval);
+
 private:
   /**
-   * Called before the texture manager is destroyed.
+   * @brief Called before the texture manager is destroyed.
    */
   void TextureManagerDestroyed() final;
 
 protected:
-  TextureManager&     mTextureManager;
-  FrameReadyObserver& mObserver;
-  unsigned int        mBatchSize;
-  unsigned int        mUrlIndex;
-  bool                mWaitingForReadyFrame : 1;
-  bool                mRequestingLoad : 1;
-  bool                mTextureManagerAlive : 1;
+  TextureManager&           mTextureManager;
+  FrameReadyObserver&       mObserver;
+  uint32_t                  mBatchSize;
+  uint32_t                  mInterval;
+  TextureManager::LoadState mLoadState;
+  bool                      mRequestingLoad : 1;
+  bool                      mTextureManagerAlive : 1;
 };
 
 } //namespace Internal
index 490c3be..473f276 100644 (file)
@@ -17,8 +17,6 @@
 // CLASS HEADER
 #include "rolling-animated-image-cache.h"
 
-// EXTERNAL HEADERS
-
 // INTERNAL HEADERS
 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
 #include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
@@ -45,7 +43,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
 #define LOG_CACHE
 #endif
 
-const bool ENABLE_ORIENTATION_CORRECTION(true);
+static constexpr bool ENABLE_ORIENTATION_CORRECTION(true);
 
 } // namespace
 
@@ -55,32 +53,30 @@ namespace Toolkit
 {
 namespace Internal
 {
+namespace
+{
+static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u;
+static constexpr uint32_t FIRST_FRAME_INDEX  = 0u;
+} // namespace
+
 RollingAnimatedImageCache::RollingAnimatedImageCache(
-  TextureManager& textureManager, AnimatedImageLoading& animatedImageLoading, uint32_t frameCount, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading)
-: ImageCache(textureManager, observer, batchSize),
+  TextureManager& textureManager, AnimatedImageLoading& animatedImageLoading, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading)
+: ImageCache(textureManager, observer, batchSize, 0u),
   mAnimatedImageLoading(animatedImageLoading),
-  mFrameCount(frameCount),
-  mFrameIndex(0),
+  mFrameCount(SINGLE_IMAGE_COUNT),
+  mFrameIndex(FIRST_FRAME_INDEX),
   mCacheSize(cacheSize),
   mQueue(cacheSize),
-  mIsSynchronousLoading(isSynchronousLoading),
-  mOnLoading(false)
+  mIsSynchronousLoading(isSynchronousLoading)
 {
   mImageUrls.resize(mFrameCount);
   mIntervals.assign(mFrameCount, 0);
-  LoadBatch();
 }
 
 RollingAnimatedImageCache::~RollingAnimatedImageCache()
 {
-  if(mTextureManagerAlive)
-  {
-    while(!mQueue.IsEmpty())
-    {
-      ImageFrame imageFrame = mQueue.PopFront();
-      mTextureManager.Remove(mImageUrls[imageFrame.mFrameNumber].mTextureId, this);
-    }
-  }
+  ClearCache();
+  mAnimatedImageLoading.Reset();
 }
 
 TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex)
@@ -95,53 +91,53 @@ TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex)
   }
 
   TextureSet textureSet;
+  uint32_t   batchFrameIndex = frameIndex;
   // If we need to load new frame that are not stored in queue.
   // Load the frame synchronously.
+  bool synchronouslyLoaded = false;
   if(mIsSynchronousLoading && mQueue.IsEmpty())
   {
-    bool synchronousLoading = true;
-    textureSet              = mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this);
-    mFrameIndex             = (frameIndex + 1) % mFrameCount;
+    textureSet          = RequestFrameLoading(frameIndex, frameIndex == FIRST_FRAME_INDEX, true);
+    batchFrameIndex     = (frameIndex + 1) % mFrameCount;
+    uint32_t interval = 0u;
+    if(textureSet)
+    {
+      synchronouslyLoaded = true;
+      interval = mAnimatedImageLoading.GetFrameInterval(mQueue.Back().mFrameNumber);
+    }
+    MakeFrameReady(synchronouslyLoaded, textureSet, interval);
   }
 
-  if(popExist || mQueue.IsEmpty())
+  if(popExist || mQueue.IsEmpty() || synchronouslyLoaded)
   {
     // If the frame of frameIndex was already loaded, load batch from the last frame of queue
     if(!mQueue.IsEmpty())
     {
       if(!mLoadWaitingQueue.empty())
       {
-        mFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount;
+        batchFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount;
       }
       else
       {
-        mFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount;
+        batchFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount;
       }
     }
     else
     {
-      mOnLoading = false;
       // If the request is for the first frame or a jumped frame(JUMP_TO) remove current waiting queue.
       mLoadWaitingQueue.clear();
       // If the queue is empty, and the frame of frameIndex is not loaded synchronously. load batch from the frame of frameIndex
       if(!textureSet)
       {
-        mFrameIndex = frameIndex;
+        batchFrameIndex = frameIndex;
       }
     }
-    LoadBatch();
+    LoadBatch(batchFrameIndex);
   }
 
-  if(!textureSet)
+  if(!textureSet && mLoadState != TextureManager::LoadState::LOAD_FAILED && IsFrontReady() == true)
   {
-    if(IsFrontReady() == true)
-    {
-      textureSet = GetFrontTextureSet();
-    }
-    else
-    {
-      mWaitingForReadyFrame = true;
-    }
+    textureSet = GetFrontTextureSet();
   }
 
   return textureSet;
@@ -149,32 +145,17 @@ TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex)
 
 TextureSet RollingAnimatedImageCache::FirstFrame()
 {
-  return Frame(0u);
-}
-
-TextureSet RollingAnimatedImageCache::NextFrame()
-{
-  TextureSet textureSet;
-  if(!mQueue.IsEmpty())
-  {
-    uint32_t frameIndex = mQueue.Front().mFrameNumber;
-    if(IsFrontReady())
-    {
-      frameIndex = (frameIndex + 1) % mFrameCount;
-    }
-    textureSet = Frame(frameIndex);
-  }
-  else
-  {
-    DALI_LOG_ERROR("Cache is empty.");
-  }
-
+  TextureSet textureSet = Frame(FIRST_FRAME_INDEX);
   return textureSet;
 }
 
 uint32_t RollingAnimatedImageCache::GetFrameInterval(uint32_t frameIndex) const
 {
-  return mAnimatedImageLoading.GetFrameInterval(frameIndex);
+  if(frameIndex >= mIntervals.size())
+  {
+    return 0u;
+  }
+  return mIntervals[frameIndex];
 }
 
 int32_t RollingAnimatedImageCache::GetCurrentFrameIndex() const
@@ -196,7 +177,7 @@ bool RollingAnimatedImageCache::IsFrontReady() const
   return (!mQueue.IsEmpty() && mQueue.Front().mReady);
 }
 
-void RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex)
+TextureSet RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex, bool useCache, bool synchronousLoading)
 {
   ImageFrame imageFrame;
   imageFrame.mFrameNumber = frameIndex;
@@ -204,39 +185,45 @@ void RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex)
 
   mQueue.PushBack(imageFrame);
 
-  mRequestingLoad = true;
+  mLoadState = TextureManager::LoadState::LOADING;
+
+  TextureManager::TextureId loadTextureId = TextureManager::INVALID_TEXTURE_ID;
+  TextureSet                textureSet    = mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading,
+                                                                   frameIndex,
+                                                                   loadTextureId,
+                                                                   SamplingMode::BOX_THEN_LINEAR,
+                                                                   Dali::WrapMode::Type::DEFAULT,
+                                                                   Dali::WrapMode::Type::DEFAULT,
+                                                                   synchronousLoading,
+                                                                   useCache,
+                                                                   this);
 
-  bool synchronousLoading = false;
-  mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this);
+  mImageUrls[frameIndex].mTextureId = loadTextureId;
 
-  mRequestingLoad = false;
+  return textureSet;
 }
 
-void RollingAnimatedImageCache::LoadBatch()
+void RollingAnimatedImageCache::LoadBatch(uint32_t frameIndex)
 {
   // Try and load up to mBatchSize images, until the cache is filled.
   // Once the cache is filled, as frames progress, the old frame is
   // removed, and another frame is loaded
-
-  bool frontFrameReady = IsFrontReady();
-  for(unsigned int i = 0; i < mBatchSize && mQueue.Count() + mLoadWaitingQueue.size() < static_cast<uint32_t>(mCacheSize) && !mQueue.IsFull(); ++i)
+  uint32_t minimumSize = std::min(mCacheSize, mFrameCount);
+  for(uint32_t i = 0; i < mBatchSize && (mQueue.Count() + mLoadWaitingQueue.size()) < minimumSize; ++i)
   {
-    if(!mOnLoading)
+    if(mLoadState != TextureManager::LoadState::LOADING)
     {
-      mOnLoading = true;
-      RequestFrameLoading(mFrameIndex);
+      RequestFrameLoading(frameIndex, frameIndex == FIRST_FRAME_INDEX, false);
     }
     else
     {
-      mLoadWaitingQueue.push_back(mFrameIndex);
+      mLoadWaitingQueue.push_back(frameIndex);
     }
 
-    mFrameIndex++;
-    mFrameIndex %= mFrameCount;
+    frameIndex++;
+    frameIndex %= mFrameCount;
   }
 
-  CheckFrontFrame(frontFrameReady);
-
   LOG_CACHE;
 }
 
@@ -265,46 +252,84 @@ TextureManager::TextureId RollingAnimatedImageCache::GetCachedTextureId(int inde
   return mImageUrls[mQueue[index].mFrameNumber].mTextureId;
 }
 
-void RollingAnimatedImageCache::CheckFrontFrame(bool wasReady)
+void RollingAnimatedImageCache::ClearCache()
 {
-  if(mWaitingForReadyFrame && wasReady == false && IsFrontReady())
+  while(mTextureManagerAlive && !mQueue.IsEmpty())
   {
-    mWaitingForReadyFrame = false;
-    mObserver.FrameReady(GetFrontTextureSet());
+    ImageFrame imageFrame = mQueue.PopFront();
+    mTextureManager.Remove(mImageUrls[imageFrame.mFrameNumber].mTextureId, this);
+    mImageUrls[imageFrame.mFrameNumber].mTextureId = TextureManager::INVALID_TEXTURE_ID;
   }
+  mLoadWaitingQueue.clear();
+  mLoadState = TextureManager::LoadState::NOT_STARTED;
 }
 
-void RollingAnimatedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
+void RollingAnimatedImageCache::MakeFrameReady(bool loadSuccess, TextureSet textureSet, uint32_t interval)
 {
-  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId);
-  LOG_CACHE;
-
-  bool frontFrameReady = IsFrontReady();
-
-  if(!mRequestingLoad)
+  if(!loadSuccess)
   {
-    SetImageFrameReady(textureInformation.textureId);
-
-    CheckFrontFrame(frontFrameReady);
+    mLoadState = TextureManager::LoadState::LOAD_FAILED;
+    mObserver.FrameReady(TextureSet(), 0);
   }
   else
   {
-    // LoadComplete has been called from within RequestLoad. TextureManager must
-    // therefore already have the texture cached, so make the texture ready.
-    // (Use the last texture, as the texture id hasn't been assigned yet)
-    mQueue.Back().mReady = true;
+    mLoadState = TextureManager::LoadState::LOAD_FINISHED;
+
+    // Reset size of Queue according to the real frame count.
+    if(mFrameCount != mAnimatedImageLoading.GetImageCount())
+    {
+      mFrameCount = mAnimatedImageLoading.GetImageCount();
+      mImageUrls.resize(mFrameCount);
+      mIntervals.assign(mFrameCount, 0u);
+    }
+
+    bool frontFrameReady = IsFrontReady();
+    // Because only one frame is on loading and the others are in mLoadWaitingQueue,
+    // mQueue.Back() is always the frame currently loaded.
+    mQueue.Back().mReady                   = true;
+    mIntervals[mQueue.Back().mFrameNumber] = interval;
+    // Check whether currently loaded frame is front of queue or not.
+    // If it is, notify frame ready to observer.
+    if(frontFrameReady == false && IsFrontReady())
+    {
+      mObserver.FrameReady(textureSet, interval);
+    }
   }
+}
 
-  mOnLoading = false;
-  // The frames of a single animated image can not be loaded parallelly.
-  // Therefore, a frame is now loading, other orders are waiting.
-  // And, after the frame is loaded, requests load of next order.
-  if(!mLoadWaitingQueue.empty())
+void RollingAnimatedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
+{
+  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId);
+  LOG_CACHE;
+
+  MakeFrameReady(loadSuccess, mTextureManager.GetTextureSet(textureInformation.textureId), textureInformation.interval);
+
+  if(loadSuccess)
   {
-    uint32_t loadingIndex = mLoadWaitingQueue.front();
-    mLoadWaitingQueue.erase(mLoadWaitingQueue.begin());
-    mOnLoading = true;
-    RequestFrameLoading(loadingIndex);
+    // The frames of a single animated image can not be loaded parallelly.
+    // Therefore, a frame is now loading, other orders are waiting.
+    // And, after the frame is loaded, requests load of next order.
+    if(!mLoadWaitingQueue.empty())
+    {
+      uint32_t loadingIndex = mLoadWaitingQueue.front();
+      mLoadWaitingQueue.erase(mLoadWaitingQueue.begin());
+      RequestFrameLoading(loadingIndex, loadingIndex == FIRST_FRAME_INDEX, false);
+    }
+    else if(mQueue.Count() == 1u && textureInformation.frameCount > SINGLE_IMAGE_COUNT)
+    {
+      // There is only an image in queue and no waiting queue.
+      // Request to load batch once again.
+      uint32_t batchFrameIndex = 0u;
+      if(!mLoadWaitingQueue.empty())
+      {
+        batchFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount;
+      }
+      else
+      {
+        batchFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount;
+      }
+      LoadBatch(batchFrameIndex);
+    }
   }
 
   LOG_CACHE;
index 08aec52..605fbb9 100644 (file)
@@ -40,10 +40,9 @@ class RollingAnimatedImageCache : public ImageCache, public TextureUploadObserve
 {
 public:
   /**
-   * Constructor.
+   * @brief Constructor.
    * @param[in] textureManager The texture manager
    * @param[in] animatedImageLoader The loaded animated image
-   * @param[in] frameCount The number of frames in the animated image
    * @param[in] observer FrameReady observer
    * @param[in] cacheSize The size of the cache
    * @param[in] batchSize The size of a batch to load
@@ -54,112 +53,128 @@ public:
    */
   RollingAnimatedImageCache(TextureManager&                 textureManager,
                             AnimatedImageLoading&           animatedImageLoader,
-                            uint32_t                        frameCount,
                             ImageCache::FrameReadyObserver& observer,
                             uint16_t                        cacheSize,
                             uint16_t                        batchSize,
                             bool                            isSynchronousLoading);
 
   /**
-   * Destructor
+   * @brief Destructor
    */
   ~RollingAnimatedImageCache() override;
 
   /**
-   * Get the Nth frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
+   * @copydoc Internal::ImageCache::Frame()
    */
   TextureSet Frame(uint32_t frameIndex) override;
 
   /**
-   * Get the first frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
+   * @copydoc Internal::ImageCache::FirstFrame()
    */
   TextureSet FirstFrame() override;
 
   /**
-   * Get the next frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
-   */
-  TextureSet NextFrame() override;
-
-  /**
-   * Get the interval of Nth frame.
+   * @copydoc Internal::ImageCache::GetFrameInterval()
    */
   uint32_t GetFrameInterval(uint32_t frameIndex) const override;
 
   /**
-   * Get the current rendered frame index.
-   * If there isn't any loaded frame, returns -1.
+   * @copydoc Internal::ImageCache::GetCurrentFrameIndex()
    */
   int32_t GetCurrentFrameIndex() const override;
 
   /**
-   * Get total frame count of the animated image file.
+   * @copydoc Internal::ImageCache::GetTotalFrameCount()
    */
   int32_t GetTotalFrameCount() const override;
 
+  /**
+   * @copydoc Internal::ImageCache::ClearCache()
+   */
+  void ClearCache() override;
+
 private:
   /**
+   * @brief Check whether the front frame is ready or not.
+   *
    * @return true if the front frame is ready
    */
   bool IsFrontReady() const;
 
   /**
-   * Request to Load a frame
+   * @brief Request to Load a frame
+   *
+   * @param[in] frameIndex          index of frame to be loaded.
+   * @param[in] useCache            true if this frame loading uses cache.
+   * @param[in] synchronousLoading  true if the frame should be loaded synchronously
+   *
+   * @return the texture set currently loaded.
    */
-  void RequestFrameLoading(uint32_t frameIndex);
+  TextureSet RequestFrameLoading(uint32_t frameIndex, bool useCache, bool synchronousLoading);
 
   /**
-   * Load the next batch of images
+   * @brief Load the next batch of images
+   *
+   * @param[in] frameIndex starting frame index of batch to be loaded.
    */
-  void LoadBatch();
+  void LoadBatch(uint32_t frameIndex);
 
   /**
-   * Find the matching image frame, and set it to ready
+   * @brief Find the matching image frame, and set it to ready
+   *
+   * @param[in] textureId texture id to be marked as ready.
    */
   void SetImageFrameReady(TextureManager::TextureId textureId);
 
   /**
-   * Get the texture set of the front frame.
-   * @return the texture set
+   * @brief Get the texture set of the front frame.
+   *
+   * @return the texture set of the front of Cache.
    */
   TextureSet GetFrontTextureSet() const;
 
   /**
-   * Get the texture id of the given index
+   * @brief Get the texture id of the given index
+   *
+   * @param[in] index index of the queue.
    */
   TextureManager::TextureId GetCachedTextureId(int index) const;
 
   /**
-   * Check if the front frame has become ready - if so, inform observer
-   * @param[in] wasReady Readiness before call.
+   * @brief Make the loaded frame ready and notify it to the texture upload observer
+   *
+   * @param[in] loadSuccess whether the loading is succeded or not.
+   * @param[in] textureSet textureSet for this frame.
+   * @param[in] interval interval between this frame and next frame.
    */
-  void CheckFrontFrame(bool wasReady);
+  void MakeFrameReady(bool loadSuccess, TextureSet textureSet, uint32_t interval);
 
 protected:
+  /**
+   * @copydoc Toolkit::TextureUploadObserver::LoadComplete()
+   */
   void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override;
 
 private:
+
   /**
    * Secondary class to hold readiness and index into url
    */
   struct ImageFrame
   {
-    unsigned int mFrameNumber = 0u;
-    bool         mReady       = false;
+    uint32_t mFrameNumber = 0u;
+    bool     mReady       = false;
   };
 
   Dali::AnimatedImageLoading mAnimatedImageLoading;
   uint32_t                   mFrameCount;
-  int                        mFrameIndex;
-  int                        mCacheSize;
+  uint32_t                   mFrameIndex;
+  uint32_t                   mCacheSize;
   std::vector<UrlStore>      mImageUrls;
   std::vector<int32_t>       mIntervals;
   std::vector<uint32_t>      mLoadWaitingQueue;
   CircularQueue<ImageFrame>  mQueue;
   bool                       mIsSynchronousLoading;
-  bool                       mOnLoading;
 };
 
 } // namespace Internal
index caecf39..08fe0d0 100644 (file)
@@ -44,7 +44,9 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
 #define LOG_CACHE
 #endif
 
-const bool ENABLE_ORIENTATION_CORRECTION(true);
+static constexpr bool ENABLE_ORIENTATION_CORRECTION(true);
+
+static constexpr uint32_t FIRST_FRAME_INDEX = 0u;
 
 } // namespace
 
@@ -55,100 +57,60 @@ namespace Toolkit
 namespace Internal
 {
 RollingImageCache::RollingImageCache(
-  TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize)
-: ImageCache(textureManager, observer, batchSize),
+  TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, uint32_t interval)
+: ImageCache(textureManager, observer, batchSize, interval),
   mImageUrls(urlList),
   mQueue(cacheSize)
 {
-  LoadBatch();
 }
 
 RollingImageCache::~RollingImageCache()
 {
-  if(mTextureManagerAlive)
-  {
-    while(!mQueue.IsEmpty())
-    {
-      ImageFrame imageFrame = mQueue.PopFront();
-      mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this);
-    }
-  }
+  ClearCache();
 }
 
 TextureSet RollingImageCache::Frame(uint32_t frameIndex)
 {
-  // If a frame of frameIndex is not loaded, clear the queue and remove all loaded textures.
-  if(mImageUrls[frameIndex].mTextureId == TextureManager::INVALID_TEXTURE_ID)
+  // Pop frames until the frame of frameIndex become front frame.
+  bool popExist = false;
+  while(!mQueue.IsEmpty() && mQueue.Front().mUrlIndex != frameIndex)
   {
-    mUrlIndex = frameIndex;
-    while(!mQueue.IsEmpty())
-    {
-      ImageFrame imageFrame = mQueue.PopFront();
-      mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this);
-      mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID;
-    }
-    LoadBatch();
+    ImageFrame imageFrame = mQueue.PopFront();
+    mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this);
+    mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID;
+    popExist                                    = true;
   }
-  // If the frame is already loaded, remove previous frames of the frame in the queue
-  // and load new frames amount of removed frames.
-  else
+
+  // TODO: synchronous loading of first frame.
+  if(popExist || mQueue.IsEmpty())
   {
-    bool popExist = false;
-    while(!mQueue.IsEmpty() && mQueue.Front().mUrlIndex != frameIndex)
-    {
-      ImageFrame imageFrame = mQueue.PopFront();
-      mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this);
-      mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID;
-      popExist                                    = true;
-    }
-    if(popExist)
+    uint32_t batchFrameIndex = frameIndex;
+    // If the frame of frameIndex was already loaded, load batch from the last frame of queue
+    if(!mQueue.IsEmpty())
     {
-      mUrlIndex = (mQueue.Back().mUrlIndex + 1) % mImageUrls.size();
-      LoadBatch();
+      batchFrameIndex = (mQueue.Back().mUrlIndex + 1) % mImageUrls.size();
     }
+    LoadBatch(batchFrameIndex);
   }
 
   TextureSet textureSet;
-  if(IsFrontReady() == true)
+  if(IsFrontReady() == true && mLoadState != TextureManager::LoadState::LOAD_FAILED)
   {
     textureSet = GetFrontTextureSet();
   }
-  else
-  {
-    mWaitingForReadyFrame = true;
-  }
 
   return textureSet;
 }
 
 TextureSet RollingImageCache::FirstFrame()
 {
-  return Frame(0u);
-}
-
-TextureSet RollingImageCache::NextFrame()
-{
-  TextureSet textureSet;
-  if(!mQueue.IsEmpty())
-  {
-    uint32_t frameIndex = mQueue.Front().mUrlIndex;
-    if(IsFrontReady())
-    {
-      frameIndex = (frameIndex + 1) % mImageUrls.size();
-    }
-    textureSet = Frame(frameIndex);
-  }
-  else
-  {
-    DALI_LOG_ERROR("Cache is empty.");
-  }
-
+  TextureSet textureSet = Frame(FIRST_FRAME_INDEX);
   return textureSet;
 }
 
 uint32_t RollingImageCache::GetFrameInterval(uint32_t frameIndex) const
 {
-  return 0u;
+  return mInterval;
 }
 
 int32_t RollingImageCache::GetCurrentFrameIndex() const
@@ -170,30 +132,26 @@ bool RollingImageCache::IsFrontReady() const
   return (!mQueue.IsEmpty() && mQueue.Front().mReady);
 }
 
-void RollingImageCache::LoadBatch()
+void RollingImageCache::LoadBatch(uint32_t frameIndex)
 {
   // Try and load up to mBatchSize images, until the cache is filled.
   // Once the cache is filled, as frames progress, the old frame is
   // cleared, but not erased, and another image is loaded
-  bool frontFrameReady = IsFrontReady();
-
   for(unsigned int i = 0; i < mBatchSize && !mQueue.IsFull(); ++i)
   {
     ImageFrame imageFrame;
 
-    std::string& url     = mImageUrls[mUrlIndex].mUrl;
-    imageFrame.mUrlIndex = mUrlIndex;
+    std::string& url     = mImageUrls[frameIndex].mUrl;
+    imageFrame.mUrlIndex = frameIndex;
     imageFrame.mReady    = false;
 
-    ++mUrlIndex;
-    mUrlIndex %= mImageUrls.size();
-
     mQueue.PushBack(imageFrame);
 
     // Note, if the image is already loaded, then LoadComplete will get called
     // from within this method. This means it won't yet have a texture id, so we
     // need to account for this inside the LoadComplete method using mRequestingLoad.
     mRequestingLoad = true;
+    mLoadState = TextureManager::LoadState::LOADING;
 
     bool                               synchronousLoading = false;
     bool                               atlasingStatus     = false;
@@ -205,24 +163,15 @@ void RollingImageCache::LoadBatch()
     Dali::ImageDimensions              textureRectSize;
     auto                               preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
-    mTextureManager.LoadTexture(
-      url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, mImageUrls[imageFrame.mUrlIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply);
+    TextureManager::TextureId loadTextureId = TextureManager::INVALID_TEXTURE_ID;
+    TextureSet                textureSet    = mTextureManager.LoadTexture(
+      url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, loadTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply);
+    mImageUrls[imageFrame.mUrlIndex].mTextureId = loadTextureId;
 
     mRequestingLoad = false;
-  }
 
-  CheckFrontFrame(frontFrameReady);
-}
-
-void RollingImageCache::SetImageFrameReady(TextureManager::TextureId textureId)
-{
-  for(std::size_t i = 0; i < mQueue.Count(); ++i)
-  {
-    if(GetCachedTextureId(i) == textureId)
-    {
-      mQueue[i].mReady = true;
-      break;
-    }
+    ++frameIndex;
+    frameIndex %= mImageUrls.size();
   }
 }
 
@@ -237,13 +186,15 @@ TextureManager::TextureId RollingImageCache::GetCachedTextureId(int index) const
   return mImageUrls[mQueue[index].mUrlIndex].mTextureId;
 }
 
-void RollingImageCache::CheckFrontFrame(bool wasReady)
+void RollingImageCache::ClearCache()
 {
-  if(mWaitingForReadyFrame && wasReady == false && IsFrontReady())
+  while(mTextureManagerAlive && !mQueue.IsEmpty())
   {
-    mWaitingForReadyFrame = false;
-    mObserver.FrameReady(GetFrontTextureSet());
+    ImageFrame imageFrame = mQueue.PopFront();
+    mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this);
+    mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID;
   }
+  mLoadState = TextureManager::LoadState::NOT_STARTED;
 }
 
 void RollingImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
@@ -251,20 +202,38 @@ void RollingImageCache::LoadComplete(bool loadSuccess, TextureInformation textur
   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId);
   LOG_CACHE;
 
-  bool frontFrameReady = IsFrontReady();
-
-  if(!mRequestingLoad)
+  if(loadSuccess)
   {
-    SetImageFrameReady(textureInformation.textureId);
+    mLoadState = TextureManager::LoadState::LOAD_FINISHED;
+    bool frontFrameReady = IsFrontReady();
+    if(!mRequestingLoad)
+    {
+      for(std::size_t i = 0; i < mQueue.Count(); ++i)
+      {
+        if(GetCachedTextureId(i) == textureInformation.textureId)
+        {
+          mQueue[i].mReady = true;
+          break;
+        }
+      }
+    }
+    else
+    {
+      // LoadComplete has been called from within RequestLoad. TextureManager must
+      // therefore already have the texture cached, so make the texture ready.
+      // (Use the last texture, as the texture id hasn't been assigned yet)
+      mQueue.Back().mReady = true;
+    }
 
-    CheckFrontFrame(frontFrameReady);
+    if(!frontFrameReady && IsFrontReady())
+    {
+      mObserver.FrameReady(mTextureManager.GetTextureSet(textureInformation.textureId), mInterval);
+    }
   }
   else
   {
-    // LoadComplete has been called from within RequestLoad. TextureManager must
-    // therefore already have the texture cached, so make the texture ready.
-    // (Use the last texture, as the texture id hasn't been assigned yet)
-    mQueue.Back().mReady = true;
+    mLoadState = TextureManager::LoadState::LOAD_FAILED;
+    mObserver.FrameReady(TextureSet(), 0);
   }
 
   LOG_CACHE;
index 7647e31..98ba45c 100644 (file)
@@ -42,6 +42,7 @@ public:
    * @param[in] observer FrameReady observer
    * @param[in] cacheSize The size of the cache
    * @param[in] batchSize The size of a batch to load
+   * @param[in] interval Time interval between each frame
    *
    * This will start loading textures immediately, according to the
    * batch and cache sizes.
@@ -50,7 +51,8 @@ public:
                     UrlList&                        urlList,
                     ImageCache::FrameReadyObserver& observer,
                     uint16_t                        cacheSize,
-                    uint16_t                        batchSize);
+                    uint16_t                        batchSize,
+                    uint32_t                        interval);
 
   /**
    * Destructor
@@ -58,73 +60,75 @@ public:
   ~RollingImageCache() override;
 
   /**
-   * Get the Nth frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
+   * @copydoc Internal::ImageCache::Frame()
    */
   TextureSet Frame(uint32_t frameIndex) override;
 
   /**
-   * Get the first frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
+   * @copydoc Internal::ImageCache::FirstFrame()
    */
   TextureSet FirstFrame() override;
 
   /**
-   * Get the next frame. If it's not ready, this will trigger the
-   * sending of FrameReady() when the image becomes ready.
-   */
-  TextureSet NextFrame() override;
-
-  /**
-   * Get the interval of Nth frame.
+   * @copydoc Internal::ImageCache::GetFrameInterval()
    */
   uint32_t GetFrameInterval(uint32_t frameIndex) const override;
 
   /**
-   * Get the current rendered frame index.
-   * If there isn't any loaded frame, returns -1.
+   * @copydoc Internal::ImageCache::GetCurrentFrameIndex()
    */
   int32_t GetCurrentFrameIndex() const override;
 
   /**
-   * Get total frame count of the animated image file.
+   * @copydoc Internal::ImageCache::GetTotalFrameCount()
    */
   int32_t GetTotalFrameCount() const override;
 
-private:
   /**
-   * @return true if the front frame is ready
+   * @copydoc Internal::ImageCache::ClearCache()
    */
-  bool IsFrontReady() const;
+  void ClearCache() override;
 
+private:
   /**
-   * Load the next batch of images
+   * @brief Check whether the front frame is ready or not.
+   *
+   * @return true if the front frame is ready
    */
-  void LoadBatch();
+  bool IsFrontReady() const;
 
   /**
-   * Find the matching image frame, and set it to ready
+   * @brief Load the next batch of images
+   *
+   * @param[in] frameIndex starting frame index of batch to be loaded.
    */
-  void SetImageFrameReady(TextureManager::TextureId textureId);
+  void LoadBatch(uint32_t frameIndex);
 
   /**
-   * Get the texture set of the front frame.
-   * @return the texture set
+   * @brief Get the texture set of the front frame.
+   *
+   * @return the texture set of the front of Cache.
    */
   TextureSet GetFrontTextureSet() const;
 
   /**
-   * Get the texture id of the given index
+   * @brief Get the texture id of the given index
+   *
+   * @param[in] index index of the queue.
    */
   TextureManager::TextureId GetCachedTextureId(int index) const;
 
   /**
-   * Check if the front frame has become ready - if so, inform observer
+   * @brief Check if the front frame has become ready - if so, inform observer
+   *
    * @param[in] wasReady Readiness before call.
    */
   void CheckFrontFrame(bool wasReady);
 
 protected:
+  /**
+   * @copydoc Toolkit::TextureUploadObserver::LoadComplete()
+   */
   void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override;
 
 private:
@@ -137,8 +141,8 @@ private:
     bool         mReady    = false;
   };
 
-  std::vector<UrlStore>&    mImageUrls;
-  CircularQueue<ImageFrame> mQueue;
+  std::vector<UrlStore>&                 mImageUrls;
+  CircularQueue<ImageFrame>              mQueue;
 };
 
 } // namespace Internal
index 7b20e1f..e971be6 100644 (file)
@@ -821,12 +821,6 @@ void ImageVisual::OnSetTransform()
   }
 }
 
-bool ImageVisual::IsResourceReady() const
-{
-  return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
-          mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
-}
-
 void ImageVisual::UpdateShader()
 {
   if(mImpl->mRenderer)
index bf9b64b..0cbc3ba 100644 (file)
@@ -224,11 +224,6 @@ protected:
   void OnSetTransform() override;
 
   /**
-   * @copydoc Visual::Base::IsResourceReady
-   */
-  bool IsResourceReady() const override;
-
-  /**
    * @copydoc Visual::Base::UpdateShader
    */
   void UpdateShader() override;
index e8a6042..cbc038a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -38,7 +38,7 @@ NPatchData::NPatchData()
   mCroppedWidth(0),
   mCroppedHeight(0),
   mBorder(0, 0, 0, 0),
-  mLoadingState(LoadingState::LOADING),
+  mLoadingState(LoadingState::NOT_STARTED),
   mPreMultiplyOnLoad(false),
   mRenderingMap{nullptr}
 {
@@ -224,22 +224,49 @@ void NPatchData::SetLoadedNPatchData(Devel::PixelBuffer& pixelBuffer, bool preMu
   mLoadingState = LoadingState::LOAD_COMPLETE;
 }
 
+void NPatchData::NotifyObserver(TextureUploadObserver* observer, const bool& loadSuccess)
+{
+  observer->LoadComplete(
+    loadSuccess,
+    TextureUploadObserver::TextureInformation(
+      TextureUploadObserver::ReturnType::TEXTURE,
+      static_cast<TextureManager::TextureId>(mId), ///< Note : until end of NPatchLoader::Load, npatch-visual don't know the id of data.
+      mTextureSet,
+      false,     // UseAtlas
+      Vector4(), // AtlasRect
+      mPreMultiplyOnLoad));
+}
+
 void NPatchData::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
 {
   if(loadSuccess)
   {
-    SetLoadedNPatchData(textureInformation.pixelBuffer, textureInformation.preMultiplied);
+    if(mLoadingState != LoadingState::LOAD_COMPLETE)
+    {
+      // If mLoadingState is LOAD_FAILED, just re-set (It can be happened when sync loading is failed, but async loading is succeeded).
+      SetLoadedNPatchData(textureInformation.pixelBuffer, textureInformation.preMultiplied);
+    }
   }
   else
   {
-    mLoadingState = LoadingState::LOAD_FAILED;
+    if(mLoadingState == LoadingState::LOADING)
+    {
+      mLoadingState = LoadingState::LOAD_FAILED;
+    }
+    // If mLoadingState is already LOAD_COMPLETE, we can use uploaded texture (It can be happened when sync loading is succeeded, but async loading is failed).
+    else if(mLoadingState == LoadingState::LOAD_COMPLETE)
+    {
+      loadSuccess = true;
+    }
   }
 
   for(uint32_t index = 0; index < mObserverList.Count(); ++index)
   {
     TextureUploadObserver* observer = mObserverList[index];
-    observer->LoadComplete(loadSuccess, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, TextureManager::INVALID_TEXTURE_ID, mTextureSet, false, Vector4(), textureInformation.preMultiplied));
+    NotifyObserver(observer, loadSuccess);
   }
+
+  mObserverList.Clear();
 }
 
 } // namespace Internal
index f67e599..cc520d8 100644 (file)
@@ -44,9 +44,10 @@ public:
    */
   enum class LoadingState
   {
-    LOADING = 0,   ///< NPatch is on loading.
-    LOAD_COMPLETE, ///< NPatch loading is completed successfully.
-    LOAD_FAILED    ///< NPatch loading is failed.
+    NOT_STARTED = 0, ///< NPatch loading is not started yet.
+    LOADING,         ///< NPatch is on loading.
+    LOAD_COMPLETE,   ///< NPatch loading is completed successfully.
+    LOAD_FAILED      ///< NPatch loading is failed.
   };
 
 public:
@@ -251,6 +252,14 @@ public:
    */
   void SetLoadedNPatchData(Devel::PixelBuffer& pixelBuffer, bool preMultiplied);
 
+  /**
+   * @brief Send LoadComplete notify with current setuped NPatchData
+   *
+   * @param [in] observer observer who will be got LoadComplete notify
+   * @param [in] loadSuccess whether the image load success or not.
+   */
+  void NotifyObserver(TextureUploadObserver* observer, const bool& loadSuccess);
+
 private:
   /**
    * @copydoc TextureUploadObserver::LoadComplete
index cb2850d..4c6471e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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,94 +54,59 @@ NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
 
 std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
 {
-  std::size_t                                 hash  = CalculateHash(url.GetUrl());
-  OwnerContainer<NPatchData*>::SizeType       index = UNINITIALIZED_ID;
-  const OwnerContainer<NPatchData*>::SizeType count = mCache.Count();
+  NPatchData* data = GetNPatchData(url, border, preMultiplyOnLoad);
 
-  for(; index < count; ++index)
+  DALI_ASSERT_ALWAYS(data && "NPatchData creation failed!");
+
+  if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
-    if(mCache[index]->GetHash() == hash)
+    if(!synchronousLoading)
     {
-      // hash match, check url as well in case of hash collision
-      if(mCache[index]->GetUrl().GetUrl() == url.GetUrl())
+      // NotifyObserver already done, so
+      // data will not iterate observer list.
+      // We need to call LoadComplete directly.
+      data->NotifyObserver(textureObserver, true);
+    }
+  }
+  else // if NOT_STARTED or LOADING or LOAD_FAILED, try to reload.
+  {
+    if(!synchronousLoading)
+    {
+      data->AddObserver(textureObserver);
+      // If still LOADING and async, don't need to request reload. Fast return.
+      if(data->GetLoadingState() == NPatchData::LoadingState::LOADING)
       {
-        // Use cached data
-        if(mCache[index]->GetBorder() == border)
-        {
-          if(mCache[index]->GetLoadingState() == NPatchData::LoadingState::LOADING)
-          {
-            mCache[index]->AddObserver(textureObserver);
-          }
-          return mCache[index]->GetId(); // valid indices are from 1 onwards
-        }
-        else
-        {
-          if(mCache[index]->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
-          {
-            // Same url but border is different - use the existing texture
-            NPatchData* newData = new NPatchData();
-            newData->SetId(GenerateUniqueNPatchDataId());
-            newData->SetHash(hash);
-            newData->SetUrl(url);
-            newData->SetCroppedWidth(mCache[index]->GetCroppedWidth());
-            newData->SetCroppedHeight(mCache[index]->GetCroppedHeight());
-
-            newData->SetTextures(mCache[index]->GetTextures());
-
-            NPatchUtility::StretchRanges stretchRangesX;
-            stretchRangesX.PushBack(Uint16Pair(border.left, ((newData->GetCroppedWidth() >= static_cast<unsigned int>(border.right)) ? newData->GetCroppedHeight() - border.right : 0)));
-
-            NPatchUtility::StretchRanges stretchRangesY;
-            stretchRangesY.PushBack(Uint16Pair(border.top, ((newData->GetCroppedWidth() >= static_cast<unsigned int>(border.bottom)) ? newData->GetCroppedHeight() - border.bottom : 0)));
+        return data->GetId();
+      }
+    }
 
-            newData->SetStretchPixelsX(stretchRangesX);
-            newData->SetStretchPixelsY(stretchRangesY);
-            newData->SetBorder(border);
+    data->SetLoadingState(NPatchData::LoadingState::LOADING);
 
-            newData->SetPreMultiplyOnLoad(mCache[index]->IsPreMultiplied());
+    auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
+                                                  : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
-            newData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
-            newData->AddObserver(textureObserver);
+    Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading);
 
-            mCache.PushBack(newData);
-            return newData->GetId(); // valid ids start from 1u
-          }
-        }
-      }
+    if(pixelBuffer)
+    {
+      preMultiplyOnLoad = (preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) ? true : false;
+      data->SetLoadedNPatchData(pixelBuffer, preMultiplyOnLoad);
+    }
+    else if(synchronousLoading)
+    {
+      data->SetLoadingState(NPatchData::LoadingState::LOAD_FAILED);
     }
-  }
-
-  // If this is new image loading, make new cache data
-  NPatchData* data;
-  data = new NPatchData();
-  data->SetId(GenerateUniqueNPatchDataId());
-  data->SetHash(hash);
-  data->SetUrl(url);
-  data->SetBorder(border);
-  data->SetPreMultiplyOnLoad(preMultiplyOnLoad);
-  data->AddObserver(textureObserver);
-  mCache.PushBack(data);
-
-  auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
-                                                : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-
-  Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading);
-
-  if(pixelBuffer)
-  {
-    preMultiplyOnLoad = (preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) ? true : false;
-    data->SetLoadedNPatchData(pixelBuffer, preMultiplyOnLoad);
   }
   return data->GetId();
 }
 
 int32_t NPatchLoader::GetCacheIndexFromId(const NPatchData::NPatchDataId id)
 {
-  const unsigned int size = mCache.Count();
+  const unsigned int size = mCache.size();
 
   for(unsigned int i = 0; i < size; ++i)
   {
-    if(mCache[i]->GetId() == id)
+    if(mCache[i].mData->GetId() == id)
     {
       return i;
     }
@@ -155,7 +120,7 @@ bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, const NPatch
   int32_t cacheIndex = GetCacheIndexFromId(id);
   if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    data = mCache[cacheIndex];
+    data = mCache[cacheIndex].mData;
     return true;
   }
   data = nullptr;
@@ -170,17 +135,119 @@ void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver
     return;
   }
 
-  NPatchData* data;
-  data = mCache[cacheIndex];
+  NPatchInfo& info(mCache[cacheIndex]);
 
-  data->RemoveObserver(textureObserver);
+  info.mData->RemoveObserver(textureObserver);
 
-  if(data->GetObserverCount() == 0)
+  if(--info.mReferenceCount <= 0)
   {
-    mCache.Erase(mCache.Begin() + cacheIndex);
+    mCache.erase(mCache.begin() + cacheIndex);
   }
 }
 
+NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
+{
+  std::size_t                              hash  = CalculateHash(url.GetUrl());
+  std::vector<NPatchInfo>::size_type       index = UNINITIALIZED_ID;
+  const std::vector<NPatchInfo>::size_type count = mCache.size();
+
+  NPatchInfo* infoPtr = nullptr;
+
+  for(; index < count; ++index)
+  {
+    if(mCache[index].mData->GetHash() == hash)
+    {
+      // hash match, check url as well in case of hash collision
+      if(mCache[index].mData->GetUrl().GetUrl() == url.GetUrl())
+      {
+        // Use cached data. Need to fast-out return.
+        if(mCache[index].mData->GetBorder() == border)
+        {
+          mCache[index].mReferenceCount++;
+          return mCache[index].mData;
+        }
+        else
+        {
+          if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
+          {
+            // If we only found LOAD_FAILED case, replace current data. We can reuse texture
+            if(infoPtr == nullptr || infoPtr->mData->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE)
+            {
+              infoPtr = &mCache[index];
+            }
+          }
+          // Still loading pixel buffer. We cannot reuse cached texture yet. Skip checking
+          else if(mCache[index].mData->GetLoadingState() == NPatchData::LoadingState::LOADING)
+          {
+            continue;
+          }
+          // if LOAD_FAILED, reuse this cached NPatchData, and try to load again.
+          else
+          {
+            if(infoPtr == nullptr)
+            {
+              infoPtr = &mCache[index];
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // If this is new image loading, make new cache data
+  if(infoPtr == nullptr)
+  {
+    NPatchInfo info(new NPatchData());
+    info.mData->SetId(GenerateUniqueNPatchDataId());
+    info.mData->SetHash(hash);
+    info.mData->SetUrl(url);
+    info.mData->SetBorder(border);
+    info.mData->SetPreMultiplyOnLoad(preMultiplyOnLoad);
+
+    mCache.emplace_back(std::move(info));
+    infoPtr = &mCache.back();
+  }
+  // Else if LOAD_COMPLETE, Same url but border is different - use the existing texture
+  else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
+  {
+    NPatchInfo info(new NPatchData());
+
+    info.mData->SetId(GenerateUniqueNPatchDataId());
+    info.mData->SetHash(hash);
+    info.mData->SetUrl(url);
+    info.mData->SetCroppedWidth(infoPtr->mData->GetCroppedWidth());
+    info.mData->SetCroppedHeight(infoPtr->mData->GetCroppedHeight());
+
+    info.mData->SetTextures(infoPtr->mData->GetTextures());
+
+    NPatchUtility::StretchRanges stretchRangesX;
+    stretchRangesX.PushBack(Uint16Pair(border.left, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.right)) ? info.mData->GetCroppedHeight() - border.right : 0)));
+
+    NPatchUtility::StretchRanges stretchRangesY;
+    stretchRangesY.PushBack(Uint16Pair(border.top, ((info.mData->GetCroppedWidth() >= static_cast<unsigned int>(border.bottom)) ? info.mData->GetCroppedHeight() - border.bottom : 0)));
+
+    info.mData->SetStretchPixelsX(stretchRangesX);
+    info.mData->SetStretchPixelsY(stretchRangesY);
+    info.mData->SetBorder(border);
+
+    info.mData->SetPreMultiplyOnLoad(infoPtr->mData->IsPreMultiplied());
+
+    info.mData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
+
+    mCache.emplace_back(std::move(info));
+    infoPtr = &mCache.back();
+  }
+  // Else, LOAD_FAILED. just increase reference so we can reuse it.
+  else
+  {
+    infoPtr->mReferenceCount++;
+  }
+
+  DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
+
+  return infoPtr->mData;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 6475613..48301a6 100644 (file)
@@ -19,7 +19,6 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
-#include <dali/devel-api/common/owner-container.h>
 #include <dali/public-api/rendering/texture-set.h>
 #include <string>
 
@@ -90,8 +89,9 @@ public:
 
   /**
    * @brief Remove a texture matching id.
-   * Erase the observer from the observer list of cache.
-   * If the observer list is empty, the textureSet will be reset.
+   * Erase the observer from the observer list of cache if we need.
+   * This API decrease cached NPatchInfo reference.
+   * If the NPatchInfo reference become 0, the textureSet will be reset.
    *
    * @param [in] id cache data id
    * @param [in] textureObserver The NPatchVisual that requested loading.
@@ -103,6 +103,60 @@ private:
 
   int32_t GetCacheIndexFromId(const NPatchData::NPatchDataId id);
 
+private:
+  /**
+   * @brief Information of NPatchData
+   * It also hold ownership of NPatchData memory.
+   */
+  struct NPatchInfo
+  {
+    NPatchInfo(NPatchData* data)
+    : mData(data),
+      mReferenceCount(1u)
+    {
+    }
+    ~NPatchInfo()
+    {
+      if(mData)
+      {
+        delete mData;
+      }
+    }
+    NPatchInfo(NPatchInfo&& info) // move constructor
+    {
+      mData                = std::move(info.mData);
+      mReferenceCount      = info.mReferenceCount;
+      info.mData           = nullptr;
+      info.mReferenceCount = 0u;
+    }
+    NPatchInfo& operator=(NPatchInfo&& info) // move operator
+    {
+      mData                = std::move(info.mData);
+      mReferenceCount      = info.mReferenceCount;
+      info.mData           = nullptr;
+      info.mReferenceCount = 0u;
+      return *this;
+    }
+
+    NPatchInfo()                       = delete; // Do not use default constructor
+    NPatchInfo(const NPatchInfo& info) = delete; // Do not use copy constructor
+
+    NPatchData*  mData;
+    std::int16_t mReferenceCount; ///< The number of N-patch visuals that use this data.
+  };
+
+  /**
+   * @brief Get cached NPatchData by inputed url and border. If there is no cached data, create new one.
+   * @note This API increase cached NPatchInfo reference.
+   *
+   * @param [in] url to retrieve
+   * @param [in] border The border size of the image
+   * @param [in,out] preMultiplyOnLoad True if the image color should be multiplied by it's alpha. Set to false if the
+   *                                   image has no alpha channel
+   * @return NPatchData pointer that Load function will used.
+   */
+  NPatchData* GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad);
+
 protected:
   /**
    * Undefined copy constructor.
@@ -115,8 +169,8 @@ protected:
   NPatchLoader& operator=(const NPatchLoader& rhs);
 
 private:
-  NPatchData::NPatchDataId    mCurrentNPatchDataId;
-  OwnerContainer<NPatchData*> mCache;
+  NPatchData::NPatchDataId mCurrentNPatchDataId;
+  std::vector<NPatchInfo>  mCache;
 };
 
 } // namespace Internal
index 71d8cba..b5c9b51 100644 (file)
@@ -90,6 +90,12 @@ void NPatchVisual::LoadImages()
     // Load the auxiliary image
     auto preMultiplyOnLoading = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
     mAuxiliaryPixelBuffer     = textureManager.LoadPixelBuffer(mAuxiliaryUrl, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, this, true, preMultiplyOnLoading);
+
+    // If synchronousLoading is true, we can check the auxiliaryResource's status now.
+    if(synchronousLoading)
+    {
+      mAuxiliaryResourceStatus = mAuxiliaryPixelBuffer ? Toolkit::Visual::ResourceStatus::READY : Toolkit::Visual::ResourceStatus::FAILED;
+    }
   }
 }
 
@@ -192,29 +198,17 @@ void NPatchVisual::DoSetOnScene(Actor& actor)
   // load when first go on stage
   LoadImages();
 
+  // Set mPlacementActor now, because some case, LoadImages can use this information in LoadComplete API.
+  // at this case, we try to SetResouce to mPlaceActor twice. so, we should avoid that case.
+  mPlacementActor = actor;
+
   const NPatchData* data;
-  if(mLoader.GetNPatchData(mId, data))
+  if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
   {
-    Geometry geometry = CreateGeometry();
-    Shader   shader   = CreateShader();
-
-    mImpl->mRenderer.SetGeometry(geometry);
-    mImpl->mRenderer.SetShader(shader);
-
-    mPlacementActor = actor;
-    if(data->GetLoadingState() != NPatchData::LoadingState::LOADING)
+    // If mAuxiliaryUrl need to be loaded, we should wait it until LoadComplete called.
+    if(!mAuxiliaryUrl.IsValid() || mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::PREPARING)
     {
-      if(RenderingAddOn::Get().IsValid())
-      {
-        RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
-      }
-
-      ApplyTextureAndUniforms();
-      actor.AddRenderer(mImpl->mRenderer);
-      mPlacementActor.Reset();
-
-      // npatch loaded and ready to display
-      ResourceReady(Toolkit::Visual::ResourceStatus::READY);
+      SetResource();
     }
   }
 }
@@ -275,6 +269,7 @@ NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFa
   mImageUrl(),
   mAuxiliaryUrl(),
   mId(NPatchData::INVALID_NPATCH_DATA_ID),
+  mAuxiliaryResourceStatus(Toolkit::Visual::ResourceStatus::PREPARING),
   mBorderOnly(false),
   mBorder(),
   mAuxiliaryImageAlpha(0.0f),
@@ -451,47 +446,53 @@ void NPatchVisual::ApplyTextureAndUniforms()
   {
     textureSet = data->GetTextures();
     NPatchHelper::ApplyTextureAndUniforms(mImpl->mRenderer, data);
+
+    if(mAuxiliaryPixelBuffer)
+    {
+      // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
+      // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
+      // on GL interpolation alone.
+      if(mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
+         mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight())
+      {
+        mAuxiliaryPixelBuffer.Resize(data->GetCroppedWidth(), data->GetCroppedHeight());
+      }
+
+      // Note, this resets mAuxiliaryPixelBuffer handle
+      auto auxiliaryPixelData = Devel::PixelBuffer::Convert(mAuxiliaryPixelBuffer);
+
+      auto texture = Texture::New(TextureType::TEXTURE_2D,
+                                  auxiliaryPixelData.GetPixelFormat(),
+                                  auxiliaryPixelData.GetWidth(),
+                                  auxiliaryPixelData.GetHeight());
+      texture.Upload(auxiliaryPixelData);
+
+      // TODO : This code exist due to the texture cache manager hold TextureSet, not Texture.
+      // If we call textureSet.SetTexture(1, texture) directly, the cached TextureSet also be changed.
+      // We should make pass utc-Dali-VisualFactory.cpp UtcDaliNPatchVisualAuxiliaryImage02().
+      TextureSet tempTextureSet = TextureSet::New();
+      tempTextureSet.SetTexture(0, textureSet.GetTexture(0));
+      tempTextureSet.SetTexture(1, texture);
+      textureSet = tempTextureSet;
+
+      mImpl->mRenderer.RegisterProperty(DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA,
+                                        AUXILIARY_IMAGE_ALPHA_NAME,
+                                        mAuxiliaryImageAlpha);
+    }
+    mImpl->mRenderer.SetTextures(textureSet);
   }
   else
   {
     DALI_LOG_ERROR("The N patch image '%s' is not a valid N patch image\n", mImageUrl.GetUrl().c_str());
-    textureSet = TextureSet::New();
-
     Actor   actor     = mPlacementActor.GetHandle();
     Vector2 imageSize = Vector2::ZERO;
     if(actor)
     {
       imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
     }
-    mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+    mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize, false);
   }
 
-  if(mAuxiliaryPixelBuffer)
-  {
-    // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
-    // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
-    // on GL interpolation alone.
-    if(mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
-       mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight())
-    {
-      mAuxiliaryPixelBuffer.Resize(data->GetCroppedWidth(), data->GetCroppedHeight());
-    }
-
-    // Note, this resets mAuxiliaryPixelBuffer handle
-    auto auxiliaryPixelData = Devel::PixelBuffer::Convert(mAuxiliaryPixelBuffer);
-
-    auto texture = Texture::New(TextureType::TEXTURE_2D,
-                                auxiliaryPixelData.GetPixelFormat(),
-                                auxiliaryPixelData.GetWidth(),
-                                auxiliaryPixelData.GetHeight());
-    texture.Upload(auxiliaryPixelData);
-    textureSet.SetTexture(1, texture);
-    mImpl->mRenderer.RegisterProperty(DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA,
-                                      AUXILIARY_IMAGE_ALPHA_NAME,
-                                      mAuxiliaryImageAlpha);
-  }
-  mImpl->mRenderer.SetTextures(textureSet);
-
   // Register transform properties
   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
 }
@@ -525,14 +526,25 @@ void NPatchVisual::SetResource()
     mImpl->mRenderer.SetGeometry(geometry);
     mImpl->mRenderer.SetShader(shader);
 
+    if(RenderingAddOn::Get().IsValid())
+    {
+      RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
+    }
     Actor actor = mPlacementActor.GetHandle();
     if(actor)
     {
       ApplyTextureAndUniforms();
       actor.AddRenderer(mImpl->mRenderer);
       mPlacementActor.Reset();
+    }
 
-      // npatch loaded and ready to display
+    // npatch loaded and ready to display
+    if(data->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE)
+    {
+      ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
+    }
+    else
+    {
       ResourceReady(Toolkit::Visual::ResourceStatus::READY);
     }
   }
@@ -540,31 +552,42 @@ void NPatchVisual::SetResource()
 
 void NPatchVisual::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
 {
-  if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE)
+  if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE) // For the Url.
   {
-    EnablePreMultipliedAlpha(textureInformation.preMultiplied);
-    if(!loadSuccess)
+    if(textureInformation.textureId != TextureManager::INVALID_TEXTURE_ID)
     {
-      // Image loaded and ready to display
-      ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
+      if(mId == NPatchData::INVALID_NPATCH_DATA_ID)
+      {
+        // Special case when mLoader.Load call LoadComplete function before mId setup.
+        // We can overwrite mId.
+        mId = static_cast<NPatchData::NPatchDataId>(textureInformation.textureId);
+      }
     }
-
-    if(mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid())
+    if(loadSuccess)
     {
-      SetResource();
+      EnablePreMultipliedAlpha(textureInformation.preMultiplied);
     }
   }
-  else // for the ReturnType::PIXEL_BUFFER
+  else // For the AuxiliaryUrl : ReturnType::PIXEL_BUFFER
   {
     if(loadSuccess && textureInformation.url == mAuxiliaryUrl.GetUrl())
     {
-      mAuxiliaryPixelBuffer = textureInformation.pixelBuffer;
-      SetResource();
+      mAuxiliaryPixelBuffer    = textureInformation.pixelBuffer;
+      mAuxiliaryResourceStatus = Toolkit::Visual::ResourceStatus::READY;
     }
     else
     {
-      // Image loaded and ready to display
-      ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
+      mAuxiliaryResourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
+    }
+  }
+  // If auxiliaryUrl didn't required OR auxiliaryUrl load done.
+  if(!mAuxiliaryUrl.IsValid() || mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::PREPARING)
+  {
+    const NPatchData* data;
+    // and.. If Url loading done.
+    if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
+    {
+      SetResource();
     }
   }
 }
index ac66c45..b86dc41 100644 (file)
@@ -218,14 +218,15 @@ private:
   WeakHandle<Actor>                         mPlacementActor; ///< Weakhandle to contain Actor during texture loading
   NPatchLoader&                             mLoader;         ///< reference to N patch loader for fast access
   ImageVisualShaderFactory&                 mImageVisualShaderFactory;
-  VisualUrl                                 mImageUrl;             ///< The url to the N patch to load
-  VisualUrl                                 mAuxiliaryUrl;         ///< An auxiliary image that can be displayed on top of the N-Patch
-  NPatchData::NPatchDataId                  mId;                   ///< id of the N patch (from loader/cache)
-  Devel::PixelBuffer                        mAuxiliaryPixelBuffer; ///< pixel buffer of the auxiliary mask 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
-  Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;        ///< The release policy to determine when an image should no longer be cached.
+  VisualUrl                                 mImageUrl;                ///< The url to the N patch to load
+  VisualUrl                                 mAuxiliaryUrl;            ///< An auxiliary image that can be displayed on top of the N-Patch
+  NPatchData::NPatchDataId                  mId;                      ///< id of the N patch (from loader/cache)
+  Devel::PixelBuffer                        mAuxiliaryPixelBuffer;    ///< pixel buffer of the auxiliary mask image
+  Toolkit::Visual::ResourceStatus           mAuxiliaryResourceStatus; ///< resource status for auxiliary mask 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
+  Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;           ///< The release policy to determine when an image should no longer be cached.
 };
 
 } // namespace Internal
index 599d3de..80c6958 100644 (file)
@@ -359,12 +359,6 @@ void SvgVisual::OnSetTransform()
   }
 }
 
-bool SvgVisual::IsResourceReady() const
-{
-  return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
-          mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
-}
-
 void SvgVisual::UpdateShader()
 {
   if(mImpl->mRenderer)
index 66de43c..2f49e16 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_SVG_VISUAL_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -133,11 +133,6 @@ protected:
   void OnSetTransform() override;
 
   /**
-   * @copydoc Visual::Base::IsResourceReady
-   */
-  bool IsResourceReady() const override;
-
-  /**
    * @copydoc Visual::Base::UpdateShader
    */
   void UpdateShader() override;
index 8601cb3..953e23e 100644 (file)
@@ -765,7 +765,8 @@ void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
 
 bool Visual::Base::IsResourceReady() const
 {
-  return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY);
+  return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
+          mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
 }
 
 bool Visual::Base::IsSynchronousLoadingRequired() const
index 0f57210..336a62d 100644 (file)
@@ -239,8 +239,8 @@ public:
   void ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus);
 
   /**
-   * @brief Called when the visuals resources are loaded / ready
-   * @return true if ready, false otherwise
+   * @brief Called when the visuals resources are loaded / ready or failed (mean, broken image ready)
+   * @return true if ready or failed (mean, broken image ready), false otherwise
    */
   virtual bool IsResourceReady() const;
 
index a65c779..2f5d123 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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/devel-api/common/hash.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/utility/npatch-helper.h>
+#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/svg/svg-visual.h>
-#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
-#include <dali-toolkit/devel-api/utility/npatch-helper.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali/integration-api/debug.h>
 
 namespace Dali
@@ -36,6 +37,10 @@ namespace Toolkit
 {
 namespace Internal
 {
+namespace
+{
+const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+}
 
 VisualFactoryCache::VisualFactoryCache(bool preMultiplyOnLoad)
 : mSvgRasterizeThread(NULL),
@@ -221,10 +226,10 @@ Texture VisualFactoryCache::GetBrokenVisualImage(uint32_t brokenIndex)
     Devel::PixelBuffer pixelBuffer = LoadImageFromFile(mBrokenImageInfoContainer[brokenIndex].url);
     if(pixelBuffer)
     {
-      pixelData                = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
-      mBrokenImageInfoContainer[brokenIndex].texture  = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
+      pixelData                                      = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+      mBrokenImageInfoContainer[brokenIndex].texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
       mBrokenImageInfoContainer[brokenIndex].texture.Upload(pixelData);
-      mBrokenImageInfoContainer[brokenIndex].width = pixelData.GetWidth();
+      mBrokenImageInfoContainer[brokenIndex].width  = pixelData.GetWidth();
       mBrokenImageInfoContainer[brokenIndex].height = pixelData.GetHeight();
     }
   }
@@ -270,7 +275,7 @@ Geometry VisualFactoryCache::GetNPatchGeometry(int index)
       geometry = GetGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY);
       if(!geometry)
       {
-        geometry = NPatchHelper::CreateGridGeometry(Uint16Pair(3,3));
+        geometry = NPatchHelper::CreateGridGeometry(Uint16Pair(3, 3));
         SaveGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY, geometry);
       }
     }
@@ -286,7 +291,7 @@ Geometry VisualFactoryCache::GetNPatchGeometry(int index)
     geometry = GetGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY);
     if(!geometry)
     {
-      geometry = NPatchHelper::CreateGridGeometry(Uint16Pair(3,3));
+      geometry = NPatchHelper::CreateGridGeometry(Uint16Pair(3, 3));
       SaveGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY, geometry);
     }
   }
@@ -337,16 +342,15 @@ void VisualFactoryCache::ApplyTextureAndUniforms(Renderer& renderer, int index)
   TextureSet        textureSet;
   if(mNPatchLoader.GetNPatchData(mBrokenImageInfoContainer[index].npatchId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
-    textureSet = data->GetTextures();
+    textureSet                               = data->GetTextures();
     mBrokenImageInfoContainer[index].texture = textureSet.GetTexture(0);
     NPatchHelper::ApplyTextureAndUniforms(renderer, data);
     renderer.SetTextures(textureSet);
   }
 }
 
-void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size)
+void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size, const bool& rendererIsImage)
 {
-
   bool useDefaultBrokenImage = false;
   if(mBrokenImageInfoContainer.size() == 0)
   {
@@ -354,7 +358,7 @@ void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vec
   }
 
   // Load Information for broken image
-  for(uint32_t index = 0; (index  < mBrokenImageInfoContainer.size()) && !useDefaultBrokenImage; index++)
+  for(uint32_t index = 0; (index < mBrokenImageInfoContainer.size()) && !useDefaultBrokenImage; index++)
   {
     if(mBrokenImageInfoContainer[index].width == 0 && mBrokenImageInfoContainer[index].height == 0)
     {
@@ -365,16 +369,16 @@ void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vec
         if(mBrokenImageInfoContainer[index].visualType == VisualUrl::Type::N_PATCH)
         {
           const NPatchData* data;
-          Rect<int> border;
-          mBrokenImageInfoContainer[index].npatchId = mNPatchLoader.Load( mTextureManager, NULL, mBrokenImageInfoContainer[index].url, border, mPreMultiplyOnLoad, true);
+          Rect<int>         border;
+          mBrokenImageInfoContainer[index].npatchId = mNPatchLoader.Load(mTextureManager, NULL, mBrokenImageInfoContainer[index].url, border, mPreMultiplyOnLoad, true);
           if(mNPatchLoader.GetNPatchData(mBrokenImageInfoContainer[index].npatchId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
           {
-            mBrokenImageInfoContainer[index].width = data->GetCroppedWidth();
+            mBrokenImageInfoContainer[index].width  = data->GetCroppedWidth();
             mBrokenImageInfoContainer[index].height = data->GetCroppedHeight();
           }
           else
           {
-            DALI_LOG_ERROR("Can't update renderer for broken image. maybe image loading is failed [index:%d] [path:%s] \n",index, mBrokenImageInfoContainer[index].url.c_str());
+            DALI_LOG_ERROR("Can't update renderer for broken image. maybe image loading is failed [index:%d] [path:%s] \n", index, mBrokenImageInfoContainer[index].url.c_str());
             useDefaultBrokenImage = true;
           }
         }
@@ -382,7 +386,7 @@ void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vec
         {
           if(!GetBrokenVisualImage(index))
           {
-            DALI_LOG_ERROR("Can't update renderer for broken image. maybe image loading is failed [index:%d] [path:%s] \n",index, mBrokenImageInfoContainer[index].url.c_str());
+            DALI_LOG_ERROR("Can't update renderer for broken image. maybe image loading is failed [index:%d] [path:%s] \n", index, mBrokenImageInfoContainer[index].url.c_str());
             useDefaultBrokenImage = true;
           }
         }
@@ -401,7 +405,7 @@ void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vec
     mBrokenImageInfoContainer[defaultBrokenIndex].url = mDefaultBrokenImageUrl;
     VisualUrl visualUrl(mBrokenImageInfoContainer[defaultBrokenIndex].url);
     mBrokenImageInfoContainer[defaultBrokenIndex].visualType = visualUrl.GetType();
-    mUseDefaultBrokenImageOnly = true;
+    mUseDefaultBrokenImageOnly                               = true;
   }
 
   // Set Texutre to renderer
@@ -410,15 +414,31 @@ void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vec
   {
     // Set geometry and shader for npatch
     Geometry geometry = GetNPatchGeometry(brokenIndex);
-    Shader shader = GetNPatchShader(brokenIndex);
+    Shader   shader   = GetNPatchShader(brokenIndex);
     renderer.SetGeometry(geometry);
     renderer.SetShader(shader);
     ApplyTextureAndUniforms(renderer, brokenIndex);
   }
   else
   {
-    Texture brokenImage = GetBrokenVisualImage(brokenIndex);
-    TextureSet textureSet = TextureSet::New();
+    // Create single image renderer only if rederer is not use normal ImageShader. i.e. npatch visual.
+    if(!rendererIsImage)
+    {
+      Geometry geometry = GetGeometry(QUAD_GEOMETRY);
+      Shader   shader   = GetShader(IMAGE_SHADER);
+      if(!shader)
+      {
+        std::string vertexShader   = std::string(Dali::Shader::GetVertexShaderPrefix() + SHADER_IMAGE_VISUAL_SHADER_VERT.data());
+        std::string fragmentShader = std::string(Dali::Shader::GetFragmentShaderPrefix() + SHADER_IMAGE_VISUAL_SHADER_FRAG.data());
+        shader                     = Shader::New(vertexShader, fragmentShader);
+        shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
+        SaveShader(IMAGE_SHADER, shader);
+      }
+      renderer.SetGeometry(geometry);
+      renderer.SetShader(shader);
+    }
+    Texture    brokenImage = GetBrokenVisualImage(brokenIndex);
+    TextureSet textureSet  = TextureSet::New();
     textureSet.SetTexture(0u, brokenImage);
     renderer.SetTextures(textureSet);
   }
@@ -428,7 +448,7 @@ int32_t VisualFactoryCache::GetProperBrokenImageIndex(const Vector2& size)
 {
   // Sets the default broken type
   int32_t returnIndex = 0;
-  if((size.width == 0 || size.height == 0) || mUseDefaultBrokenImageOnly )
+  if((size.width == 0 || size.height == 0) || mUseDefaultBrokenImageOnly)
   {
     // To do : Need to add observer about size
     return returnIndex;
index 3ed626f..417f4d4 100644 (file)
@@ -201,8 +201,11 @@ public:
    * @brief Update the broken image Renderer object
    * @param[in,out] renderer renderer for broken image
    * @param[in] size the size of actor
+   * @param[in] rendererIsImage True if input renderer use image shader already.
+   *                            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);
+  void UpdateBrokenImageRenderer(Renderer& renderer, const Vector2& size, const bool& rendererIsImage = true);
 
 public:
   /**
index 958d031..91939c0 100644 (file)
 #include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/gl-view/drawable-view-impl.h>
 #include <dali-toolkit/internal/controls/gl-view/gl-view-impl.h>
 
-namespace Dali
+namespace Dali::Toolkit
 {
-namespace Toolkit
-{
-GlView::GlView()
-{
-}
+
+GlView::GlView() = default;
 
 GlView::GlView(const GlView& GlView) = default;
 
@@ -37,18 +35,38 @@ GlView& GlView::operator=(const GlView& GlView) = default;
 
 GlView& GlView::operator=(GlView&& rhs) = default;
 
-GlView::~GlView()
+GlView::~GlView() = default;
+
+GlView GlView::New(ColorFormat colorFormat)
 {
+  // This function is backward compatible and always returns
+  // backend based on NativeImage.
+  return Internal::GlView::New( colorFormat );
 }
 
-GlView GlView::New(ColorFormat colorFormat)
+GlView GlView::New(BackendMode backendMode, ColorFormat colorFormat)
 {
-  return Internal::GlView::New(colorFormat);
+  switch(backendMode)
+  {
+    case BackendMode::DIRECT_RENDERING:
+    {
+      return Internal::DrawableView::New();
+    }
+    case BackendMode::EGL_IMAGE_OFFSCREEN_RENDERING:
+    {
+      return Internal::GlView::New(colorFormat);
+    }
+    default:
+    {
+      DALI_ASSERT_ALWAYS("Invalid BackendMode");
+    }
+  }
+  return {};
 }
 
 GlView GlView::DownCast(BaseHandle handle)
 {
-  return Control::DownCast<GlView, Internal::GlView>(handle);
+  return Control::DownCast<GlView, Internal::GlViewImpl>(handle);
 }
 
 void GlView::RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback)
@@ -76,12 +94,17 @@ Dali::Toolkit::GlView::RenderingMode GlView::GetRenderingMode() const
   return Dali::Toolkit::GetImpl(*this).GetRenderingMode();
 }
 
+Dali::Toolkit::GlView::BackendMode GlView::GetBackendMode() const
+{
+  return Dali::Toolkit::GetImpl(*this).GetBackendMode();
+}
+
 void GlView::RenderOnce()
 {
   Dali::Toolkit::GetImpl(*this).RenderOnce();
 }
 
-GlView::GlView(Internal::GlView& implementation)
+GlView::GlView(Internal::GlViewImpl& implementation)
 : Control(implementation)
 {
 }
@@ -89,9 +112,7 @@ GlView::GlView(Internal::GlView& implementation)
 GlView::GlView(Dali::Internal::CustomActor* internal)
 : Control(internal)
 {
-  VerifyCustomActorPointer<Internal::GlView>(internal);
+  VerifyCustomActorPointer<Internal::GlViewImpl>(internal);
 }
 
-} // namespace Toolkit
-
 } // namespace Dali
index 57c3201..099b00f 100644 (file)
@@ -26,7 +26,7 @@ namespace Toolkit
 {
 namespace Internal DALI_INTERNAL
 {
-class GlView;
+class GlViewImpl;
 }
 
 /**
@@ -36,10 +36,42 @@ class GlView;
  * GlView creates a GL context, a GL surface and a render thread.
  * The render thread invokes user's callbacks.
  *
+ * @SINCE_2_0.45
  */
 class DALI_TOOLKIT_API GlView : public Dali::Toolkit::Control
 {
 public:
+
+  /**
+   * @brief Implementation backend mode
+   *
+   * @SINCE_2_1.18
+   */
+  enum class BackendMode
+  {
+    /**
+     * DIRECT_RENDERING mode executes GL code within DALi graphics
+     * pipeline. When Renderer is about to be drawn, the callback
+     * will be executed and the custom code "injected" into the pipeline.
+     * This allows rendering directly to the surface rather than offscreen.
+     */
+    DIRECT_RENDERING = 0,
+
+    /**
+     * EGL_IMAGE_OFFSCREEN_RENDERING mode executes GL code in own thread
+     * and renders to the offscreen NativeImage (EGL) buffer. This backend
+     * will render in parallel but has higher memory footprint and may suffer
+     * performance issues due to using EGL image.
+     */
+    EGL_IMAGE_OFFSCREEN_RENDERING,
+
+    /**
+     * The default mode is set to EGL_IMAGE_OFFSCREEN_RENDERING for backwards
+     * compatibility.
+     */
+    DEFAULT = EGL_IMAGE_OFFSCREEN_RENDERING
+  };
+
   /**
    * @brief Enumeration for rendering mode
    *
@@ -47,6 +79,8 @@ public:
    * It has two options.
    * One of them is continuous mode. It is rendered continuously.
    * The other is on demand mode. It is rendered by application.
+   *
+   * @SINCE_2_0.45
    */
   enum class RenderingMode
   {
@@ -58,6 +92,8 @@ public:
    * @brief Enumeration for Graphics API version
    *
    * This Enumeration is used to set a GLES version for EGL configuration.
+   *
+   * @SINCE_2_0.45
    */
   enum class GraphicsApiVersion
   {
@@ -69,6 +105,8 @@ public:
    * @brief Enumeration for color buffer format
    *
    * This Enumeration is used to set a color buffer format of GlView
+   *
+   * @SINCE_2_0.45
    */
   enum class ColorFormat
   {
@@ -78,13 +116,34 @@ public:
 
   /**
    * @brief Creates a GlView control.
+   *
+   * @note This function always creates the GlView with NativeImage backend.
+   *
    * @param[in] colorFormat the format of the color buffer.
    * @return A handle to a GlView control
+   *
+   * @SINCE_2_0.45
    */
   static GlView New(ColorFormat colorFormat);
 
   /**
+   * @brief Creates a GlView control.
+   *
+   * The new GlView will be created with specified backend.
+   * The colorFormat is ignored for DIRECT_RENDERING backend.
+   *
+   * @param[in] colorFormat the format of the color buffer.
+   * @param[in] backendMode the backend used by the GlView
+   * @return A handle to a GlView control
+   *
+   * @SINCE_2_1.18
+   */
+  static GlView New(BackendMode backendMode, ColorFormat colorFormat);
+
+  /**
    * @brief Creates an uninitialized GlView.
+   *
+   * @SINCE_2_0.45
    */
   GlView();
 
@@ -92,6 +151,8 @@ public:
    * @brief Destructor.
    *
    * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   *
+   * @SINCE_2_0.45
    */
   ~GlView();
 
@@ -99,6 +160,8 @@ public:
    * @brief Copy constructor.
    *
    * @param[in] GlView GlView to copy. The copied GlView will point at the same implementation
+   *
+   * @SINCE_2_0.45
    */
   GlView(const GlView& GlView);
 
@@ -106,6 +169,8 @@ public:
    * @brief Move constructor
    *
    * @param[in] rhs A reference to the moved handle
+   *
+   * @SINCE_2_0.45
    */
   GlView(GlView&& rhs);
 
@@ -114,6 +179,8 @@ public:
    *
    * @param[in] GlView The GlView to assign from
    * @return A reference to this
+   *
+   * @SINCE_2_0.45
    */
   GlView& operator=(const GlView& GlView);
 
@@ -122,6 +189,8 @@ public:
    *
    * @param[in] rhs A reference to the moved handle
    * @return A reference to this
+   *
+   * @SINCE_2_0.45
    */
   GlView& operator=(GlView&& rhs);
 
@@ -133,6 +202,8 @@ public:
    *
    * @param[in] handle Handle to an object
    * @return Handle to a GlView or an uninitialized handle
+   *
+   * @SINCE_2_0.45
    */
   static GlView DownCast(BaseHandle handle);
 
@@ -164,6 +235,8 @@ public:
    * @note Ownership of the callbacks is passed onto this class.
    * <b>You can't call Dali APIs in your callbacks because it is invoked in GlView's own render thread.</b>
    * And this must be called before adding GlView to the scene.
+   *
+   * @SINCE_2_0.45
    */
   void RegisterGlCallbacks(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback);
 
@@ -182,6 +255,8 @@ public:
    * @note Ownership of the callback is passed onto this class.
    * <b>You can't call Dali APIs in your callback because it is invoked in GlView's own render thread.</b>
    * And this must be called before adding GlView to the scene.
+   *
+   * @SINCE_2_0.45
    */
   void SetResizeCallback(CallbackBase* resizeCallback);
 
@@ -192,13 +267,24 @@ public:
    *
    * @note The default Rendering mode is CONTINUOUS.
    * If ON_DEMAND mode is set, it is rendered by RenderOnce()
+   *
+   * @SINCE_2_0.45
    */
   void SetRenderingMode(RenderingMode mode);
 
   /**
    * @brief Gets the rendering mode.
+   *
+   * @SINCE_2_0.45
+   */
+  [[nodiscard]] RenderingMode GetRenderingMode() const;
+
+  /**
+   * @brief Gets the backend mode.
+   *
+   * @SINCE_2_1.18
    */
-  RenderingMode GetRenderingMode() const;
+  [[nodiscard]] BackendMode GetBackendMode() const;
 
   /**
    * @brief Sets egl configuration for GlView
@@ -208,12 +294,16 @@ public:
    * @param[in] msaa the expected sampling number per pixel.
    * @param[in] version the graphics API version
    * @return True if the config exists, false otherwise.
+   *
+   * @SINCE_2_0.45
    */
   bool SetGraphicsConfig(bool depth, bool stencil, int msaa, GraphicsApiVersion version);
 
   /**
    * @brief Renders once more even if GL render functions are not added to idler.
    * @note Will not work if the window is hidden or GL render functions are added to idler
+   *
+   * @SINCE_2_0.45
    */
   void RenderOnce();
 
@@ -222,12 +312,16 @@ public: // Not intended for application developers
   /**
    * @brief Creates a handle using the Toolkit::Internal implementation.
    * @param[in] implementation The GlView implementation
+   *
+   * @SINCE_2_0.45
    */
-  DALI_INTERNAL GlView(Internal::GlView& implementation);
+  DALI_INTERNAL GlView(Internal::GlViewImpl& implementation);
 
   /**
    * @brief Allows the creation of this GlView from an Internal::CustomActor pointer.
    * @param[in] internal A pointer to the internal CustomActor
+   *
+   * @SINCE_2_0.45
    */
   DALI_INTERNAL GlView(Dali::Internal::CustomActor* internal);
   /// @endcond
index 9323093..9630b37 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 1;
-const unsigned int TOOLKIT_MICRO_VERSION = 15;
+const unsigned int TOOLKIT_MICRO_VERSION = 18;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index f6e25ab..cb7df7b 100644 (file)
Binary files a/dali-toolkit/styles/images-common/button-down.9.png and b/dali-toolkit/styles/images-common/button-down.9.png differ
index a2e2e01..252880d 100644 (file)
Binary files a/dali-toolkit/styles/images-common/button-up.9.png and b/dali-toolkit/styles/images-common/button-up.9.png differ
index a6517b8..c5996ee 100644 (file)
Binary files a/dali-toolkit/styles/images-common/checkbox-selected-disabled.png and b/dali-toolkit/styles/images-common/checkbox-selected-disabled.png differ
index e3a8c43..91be569 100644 (file)
Binary files a/dali-toolkit/styles/images-common/checkbox-selected.png and b/dali-toolkit/styles/images-common/checkbox-selected.png differ
index 022c1cf..7aba7cf 100644 (file)
Binary files a/dali-toolkit/styles/images-common/checkbox-unselected-disabled.png and b/dali-toolkit/styles/images-common/checkbox-unselected-disabled.png differ
index 58e9390..b8a7d0d 100644 (file)
Binary files a/dali-toolkit/styles/images-common/checkbox-unselected.png and b/dali-toolkit/styles/images-common/checkbox-unselected.png differ
index 5c40e4b..4e30b7f 100644 (file)
Binary files a/dali-toolkit/styles/images-common/radio-button-selected-disabled.png and b/dali-toolkit/styles/images-common/radio-button-selected-disabled.png differ
index d49cbb5..7c3afab 100644 (file)
Binary files a/dali-toolkit/styles/images-common/radio-button-selected.png and b/dali-toolkit/styles/images-common/radio-button-selected.png differ
index 0c5fa5f..dc659f8 100644 (file)
Binary files a/dali-toolkit/styles/images-common/radio-button-unselected-disabled.png and b/dali-toolkit/styles/images-common/radio-button-unselected-disabled.png differ
index 6f647e8..65d93c6 100644 (file)
Binary files a/dali-toolkit/styles/images-common/radio-button-unselected.png and b/dali-toolkit/styles/images-common/radio-button-unselected.png differ
index ea8a871..c115b60 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.1.15
+Version:    2.1.18
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT