Merge "Set FullRelayoutNeeded after GetNaturalSize/GetHeightForWidth to avoid the...
authorBowon Ryu <bowon.ryu@samsung.com>
Wed, 15 Dec 2021 09:04:49 +0000 (09:04 +0000)
committerGerrit Code Review <gerrit@review>
Wed, 15 Dec 2021 09:04:49 +0000 (09:04 +0000)
40 files changed:
automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/test-text-geometry-utils.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/test-text-geometry-utils.h [new file with mode: 0644]
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-Visual.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/devel-api/controls/text-controls/text-field-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-field-devel.h
dali-toolkit/devel-api/controls/text-controls/text-label-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-label-devel.h
dali-toolkit/internal/controls/buttons/button-impl.cpp
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-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.h
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.h
dali-toolkit/internal/file.list
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/text/text-geometry.cpp [new file with mode: 0644]
dali-toolkit/internal/text/text-geometry.h [new file with mode: 0644]
dali-toolkit/internal/text/visual-model-impl.cpp
dali-toolkit/internal/text/visual-model-impl.h
dali-toolkit/internal/visuals/animated-image/animated-image-visual.h
dali-toolkit/internal/visuals/color/color-visual.cpp
dali-toolkit/internal/visuals/image-atlas-manager.cpp
dali-toolkit/internal/visuals/image-atlas-manager.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h
dali-toolkit/internal/visuals/visual-base-impl.cpp
dali-toolkit/internal/visuals/visual-base-impl.h
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

index 04c51af..1553f97 100644 (file)
@@ -71,7 +71,8 @@ public:
   TestObserver()
   : mCompleteType( CompleteType::NOT_COMPLETED ),
     mLoaded(false),
-    mObserverCalled(false)
+    mObserverCalled(false),
+    mTextureSet()
   {
   }
 
@@ -81,6 +82,7 @@ public:
     mCompleteType = CompleteType::UPLOAD_COMPLETE;
     mLoaded = loadSuccess;
     mObserverCalled = true;
+    mTextureSet = textureSet;
   }
 
   virtual void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override
@@ -93,6 +95,7 @@ public:
   CompleteType mCompleteType;
   bool mLoaded;
   bool mObserverCalled;
+  TextureSet mTextureSet;
 };
 
 
@@ -590,3 +593,305 @@ int UtcTextureManagerUseInvalidMask(void)
 
   END_TEST;
 }
+
+int UtcTextureManagerSynchronousLoadingFail(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerSynchronousLoadingFail" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string maskname("");
+  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;
+
+  std::string filename("dummy");
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  bool loadingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image synchronously.
+  TestObserver observer;
+  TextureSet textureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(!textureSet);  // texture loading fail.
+  DALI_TEST_CHECK(textureId == TextureManager::INVALID_TEXTURE_ID); // invalid texture id is returned.
+
+  END_TEST;
+}
+
+int UtcTextureManagerCachingSynchronousLoading(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerCachingSynchronousLoading" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string filename( TEST_IMAGE_FILE_NAME );
+
+  std::string maskname("");
+  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;
+
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  bool loadingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image synchronously.
+  TestObserver observer;
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  TextureSet textureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(textureSet);  // texture is loaded.
+
+  // observer isn't called in synchronous loading.
+  DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION);
+
+
+  // load same image asynchronously.
+  TestObserver asyncObserver;
+  auto asyncTextureId( TextureManager::INVALID_TEXTURE_ID );
+  loadingStatus = false;
+  TextureSet asyncTextureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncTextureId, textureId, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(asyncTextureSet);  // Cached texture.
+
+  // observer is directly called because textureSet is retrieved by cache.
+  DALI_TEST_EQUALS(asyncObserver.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver.mObserverCalled, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcTextureManagerAsyncSyncAsync(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerAsyncSyncAsync" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string filename( TEST_IMAGE_FILE_NAME );
+
+  std::string maskname("");
+  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;
+
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image asynchronously.
+  TestObserver asyncObserver1;
+  auto asyncTextureId1( TextureManager::INVALID_TEXTURE_ID );
+  bool asyncLoadingStatus1 = false;
+  TextureSet asyncTextureSet1 = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId1,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    asyncLoadingStatus1,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver1,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncLoadingStatus1, true, TEST_LOCATION); // texture is loading now.
+  DALI_TEST_CHECK(!asyncTextureSet1);  // texture is not loaded yet.
+
+  // observer is still not called.
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION);
+
+
+  // load same image synchronously just after asynchronous loading.
+  TestObserver syncObserver;
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  bool syncLoadingStatus = false;
+  TextureSet syncTextureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    syncLoadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &syncObserver,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncTextureId1, textureId, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_EQUALS(syncLoadingStatus, false, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_CHECK(syncTextureSet);  // texture is loaded.
+
+  // syncObserver isn't called in synchronous loading.
+  DALI_TEST_EQUALS(syncObserver.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(syncObserver.mObserverCalled, false, TEST_LOCATION);
+
+  // asyncObserver1 is still not called too.
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION);
+
+
+
+  // load image asynchronously.
+  TestObserver asyncObserver2;
+  auto asyncTextureId2( TextureManager::INVALID_TEXTURE_ID );
+  bool asyncLoadingStatus2 = false;
+  TextureSet asyncTextureSet2 = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId2,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    asyncLoadingStatus2,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver2,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncLoadingStatus2, false, TEST_LOCATION); // texture is loaded by previous sync request
+  DALI_TEST_CHECK(asyncTextureSet2); // texture is loaded
+  DALI_TEST_CHECK(asyncTextureSet2 == syncTextureSet);  // check loaded two texture is same.
+
+  // observer is called synchronously because the texture is cached.
+  DALI_TEST_EQUALS(asyncObserver2.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, true, TEST_LOCATION);
+
+  asyncObserver2.mLoaded = false;
+  asyncObserver2.mObserverCalled = false;
+
+  application.SendNotification();
+  application.Render();
+
+  // Requested asynchronous loading at first is finished now and async observer is called now.
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_CHECK(asyncObserver1.mTextureSet == asyncTextureSet2);  // check loaded two texture is same.
+
+  // asyncObserver2 was already called so it isn't called here.
+  DALI_TEST_EQUALS(asyncObserver2.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
index b1aece3..641a9df 100755 (executable)
@@ -135,6 +135,7 @@ SET(TEST_HARNESS_SOURCES
   dali-toolkit-test-utils/test-render-controller.cpp
   dali-toolkit-test-utils/test-trace-call-stack.cpp
   dali-toolkit-test-utils/test-native-image.cpp
+  test-text-geometry-utils.cpp
 )
 
 PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
diff --git a/automated-tests/src/dali-toolkit/test-text-geometry-utils.cpp b/automated-tests/src/dali-toolkit/test-text-geometry-utils.cpp
new file mode 100644 (file)
index 0000000..b2f0411
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test-text-geometry-utils.h"
+
+namespace TestTextGeometryUtils
+{
+
+void CheckGeometryResult(Vector<Vector2> positionsList, Vector<Vector2> sizeList, Vector<Vector2> expectedPositions, Vector<Vector2> expectedSizes)
+{
+  unsigned int expectedCount = expectedSizes.Size();
+
+  for(unsigned int i = 0; i < expectedCount; i++)
+  {
+    DALI_TEST_EQUALS((int)positionsList[i].x, (int)expectedPositions[i].x, TEST_LOCATION);
+    DALI_TEST_EQUALS((int)positionsList[i].y, (int)expectedPositions[i].y, TEST_LOCATION);
+
+    DALI_TEST_EQUALS((int)sizeList[i].x, (int)expectedSizes[i].x, TEST_LOCATION);
+    DALI_TEST_EQUALS((int)sizeList[i].y, (int)expectedSizes[i].y, TEST_LOCATION);
+  }
+}
+
+}
\ No newline at end of file
diff --git a/automated-tests/src/dali-toolkit/test-text-geometry-utils.h b/automated-tests/src/dali-toolkit/test-text-geometry-utils.h
new file mode 100644 (file)
index 0000000..4aa2a33
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef  TOOLKIT_TEXT_GEOMETRY_UTILS_H
+#define  TOOLKIT_TEXT_GEOMETRY_UTILS_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.
+ *
+ */
+
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+
+
+
+namespace TestTextGeometryUtils
+{
+void CheckGeometryResult(Vector<Vector2> positionsList, Vector<Vector2> sizeList, Vector<Vector2> expectedPositions, Vector<Vector2> expectedSizes);
+}
+
+#endif // TOOLKIT_TEXT_GEOMETRY_UTILS_H
index c32c391..0bac46a 100644 (file)
@@ -29,6 +29,7 @@
 #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 "test-text-geometry-utils.h"
 
 using namespace Dali;
 using namespace Toolkit;
@@ -4455,6 +4456,258 @@ int utcDaliTextEditorCursorPositionChangedSignal(void)
   END_TEST;
 }
 
+int utcDaliTextEditorGeometryEllipsisStart(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryEllipsisStart");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.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( DevelTextEditor::Property::ENABLE_SCROLL_BAR, false );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS, true );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 2;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(37, 0));
+  expectedSizes.PushBack(Vector2(20, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryEllipsisMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryEllipsisMiddle");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.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( DevelTextEditor::Property::ENABLE_SCROLL_BAR, false );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS, true );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::MIDDLE );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 2;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(-1, 0));
+  expectedSizes.PushBack(Vector2(25, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryEllipsisEnd(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryEllipsisEnd");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.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( DevelTextEditor::Property::ENABLE_SCROLL_BAR, false );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS, true );
+  editor.SetProperty( DevelTextEditor::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END );
+  editor.SetProperty( TextEditor::Property::TEXT, "line1 \nline2\nline 3\nline4" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 2;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(-1, 0));
+  expectedSizes.PushBack(Vector2(59, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(25, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryRTL(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryRTL");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 50.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::TEXT, "line1 \nline2\nline 3\nالاخيرالسطر" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 4;
+  unsigned int startIndex = 3;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(24, 0));
+  expectedSizes.PushBack(Vector2(33, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 50));
+  expectedSizes.PushBack(Vector2(59, 25));
+
+  expectedPositions.PushBack(Vector2(61, 75));
+  expectedSizes.PushBack(Vector2(37, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGeometryGlyphMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextEditorGeometryGlyphMiddle");
+
+  TextEditor editor = TextEditor::New();
+  DALI_TEST_CHECK( editor );
+
+  application.GetScene().Add( editor );
+
+  editor.SetProperty( TextEditor::Property::POINT_SIZE, 7.f );
+  editor.SetProperty( Actor::Property::SIZE, Vector2( 150.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::TEXT, "لا ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ù„ا" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 13;
+
+  Vector<Vector2> positionsList = DevelTextEditor::GetTextPosition(editor, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextEditor::GetTextSize(editor, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(6, 0));
+  expectedSizes.PushBack(Vector2(124, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
 int utcDaliTextEditorSelectionClearedSignal(void)
 {
   ToolkitTestApplication application;
index e8bf4b6..d1a04e9 100644 (file)
@@ -31,6 +31,7 @@
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include "toolkit-clipboard.h"
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include "test-text-geometry-utils.h"
 
 using namespace Dali;
 using namespace Toolkit;
@@ -4391,6 +4392,190 @@ int utcDaliTextFieldCursorPositionChangedSignal(void)
   END_TEST;
 }
 
+int utcDaliTextFieldGeometryEllipsisStart(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryEllipsisStart");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 250.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::START );
+  field.SetProperty( TextField::Property::TEXT, "Hello World" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 10;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(14, 0));
+  expectedSizes.PushBack(Vector2(106, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextFieldGeometryEllipsisEnd(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryEllipsisEnd");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 250.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS, true );
+  field.SetProperty( DevelTextField::Property::ELLIPSIS_POSITION, DevelText::EllipsisPosition::END );
+  field.SetProperty( TextField::Property::TEXT, "Hello World" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 0;
+  unsigned int endIndex = 10;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(-2, 0));
+  expectedSizes.PushBack(Vector2(122, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextFieldGeometryRTL(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryRTL");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 300.f, 50.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( TextField::Property::TEXT, "السطر Ø§Ù„اخير" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 7;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(38, 0));
+  expectedSizes.PushBack(Vector2(73, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextFieldGeometryGlyphMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextFieldGeometryGlyphMiddle");
+
+  TextField field = TextField::New();
+  DALI_TEST_CHECK( field );
+
+  application.GetScene().Add( field );
+
+  field.SetProperty( TextField::Property::POINT_SIZE, 7.f );
+  field.SetProperty( Actor::Property::SIZE, Vector2( 150.f, 200.f ) );
+  field.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  field.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  field.SetProperty( TextField::Property::ENABLE_MARKUP, true );
+  field.SetProperty( TextField::Property::TEXT, "لا ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ù„ا" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 13;
+
+  Vector<Vector2> positionsList = DevelTextField::GetTextPosition(field, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextField::GetTextSize(field, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(6, 0));
+  expectedSizes.PushBack(Vector2(124, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
 int utcDaliTextFieldSelectionClearedSignal(void)
 {
   ToolkitTestApplication application;
index b0f9764..c060743 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-toolkit/devel-api/text/bitmap-font.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
 #include <dali-toolkit/devel-api/text/text-utils-devel.h>
+#include "test-text-geometry-utils.h"
 
 using namespace Dali;
 using namespace Toolkit;
@@ -2017,6 +2018,106 @@ int utcDaliTextLabelGetHeightForWidthChangeLineCountWhenTextChanged(void)
   END_TEST;
 }
 
+int utcDaliTextLabelGeometryRTL(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextLabelGeometryRTL");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK( label );
+
+  application.GetScene().Add( label );
+
+  label.SetProperty( TextLabel::Property::POINT_SIZE, 7.f );
+  label.SetProperty( Actor::Property::SIZE, Vector2( 150.f, 100.f ) );
+  label.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  label.SetProperty( TextLabel::Property::ENABLE_MARKUP, true );
+  label.SetProperty( TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty( TextLabel::Property::TEXT, "line1 \nline2\nline 3\nالاخيرالسطر" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 4;
+  unsigned int startIndex = 3;
+  unsigned int endIndex = 24;
+
+  Vector<Vector2> positionsList = DevelTextLabel::GetTextPosition(label, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextLabel::GetTextSize(label, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(24, 0));
+  expectedSizes.PushBack(Vector2(33, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 25));
+  expectedSizes.PushBack(Vector2(52, 25));
+
+  expectedPositions.PushBack(Vector2(-1, 50));
+  expectedSizes.PushBack(Vector2(59, 25));
+
+  expectedPositions.PushBack(Vector2(73, 75));
+  expectedSizes.PushBack(Vector2(37, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
+int utcDaliTextLabelGeometryGlyphMiddle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" utcDaliTextLabelGeometryGlyphMiddle");
+
+  TextLabel label = TextLabel::New();
+  DALI_TEST_CHECK( label );
+
+  application.GetScene().Add( label );
+
+  label.SetProperty( TextLabel::Property::POINT_SIZE, 7.f );
+  label.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+  label.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+  label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+  label.SetProperty( TextLabel::Property::ENABLE_MARKUP, true );
+  label.SetProperty( TextLabel::Property::TEXT, "لا ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ù„ا" );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  unsigned int expectedCount = 1;
+  unsigned int startIndex = 1;
+  unsigned int endIndex = 13;
+
+  Vector<Vector2> positionsList = DevelTextLabel::GetTextPosition(label, startIndex, endIndex);
+  Vector<Vector2> sizeList = DevelTextLabel::GetTextSize(label, startIndex, endIndex);
+
+  DALI_TEST_EQUALS(positionsList.Size(), expectedCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(sizeList.Size(), expectedCount, TEST_LOCATION);
+
+  Vector<Vector2> expectedSizes;
+  Vector<Vector2> expectedPositions;
+
+  expectedPositions.PushBack(Vector2(12, 0));
+  expectedSizes.PushBack(Vector2(118, 25));
+
+  TestTextGeometryUtils::CheckGeometryResult(positionsList, sizeList, expectedPositions, expectedSizes);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextlabelEllipsisPositionProperty(void)
 {
   ToolkitTestApplication application;
index d1a2e56..387275d 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-toolkit/devel-api/visual-factory/transition-data.h>
 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/text-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/animated-gradient-visual-properties-devel.h>
@@ -5446,4 +5447,667 @@ int UtcDaliVisualGetVisualProperty07(void)
 #endif
 
   END_TEST;
+}
+
+int UtcDaliVisualUpdateProperty(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdateProperty: Test update property by DoAction. Standard case" );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap[Visual::Property::TYPE] = Visual::Type::IMAGE;
+  propertyMap[ImageVisual::Property::URL] = TEST_IMAGE_FILE_NAME;
+  propertyMap[Visual::Property::MIX_COLOR] = Color::BLUE;
+  propertyMap[DevelVisual::Property::VISUAL_FITTING_MODE] = DevelVisual::FIT_WIDTH;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  Property::Map originalMap;
+  imageVisual.CreatePropertyMap( originalMap );
+
+  float                    targetOpacity            = 0.5f;
+  Vector3                  targetMixColor           = Vector3(1.0f, 0.4f, 0.2f);
+  bool                     targetPreMultipliedAlpha = originalMap[Visual::Property::PREMULTIPLIED_ALPHA].Get<bool>() ^ true;
+  DevelVisual::FittingMode targetVisualFittingMode  = DevelVisual::CENTER;
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[Visual::Property::OPACITY]                  = targetOpacity;
+  targetPropertyMap[ImageVisual::Property::URL]                 = "foobar";
+  targetPropertyMap[Visual::Property::MIX_COLOR]                = targetMixColor;
+  targetPropertyMap[Visual::Property::PREMULTIPLIED_ALPHA]      = targetPreMultipliedAlpha;
+  targetPropertyMap[DevelVisual::Property::VISUAL_FITTING_MODE] = targetVisualFittingMode;
+
+  // Update Properties
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* colorValue = resultMap.Find(Visual::Property::MIX_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(colorValue);
+  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetMixColor.r, targetMixColor.g, targetMixColor.b, targetOpacity), TEST_LOCATION);
+
+  Property::Value* urlValue = resultMap.Find(ImageVisual::Property::URL, Property::STRING);
+  DALI_TEST_CHECK(urlValue);
+  // NOTE : ImageVisual URL must NOT changed.
+  DALI_TEST_EQUALS(urlValue->Get< std::string >(), TEST_IMAGE_FILE_NAME, TEST_LOCATION);
+
+  Property::Value* preMultipliedValue = resultMap.Find(Visual::Property::PREMULTIPLIED_ALPHA, Property::BOOLEAN);
+  DALI_TEST_CHECK(preMultipliedValue);
+  DALI_TEST_EQUALS(preMultipliedValue->Get< bool >(), targetPreMultipliedAlpha, TEST_LOCATION);
+
+  Property::Value* visualFittingModeValue = resultMap.Find(DevelVisual::Property::VISUAL_FITTING_MODE, Property::STRING);
+  DALI_TEST_CHECK(visualFittingModeValue);
+  DALI_TEST_EQUALS(visualFittingModeValue->Get< std::string >(), "CENTER", TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualUpdatePropertyChangeShader01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdatePropertyChangeShader01: Test update property by DoAction. Change the shader case" );
+
+  TraceCallStack& callStack = application.GetGraphicsController().mCallStack;
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  // Case ImageVisual
+  propertyMap[Visual::Property::TYPE] = Visual::Type::IMAGE;
+  propertyMap[ImageVisual::Property::URL] = TEST_IMAGE_FILE_NAME;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Vector4 targetCornerRadius = Vector4(1.0f, 12.0f, 2.0f, 21.0f);
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS] = targetCornerRadius;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS_POLICY] = Toolkit::Visual::Transform::Policy::RELATIVE;
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
+
+  Property::Value* cornerRadiusPolicyValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cornerRadiusPolicyValue);
+  DALI_TEST_EQUALS(cornerRadiusPolicyValue->Get<int>(), static_cast<int>(Toolkit::Visual::Transform::Policy::RELATIVE), TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+  // Shader changed
+  DALI_TEST_CHECK( callStack.FindMethod("CreateShader") );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  float   targetBorderlineWidth  = 10.0f;
+  Vector4 targetBorderlineColor  = Vector4(1.0f, 0.2f, 0.1f, 0.5f);
+  float   targetBorderlineOffset = -0.3f;
+
+  Property::Map targetPropertyMap2;
+  targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS]        = Vector4::ZERO;
+  targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS_POLICY] = Toolkit::Visual::Transform::Policy::ABSOLUTE;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH]     = targetBorderlineWidth;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_COLOR]     = targetBorderlineColor;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_OFFSET]    = targetBorderlineOffset;
+
+  // Update Properties with Borderline
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
+
+  Property::Map resultMap2;
+  imageVisual.CreatePropertyMap( resultMap2 );
+
+  // Test property values: they should be updated
+  cornerRadiusValue = resultMap2.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  cornerRadiusPolicyValue = resultMap2.Find(DevelVisual::Property::CORNER_RADIUS_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cornerRadiusPolicyValue);
+  DALI_TEST_EQUALS(cornerRadiusPolicyValue->Get<int>(), static_cast<int>(Toolkit::Visual::Transform::Policy::ABSOLUTE), TEST_LOCATION);
+
+  Property::Value* borderlineWidthValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_WIDTH, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineWidthValue);
+  DALI_TEST_EQUALS(borderlineWidthValue->Get<float>(), targetBorderlineWidth, TEST_LOCATION);
+
+  Property::Value* borderlineColorValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(borderlineColorValue);
+  DALI_TEST_EQUALS(borderlineColorValue->Get<Vector4>(), targetBorderlineColor, TEST_LOCATION);
+
+  Property::Value* borderlineOffsetValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_OFFSET, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineOffsetValue);
+  DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), targetBorderlineOffset, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    // Note : mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    // Note : mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+  // Shader changed
+  DALI_TEST_CHECK( callStack.FindMethod("CreateShader") );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Property::Map targetPropertyMap3;
+  targetPropertyMap3[DevelVisual::Property::CORNER_RADIUS]        = Vector4::ZERO;
+  targetPropertyMap3[DevelVisual::Property::CORNER_RADIUS_POLICY] = Toolkit::Visual::Transform::Policy::ABSOLUTE;
+  targetPropertyMap3[DevelVisual::Property::BORDERLINE_WIDTH]     = 0.0f;
+  targetPropertyMap3[DevelVisual::Property::BORDERLINE_COLOR]     = Vector4::ZERO;
+  targetPropertyMap3[DevelVisual::Property::BORDERLINE_OFFSET]    = 0.0f;
+
+  // Update Properties into zero
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap3);
+
+  Property::Map resultMap3;
+  imageVisual.CreatePropertyMap( resultMap3 );
+
+  // Test property values: they should be updated
+  cornerRadiusValue = resultMap3.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  cornerRadiusPolicyValue = resultMap3.Find(DevelVisual::Property::CORNER_RADIUS_POLICY, Property::INTEGER);
+  DALI_TEST_CHECK(cornerRadiusPolicyValue);
+  DALI_TEST_EQUALS(cornerRadiusPolicyValue->Get<int>(), static_cast<int>(Toolkit::Visual::Transform::Policy::ABSOLUTE), TEST_LOCATION);
+
+  borderlineWidthValue = resultMap3.Find(DevelVisual::Property::BORDERLINE_WIDTH, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineWidthValue);
+  DALI_TEST_EQUALS(borderlineWidthValue->Get<float>(), 0.0f, TEST_LOCATION);
+
+  borderlineColorValue = resultMap3.Find(DevelVisual::Property::BORDERLINE_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(borderlineColorValue);
+  DALI_TEST_EQUALS(borderlineColorValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  borderlineOffsetValue = resultMap3.Find(DevelVisual::Property::BORDERLINE_OFFSET, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineOffsetValue);
+  DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), 0.0f, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    // Note : mAlwaysUsingBorderline and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    // Note : mAlwaysUsingBorderline and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") != std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+  // Shader not changed
+  DALI_TEST_CHECK( !callStack.FindMethod("CreateShader") );
+
+  END_TEST;
+}
+
+int UtcDaliVisualUpdatePropertyChangeShader02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdatePropertyChangeShader02: Test update property by DoAction. Fake update" );
+
+  TraceCallStack& callStack = application.GetGraphicsController().mCallStack;
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  // Case ImageVisual
+  propertyMap[Visual::Property::TYPE] = Visual::Type::IMAGE;
+  propertyMap[ImageVisual::Property::URL] = TEST_IMAGE_FILE_NAME;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  Vector4 targetCornerRadius = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS] = targetCornerRadius;
+
+  callStack.Reset();
+  callStack.Enable(true);
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    // Note : corner radius is zero. so we don't change shader!
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    // Note : corner radius is zero. so we don't change shader!
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader doesn't changed
+  DALI_TEST_CHECK( !(callStack.FindMethod("CreateShader")) );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  float   targetBorderlineWidth = 0.0f;
+  Vector4 targetBorderlineColor = Vector4(1.0f, 1.0f, 0.0f, 0.0f);
+  float   targetBorderlineOffset = -1.0f;
+
+  Property::Map targetPropertyMap2;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH]  = targetBorderlineWidth;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_COLOR]  = targetBorderlineColor;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_OFFSET] = targetBorderlineOffset;
+
+  // Update Properties with Borderline
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
+
+  Property::Map resultMap2;
+  imageVisual.CreatePropertyMap( resultMap2 );
+
+  // Test property values: they should be updated
+  Property::Value* borderlineWidthValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_WIDTH, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineWidthValue);
+  DALI_TEST_EQUALS(borderlineWidthValue->Get<float>(), targetBorderlineWidth, TEST_LOCATION);
+
+  Property::Value* borderlineColorValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(borderlineColorValue);
+  DALI_TEST_EQUALS(borderlineColorValue->Get<Vector4>(), targetBorderlineColor, TEST_LOCATION);
+
+  Property::Value* borderlineOffsetValue = resultMap2.Find(DevelVisual::Property::BORDERLINE_OFFSET, Property::FLOAT);
+  DALI_TEST_CHECK(borderlineOffsetValue);
+  DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), targetBorderlineOffset, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    // Note : borderline width is zero. so we don't change shader!
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    // Note : borderline width is zero. so we don't change shader!
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader doesn't changed
+  DALI_TEST_CHECK( !(callStack.FindMethod("CreateShader")) );
+
+  END_TEST;
+}
+
+int UtcDaliVisualUpdatePropertyChangeShader03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualUpdatePropertyChangeShader03: Test update property by DoAction. Blur Radius" );
+
+  TraceCallStack& callStack = application.GetGraphicsController().mCallStack;
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  // Case ImageVisual
+  propertyMap[Visual::Property::TYPE] = Visual::Type::COLOR;
+  propertyMap[ColorVisual::Property::MIX_COLOR] = Color::BLUE;
+
+  Visual::Base imageVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  application.SendNotification();
+  application.Render();
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BLUR 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BLUR 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") == std::string::npos );
+  }
+
+  float   targetBlurRadius   = 15.0f;
+  Vector4 targetCornerRadius = Vector4(1.0f, 0.1f, 1.1f, 0.0f);
+
+  Property::Map targetPropertyMap;
+  targetPropertyMap[DevelColorVisual::Property::BLUR_RADIUS] = targetBlurRadius;
+  targetPropertyMap[DevelVisual::Property::CORNER_RADIUS]    = targetCornerRadius;
+  targetPropertyMap[DevelVisual::Property::BORDERLINE_WIDTH] = 10.0f; // Don't care. just dummy
+
+  callStack.Reset();
+  callStack.Enable(true);
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap);
+
+  Property::Map resultMap;
+  imageVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* blurRadiusValue = resultMap.Find(DevelColorVisual::Property::BLUR_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(blurRadiusValue);
+  DALI_TEST_EQUALS(blurRadiusValue->Get<float>(), targetBlurRadius, TEST_LOCATION);
+
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader changed
+  DALI_TEST_CHECK( (callStack.FindMethod("CreateShader")) );
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Property::Map targetPropertyMap2;
+  targetPropertyMap2[DevelColorVisual::Property::BLUR_RADIUS] = 0.0f;
+  targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS]    = Vector4::ZERO;
+  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH] = 15.0f; // Don't care. just dummy
+
+  // Update Properties with CornerRadius
+  DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
+
+  Property::Map resultMap2;
+  imageVisual.CreatePropertyMap( resultMap2 );
+
+  // Test property values: they should be updated
+  blurRadiusValue = resultMap2.Find(DevelColorVisual::Property::BLUR_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(blurRadiusValue);
+  DALI_TEST_EQUALS(blurRadiusValue->Get<float>(), 0.0f, TEST_LOCATION);
+
+  cornerRadiusValue = resultMap2.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), Vector4::ZERO, TEST_LOCATION);
+
+  // Get shader
+  {
+    Renderer renderer = dummyControl.GetRendererAt( 0 );
+    Shader shader = renderer.GetShader();
+    Property::Value value = shader.GetProperty( Shader::Property::PROGRAM );
+    Property::Map* map = value.GetMap();
+    DALI_TEST_CHECK( map );
+
+    Property::Value* fragment = map->Find( "fragment" ); // fragment key name from shader-impl.cpp
+    DALI_TEST_CHECK( fragment );
+    std::string fragmentShader;
+    DALI_TEST_CHECK( fragment->Get(fragmentShader) );
+    // Note : mAlwaysUsingBlurRadius and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( fragmentShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+
+    Property::Value* vertex = map->Find( "vertex" ); // vertex key name from shader-impl.cpp
+    std::string vertexShader;
+    DALI_TEST_CHECK( vertex->Get(vertexShader) );
+    // Note : mAlwaysUsingBlurRadius and mAlwaysUsingCornerRadius is true.
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BLUR 1") != std::string::npos );
+    // Note : We ignore borderline when blur radius occured
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_BORDERLINE 1") == std::string::npos );
+    DALI_TEST_CHECK( vertexShader.find("#define IS_REQUIRED_ROUNDED_CORNER 1") != std::string::npos );
+  }
+
+  // Send shader compile signal
+  application.SendNotification();
+  application.Render();
+
+  callStack.Enable(false);
+
+  // Shader not changed
+  DALI_TEST_CHECK( !(callStack.FindMethod("CreateShader")) );
+
+  END_TEST;
 }
\ No newline at end of file
index f3f073b..a7b4ae4 100644 (file)
@@ -80,6 +80,16 @@ void ScrollBy(TextEditor textEditor, Vector2 scroll)
   GetImpl(textEditor).ScrollBy(scroll);
 }
 
+Vector<Vector2> GetTextSize(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textEditor).GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> GetTextPosition(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textEditor).GetTextPosition(startIndex, endIndex);
+}
+
 string CopyText(TextEditor textEditor)
 {
   return GetImpl(textEditor).CopyText();
index 177be64..74c19df 100644 (file)
@@ -451,6 +451,30 @@ DALI_TOOLKIT_API void SelectText(TextEditor textEditor, const uint32_t start, co
 DALI_TOOLKIT_API void ScrollBy(TextEditor textEditor, Vector2 scroll);
 
 /**
+ * @brief Get the rendered size of a specific text range.
+ * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @param[in] startIndex start index of the text requested to calculate size for.
+ * @param[in] endIndex end index(included) of the text requested to calculate size for.
+ * @return list of sizes of the reuested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextSize(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
+ * @brief Get the top/left rendered position of a specific text range.
+ * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @param[in] startIndex start index of the text requested to get position to.
+ * @param[in] endIndex end index(included) of the text requested to get position to.
+ * @return list of positions of the requested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextPosition(TextEditor textEditor, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
  * @brief Copy and return the selected text of TextEditor.
  *
  * @param[in] textEditor The instance of TextEditor.
index e5abdfa..fce7819 100644 (file)
@@ -70,6 +70,16 @@ void SelectText(TextField textField, const uint32_t start, const uint32_t end)
   GetImpl(textField).SelectText(start, end);
 }
 
+Vector<Vector2> GetTextSize(TextField textField, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textField).GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> GetTextPosition(TextField textField, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textField).GetTextPosition(startIndex, endIndex);
+}
+
 string CopyText(TextField textField)
 {
   return GetImpl(textField).CopyText();
index 10d79e8..7498110 100644 (file)
@@ -336,6 +336,30 @@ using SelectionClearedSignalType = Signal<void(TextField)>;
 DALI_TOOLKIT_API SelectionClearedSignalType& SelectionClearedSignal(TextField textField);
 
 /**
+ * @brief Get the rendered size of a specific text range.
+ * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textField The instance of TextField.
+ * @param[in] startIndex start index of the text requested to calculate size for.
+ * @param[in] endIndex end index(included) of the text requested to calculate size for.
+ * @return list of sizes of the reuested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextSize(TextField textField, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
+ * @brief Get the top/left rendered position of a specific text range.
+ * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textField The instance of TextField.
+ * @param[in] startIndex start index of the text requested to get position to.
+ * @param[in] endIndex end index(included) of the text requested to get position to.
+ * @return list of positions of the requested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextPosition(TextField textField, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
  * @brief Select the whole text of TextField.
  *
  * @param[in] textField The instance of TextField.
index c117152..5e6bc02 100644 (file)
@@ -35,6 +35,16 @@ TextFitChangedSignalType& TextFitChangedSignal(TextLabel textLabel)
   return GetImpl(textLabel).TextFitChangedSignal();
 }
 
+Vector<Vector2> GetTextSize(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textLabel).GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> GetTextPosition(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex)
+{
+  return GetImpl(textLabel).GetTextPosition(startIndex, endIndex);
+}
+
 } // namespace DevelTextLabel
 
 } // namespace Toolkit
index 6bcbdd0..17a808f 100644 (file)
@@ -167,6 +167,30 @@ enum Type
 } // namespace Property
 
 /**
+ * @brief Get the rendered size of a specific text range.
+ * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] startIndex start index of the text requested to calculate size for.
+ * @param[in] endIndex end index(included) of the text requested to calculate size for.
+ * @return list of sizes of the reuested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextSize(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
+ * @brief Get the top/left rendered position of a specific text range.
+ * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+ * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+ *
+ * @param[in] textLabel The instance of TextLabel.
+ * @param[in] startIndex start index of the text requested to get position to.
+ * @param[in] endIndex end index(included) of the text requested to get position to.
+ * @return list of positions of the requested text.
+ */
+DALI_TOOLKIT_API Vector<Vector2> GetTextPosition(TextLabel textLabel, const uint32_t startIndex, const uint32_t endIndex);
+
+/**
  * @brief Anchor clicked signal type.
  *
  * @note Signal
index 70e9c76..7ff5875 100644 (file)
@@ -111,7 +111,7 @@ const Scripting::StringEnum ALIGNMENT_STRING_TABLE[] =
 
 const unsigned int ALIGNMENT_STRING_TABLE_COUNT = sizeof(ALIGNMENT_STRING_TABLE) / sizeof(ALIGNMENT_STRING_TABLE[0]);
 
-const Property::Index VISUAL_INDEX_FOR_STATE[][Button::STATE_COUNT] =
+const Property::Index VISUAL_INDEX_FOR_STATE[Button::STATE_COUNT][Button::VISUAL_STATE_COUNT] =
   {
     {Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::UNSELECTED_VISUAL},
     {Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, Toolkit::Button::Property::SELECTED_VISUAL},
index 44c1176..5e9e7d9 100644 (file)
@@ -341,6 +341,16 @@ float TextEditor::GetVerticalScrollPosition()
   return 0;
 }
 
+Vector<Vector2> TextEditor::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> TextEditor::GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextPosition(startIndex, endIndex);
+}
+
 string TextEditor::GetSelectedText() const
 {
   string selectedText = "";
index e4b5fa5..9d41735 100644 (file)
@@ -317,6 +317,28 @@ public:
   float GetVerticalScrollPosition();
 
   /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
    * @copydoc Text::SelectableControlInterface::GetSelectedText()
    */
   string GetSelectedText() const override;
index 2f4ef60..9cefc6c 100644 (file)
@@ -204,8 +204,8 @@ Toolkit::TextField::InputStyle::Mask ConvertInputStyle(Text::InputStyle::Mask in
 bool IsHiddenInput(Toolkit::TextField textField)
 {
   Property::Map hiddenInputSettings = textField.GetProperty<Property::Map>(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS);
-  auto mode  = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::MODE);
-  if (mode && (mode->Get<int>() != Toolkit::HiddenInput::Mode::HIDE_NONE))
+  auto          mode                = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::MODE);
+  if(mode && (mode->Get<int>() != Toolkit::HiddenInput::Mode::HIDE_NONE))
   {
     return true;
   }
@@ -215,8 +215,8 @@ bool IsHiddenInput(Toolkit::TextField textField)
 char GetSubstituteCharacter(Toolkit::TextField textField)
 {
   Property::Map hiddenInputSettings = textField.GetProperty<Property::Map>(Toolkit::TextField::Property::HIDDEN_INPUT_SETTINGS);
-  auto substChar = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER);
-  if (substChar)
+  auto          substChar           = hiddenInputSettings.Find(Toolkit::HiddenInput::Property::SUBSTITUTE_CHARACTER);
+  if(substChar)
   {
     return static_cast<char>(substChar->Get<int>());
   }
@@ -1128,10 +1128,20 @@ TextField::~TextField()
   }
 }
 
+Vector<Vector2> TextField::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> TextField::GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextPosition(startIndex, endIndex);
+}
+
 std::string TextField::AccessibleImpl::GetName()
 {
   auto self = Toolkit::TextField::DownCast(Self());
-  if (IsHiddenInput(self))
+  if(IsHiddenInput(self))
   {
     return {};
   }
@@ -1193,8 +1203,8 @@ bool TextField::AccessibleImpl::SetCursorOffset(size_t offset)
 Dali::Accessibility::Range TextField::AccessibleImpl::GetTextAtOffset(
   size_t offset, Dali::Accessibility::TextBoundary boundary)
 {
-  auto self     = Toolkit::TextField::DownCast(Self());
-  auto range    = Dali::Accessibility::Range{};
+  auto self  = Toolkit::TextField::DownCast(Self());
+  auto range = Dali::Accessibility::Range{};
 
   if(IsHiddenInput(self))
   {
@@ -1297,14 +1307,14 @@ Dali::Accessibility::Range TextField::AccessibleImpl::GetRangeOfSelection(size_t
     return {};
   }
 
-  auto self = Toolkit::TextField::DownCast(Self());
+  auto self       = Toolkit::TextField::DownCast(Self());
   auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
-  auto indices = controller->GetSelectionIndexes();
+  auto indices    = controller->GetSelectionIndexes();
 
   auto startOffset = static_cast<size_t>(indices.first);
-  auto endOffset = static_cast<size_t>(indices.second);
+  auto endOffset   = static_cast<size_t>(indices.second);
 
-  if (IsHiddenInput(self))
+  if(IsHiddenInput(self))
   {
     return {startOffset, endOffset, std::string(endOffset - startOffset, GetSubstituteCharacter(self))};
   }
index 7e18a82..1f97263 100644 (file)
@@ -325,6 +325,28 @@ public:
    */
   void AnchorClicked(const std::string& href) override;
 
+  /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const;
+
 private: // Implementation
   /**
    * @copydoc Dali::Toolkit::Text::Controller::(InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent)
index 22990d9..9e15b34 100644 (file)
@@ -30,6 +30,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/text/property-string-parser.h>
 #include <dali-toolkit/internal/text/rendering/text-backend.h>
@@ -37,7 +38,6 @@
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
 #include <dali-toolkit/internal/text/text-view.h>
-#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/public-api/text/text-enumerations.h>
 
 #include <dali-toolkit/devel-api/controls/control-devel.h>
@@ -729,10 +729,10 @@ Property::Value TextLabel::GetProperty(BaseObject* object, Property::Index index
       }
       case Toolkit::DevelTextLabel::Property::TEXT_FIT:
       {
-        const bool  enabled  = impl.mController->IsTextFitEnabled();
-        const float minSize  = impl.mController->GetTextFitMinSize();
-        const float maxSize  = impl.mController->GetTextFitMaxSize();
-        const float stepSize = impl.mController->GetTextFitStepSize();
+        const bool  enabled   = impl.mController->IsTextFitEnabled();
+        const float minSize   = impl.mController->GetTextFitMinSize();
+        const float maxSize   = impl.mController->GetTextFitMaxSize();
+        const float stepSize  = impl.mController->GetTextFitStepSize();
         const float pointSize = impl.mController->GetTextFitPointSize();
 
         Property::Map map;
@@ -1136,6 +1136,16 @@ TextLabel::~TextLabel()
 {
 }
 
+Vector<Vector2> TextLabel::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextSize(startIndex, endIndex);
+}
+
+Vector<Vector2> TextLabel::GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const
+{
+  return mController->GetTextPosition(startIndex, endIndex);
+}
+
 std::string TextLabel::AccessibleImpl::GetNameRaw()
 {
   auto self = Toolkit::TextLabel::DownCast(Self());
index 5c9a6b1..d776c32 100644 (file)
@@ -98,6 +98,28 @@ public:
    */
   Text::ControllerPtr GetTextController();
 
+  /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const;
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(const uint32_t startIndex, const uint32_t endIndex) const;
+
 private: // From Control
   /**
    * @copydoc Control::OnInitialize()
@@ -209,7 +231,7 @@ private: // Data
   std::vector<Toolkit::TextAnchor> mAnchorActors;
 
   // Signals
-  Toolkit::DevelTextLabel::AnchorClickedSignalType mAnchorClickedSignal;
+  Toolkit::DevelTextLabel::AnchorClickedSignalType  mAnchorClickedSignal;
   Toolkit::DevelTextLabel::TextFitChangedSignalType mTextFitChangedSignal;
 
   int  mRenderingBackend;
index 8438261..0ad41cd 100644 (file)
@@ -208,6 +208,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/xhtml-entities.cpp
    ${toolkit_src_dir}/drag-drop-detector/drag-and-drop-detector-impl.cpp
    ${toolkit_src_dir}/text/emoji-helper.cpp
+   ${toolkit_src_dir}/text/text-geometry.cpp
 )
 
 SET( SOURCES ${SOURCES}
index 19f2131..be985d1 100644 (file)
@@ -37,6 +37,7 @@
 #include <dali-toolkit/internal/text/text-controller-relayouter.h>
 #include <dali-toolkit/internal/text/text-controller-text-updater.h>
 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
+#include <dali-toolkit/internal/text/text-geometry.h>
 
 namespace
 {
@@ -1233,6 +1234,24 @@ void Controller::RequestRelayout()
   mImpl->RequestRelayout();
 }
 
+Vector<Vector2> Controller::GetTextSize(CharacterIndex startIndex, CharacterIndex endIndex)
+{
+  Vector<Vector2> sizesList;
+  Vector<Vector2> positionsList;
+
+  GetTextGeometry(mImpl->mModel, startIndex, endIndex, sizesList, positionsList);
+  return sizesList;
+}
+
+Vector<Vector2> Controller::GetTextPosition(CharacterIndex startIndex, CharacterIndex endIndex)
+{
+  Vector<Vector2> sizesList;
+  Vector<Vector2> positionsList;
+
+  GetTextGeometry(mImpl->mModel, startIndex, endIndex, sizesList, positionsList);
+  return positionsList;
+}
+
 bool Controller::IsInputStyleChangedSignalsQueueEmpty()
 {
   return mImpl->IsInputStyleChangedSignalsQueueEmpty();
index a582110..b16186b 100644 (file)
@@ -23,9 +23,9 @@
 #include <dali/public-api/events/gesture.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
 #include <dali-toolkit/devel-api/controls/text-controls/text-selection-popup-callback-interface.h>
-#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
 #include <dali-toolkit/internal/text/hidden-text.h>
@@ -1516,6 +1516,28 @@ public: // Queries & retrieves.
   Dali::LayoutDirection::Type GetLayoutDirection(Dali::Actor& actor) const;
 
   /**
+   * @brief Get the rendered size of a specific text range.
+   * if the requested text is at multilines, multiple sizes will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to calculate size for.
+   * @param[in] endIndex end index(included) of the text requested to calculate size for.
+   * @return list of sizes of the reuested text.
+   */
+  Vector<Vector2> GetTextSize(CharacterIndex startIndex, CharacterIndex endIndex);
+
+  /**
+   * @brief Get the top/left rendered position of a specific text range.
+   * if the requested text is at multilines, multiple positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple positions will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] startIndex start index of the text requested to get position to.
+   * @param[in] endIndex end index(included) of the text requested to get position to.
+   * @return list of positions of the requested text.
+   */
+  Vector<Vector2> GetTextPosition(CharacterIndex startIndex, CharacterIndex endIndex);
+
+  /**
    * @brief Sets the layout direction changed.
    */
   void ChangedLayoutDirection();
diff --git a/dali-toolkit/internal/text/text-geometry.cpp b/dali-toolkit/internal/text/text-geometry.cpp
new file mode 100644 (file)
index 0000000..9fa5ad1
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * 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/text/text-geometry.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+
+using namespace Dali;
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+bool GetNextLine(GlyphIndex index, LineIndex& lineIndex, LineRun*& lineRun, GlyphIndex& lastGlyphOfLine, Length numberOfLines)
+{
+  if(index == lastGlyphOfLine)
+  {
+    ++lineIndex;
+    if(lineIndex < numberOfLines)
+    {
+      ++lineRun;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void UpdateLineInfo(const LineRun* lineRun, float& currentLineOffset, float& currentLineHeight, GlyphIndex& lastGlyphOfLine)
+{
+  lastGlyphOfLine   = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+  currentLineOffset = currentLineOffset + currentLineHeight;
+  currentLineHeight = GetLineHeight(*lineRun);
+}
+
+void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterIndex endIndex, Vector<Vector2>& sizesList, Vector<Vector2>& positionsList)
+{
+  VisualModelPtr&  visualModel  = textModel->mVisualModel;
+  LogicalModelPtr& logicalModel = textModel->mLogicalModel;
+
+  const GlyphIndex* const         charactersToGlyphBuffer        = visualModel->mCharactersToGlyph.Begin();
+  const Length* const             glyphsPerCharacterBuffer       = visualModel->mGlyphsPerCharacter.Begin();
+  const GlyphInfo* const          glyphsBuffer                   = visualModel->mGlyphs.Begin();
+  const Vector2* const            positionsBuffer                = visualModel->mGlyphPositions.Begin();
+  const Length* const             charactersPerGlyphBuffer       = visualModel->mCharactersPerGlyph.Begin();
+  const CharacterIndex* const     glyphToCharacterBuffer         = visualModel->mGlyphsToCharacters.Begin();
+  const CharacterDirection* const modelCharacterDirectionsBuffer = (0u != logicalModel->mCharacterDirections.Count()) ? logicalModel->mCharacterDirections.Begin() : NULL;
+
+  if(startIndex >= logicalModel->mText.Count() && endIndex >= logicalModel->mText.Count())
+    return;
+
+  if(startIndex >= logicalModel->mText.Count())
+    startIndex = logicalModel->mText.Count() - 1;
+
+  if(endIndex >= logicalModel->mText.Count())
+    endIndex = logicalModel->mText.Count() - 1;
+
+  if(startIndex > endIndex)
+  {
+    std::swap(startIndex, endIndex);
+  }
+
+  LineRun*   lineRun    = visualModel->mLines.Begin();
+  GlyphIndex glyphStart = *(charactersToGlyphBuffer + startIndex);
+
+  //if glyph not in the first line (in some ellipsis cases)
+  if(glyphStart < lineRun->glyphRun.glyphIndex)
+  {
+    glyphStart = lineRun->glyphRun.glyphIndex;
+    startIndex = *(glyphToCharacterBuffer + glyphStart);
+
+    if(startIndex > endIndex)
+    {
+      std::swap(startIndex, endIndex);
+    }
+  }
+
+  const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + endIndex);
+  GlyphIndex   glyphEnd       = *(charactersToGlyphBuffer + endIndex) + ((numberOfGlyphs > 0) ? numberOfGlyphs - 1u : 0u);
+  LineIndex    lineIndex      = visualModel->GetLineOfCharacter(startIndex);
+  Length       numberOfLines  = visualModel->GetTotalNumberOfLines();
+
+  LineIndex firstLineIndex = lineIndex;
+  Size      textInLineSize;
+  Vector2   textInLinePosition;
+
+  lineRun += firstLineIndex;
+
+  //get the first line and its vertical offset
+  float      currentLineOffset = CalculateLineOffset(visualModel->mLines, firstLineIndex);
+  float      currentLineHeight = GetLineHeight(*lineRun);
+  GlyphIndex lastGlyphOfLine   = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1;
+
+  // Check if the first/last glyph is a ligature that needs be splitted like English fi or Arabic ï»».
+  const Length numberOfCharactersStart = *(charactersPerGlyphBuffer + glyphStart);
+  const Length numberOfCharactersEnd   = *(charactersPerGlyphBuffer + glyphEnd);
+
+  bool splitStartGlyph = (numberOfCharactersStart > 1u) && HasLigatureMustBreak(logicalModel->GetScript(startIndex));
+  bool splitEndGlyph   = (glyphStart != glyphEnd) && (numberOfCharactersEnd > 1u) && HasLigatureMustBreak(logicalModel->GetScript(endIndex));
+
+  Vector2            currentSize;
+  Vector2            currentPosition;
+  Vector2            blockSize;
+  Vector2            blockPos;
+  CharacterDirection isCurrentRightToLeft;
+
+  CharacterDirection                      isPrevoiusRightToLeft           = (nullptr != modelCharacterDirectionsBuffer ? *(modelCharacterDirectionsBuffer + startIndex) : false);
+  const bool                              isEllipsisEnabled               = textModel->mElideEnabled;
+  const GlyphIndex                        startIndexOfGlyphs              = textModel->GetStartIndexOfElidedGlyphs();
+  const GlyphIndex                        endIndexOfGlyphs                = textModel->GetEndIndexOfElidedGlyphs();
+  const GlyphIndex                        firstMiddleIndexOfElidedGlyphs  = textModel->GetFirstMiddleIndexOfElidedGlyphs();
+  const GlyphIndex                        secondMiddleIndexOfElidedGlyphs = textModel->GetSecondMiddleIndexOfElidedGlyphs();
+  const DevelText::EllipsisPosition::Type ellipsisPosition                = textModel->GetEllipsisPosition();
+
+  for(GlyphIndex index = glyphStart; index <= glyphEnd; ++index)
+  {
+    if(isEllipsisEnabled)
+    {
+      if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
+      {
+        if(index >= firstMiddleIndexOfElidedGlyphs &&
+           index < secondMiddleIndexOfElidedGlyphs)
+        {
+          if((index - 1 == firstMiddleIndexOfElidedGlyphs) && (firstMiddleIndexOfElidedGlyphs != 0))
+          {
+            sizesList.PushBack(blockSize);
+            positionsList.PushBack(blockPos);
+          }
+
+          if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
+          {
+            UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
+          }
+          // Ignore any glyph that was removed
+          continue;
+        }
+      }
+      else
+      {
+        if((ellipsisPosition == DevelText::EllipsisPosition::END) && (index >= endIndexOfGlyphs))
+        {
+          //skip remaining elided glyphs
+          break;
+        }
+        else if((ellipsisPosition == DevelText::EllipsisPosition::START) && (index <= startIndexOfGlyphs))
+        {
+          if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
+          {
+            UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
+          }
+
+          continue;
+        }
+      }
+    }
+
+    const GlyphInfo& glyph    = *(glyphsBuffer + index);
+    const Vector2&   position = *(positionsBuffer + index);
+
+    // If NULL, means all of the characters is left to right.
+    isCurrentRightToLeft = (nullptr != modelCharacterDirectionsBuffer ? *(modelCharacterDirectionsBuffer + *(glyphToCharacterBuffer + index)) : false);
+
+    if(splitStartGlyph && (index == glyphStart))
+    {
+      // If the first glyph is a ligature that needs to be splitted, we may need only to add part of the glyph.
+      const float          glyphAdvance       = glyph.advance / static_cast<float>(numberOfCharactersStart);
+      const CharacterIndex interGlyphIndex    = startIndex - *(glyphToCharacterBuffer + glyphStart);
+      const Length         numberOfCharacters = (glyphStart == glyphEnd) ? (endIndex - startIndex) + 1 : (numberOfCharactersStart - interGlyphIndex);
+
+      currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x + glyphAdvance * static_cast<float>(isCurrentRightToLeft ? (numberOfCharactersStart - interGlyphIndex - numberOfCharacters) : interGlyphIndex);
+      currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
+      currentSize.x     = static_cast<float>(numberOfCharacters) * glyphAdvance;
+      currentSize.y     = currentLineHeight;
+      splitStartGlyph   = false;
+    }
+    else if(splitEndGlyph && (index == glyphEnd))
+    {
+      const float          glyphAdvance       = glyph.advance / static_cast<float>(numberOfCharactersEnd);
+      const CharacterIndex interGlyphIndex    = endIndex - *(glyphToCharacterBuffer + glyphEnd);
+      const Length         numberOfCharacters = numberOfCharactersEnd - interGlyphIndex - 1;
+
+      currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x + (isCurrentRightToLeft ? (glyphAdvance * static_cast<float>(numberOfCharacters)) : 0.f);
+      currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
+      currentSize.x     = static_cast<float>(interGlyphIndex + 1) * glyphAdvance;
+      currentSize.y     = currentLineHeight;
+      splitEndGlyph     = false;
+    }
+    else
+    {
+      currentPosition.x = lineRun->alignmentOffset + position.x - glyph.xBearing + textModel->mScrollPosition.x;
+      currentPosition.y = currentLineOffset + textModel->mScrollPosition.y;
+      currentSize.x     = glyph.advance;
+      currentSize.y     = currentLineHeight;
+
+      // if there is next line to retrieve.
+      if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines))
+      {
+        UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine);
+      }
+    }
+
+    if((index == glyphStart) || (isEllipsisEnabled && (((ellipsisPosition == DevelText::EllipsisPosition::MIDDLE) && (index == secondMiddleIndexOfElidedGlyphs)) || ((ellipsisPosition == DevelText::EllipsisPosition::START) && (index - 1 == startIndexOfGlyphs)))))
+    {
+      blockPos  = currentPosition;
+      blockSize = currentSize;
+    }
+    else if((isPrevoiusRightToLeft != isCurrentRightToLeft) || (blockPos.y != currentPosition.y)) //new direction or new line
+    {
+      sizesList.PushBack(blockSize);
+      positionsList.PushBack(blockPos);
+
+      blockPos  = currentPosition;
+      blockSize = currentSize;
+    }
+    else
+    {
+      if(isCurrentRightToLeft)
+      {
+        blockPos.x -= currentSize.x;
+      }
+
+      blockSize.x += currentSize.x;
+    }
+
+    isPrevoiusRightToLeft = isCurrentRightToLeft;
+  }
+
+  //add last block
+  sizesList.PushBack(blockSize);
+  positionsList.PushBack(blockPos);
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/text-geometry.h b/dali-toolkit/internal/text/text-geometry.h
new file mode 100644 (file)
index 0000000..fb118f0
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef DALI_TOOLKIT_TEXT_GEOMETRY_H
+#define DALI_TOOLKIT_TEXT_GEOMETRY_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/text-model.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+   * @brief Get the rendered size & position of a specific text range.
+   * if the requested text is at multilines, multiple sizes/positions will be returned for each text located in a separate line.
+   * if a line contains characters with different directions, multiple sizes will be returned for each block of contiguous characters with the same direction.
+   *
+   * @param[in] textModel text model containing text info.
+   * @param[in] startIndex start index of the text requested to get position/size for.
+   * @param[in] endIndex end index(included) of the text requested to get position/size for.
+   * @param[in] sizesList list of sizes for the reuested text.
+   * @param[in] positionsList list of positions for the requested text
+   */
+void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterIndex endIndex, Vector<Vector2>& sizesList, Vector<Vector2>& positionsList);
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_GEOMETRY_H
index 5fa3843..9c57494 100644 (file)
@@ -204,6 +204,11 @@ void VisualModel::GetGlyphPositions(Vector2*   glyphPositions,
   memcpy(glyphPositions, mGlyphPositions.Begin() + glyphIndex, numberOfGlyphs * sizeof(Vector2));
 }
 
+Length VisualModel::GetTotalNumberOfLines() const
+{
+  return mLines.Size();
+}
+
 void VisualModel::GetNumberOfLines(GlyphIndex glyphIndex,
                                    Length     numberOfGlyphs,
                                    LineIndex& firstLine,
index 359b695..ac9b12d 100644 (file)
@@ -120,6 +120,13 @@ public:
   // Line interface.
 
   /**
+   * @brief Retrieves the total number of lines.
+   *
+   * @return The number of lines.
+   */
+  Length GetTotalNumberOfLines() const;
+
+  /**
    * @brief Retrieves the number of lines and the index to the first line where the given range of glyphs is laid out.
    *
    * @param[in] glyphIndex Index to the first glyph.
index 1d21356..ad8b5ac 100644 (file)
@@ -133,7 +133,7 @@ public: // from Visual
   /**
    * @copydoc Visual::Base::OnDoAction
    */
-  void OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes) override;
+  void OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) override;
 
 protected:
   /**
index 73b0931..31e0c55 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/integration-api/debug.h>
 
 //INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
@@ -113,6 +114,27 @@ void ColorVisual::DoSetProperties(const Property::Map& propertyMap)
     {
       DALI_LOG_ERROR("ColorVisual:DoSetProperties:: BLUR_RADIUS property has incorrect type: %d\n", blurRadiusValue->GetType());
     }
+
+    if(mBlurRadiusIndex != Property::INVALID_INDEX)
+    {
+      mImpl->mRenderer.SetProperty(mBlurRadiusIndex, mBlurRadius);
+    }
+    else if(DALI_UNLIKELY(mImpl->mRenderer && (!EqualsZero(mBlurRadius) || mAlwaysUsingBlurRadius)))
+    {
+      // Unusual case. SetProperty called after OnInitialize().
+      // Assume that DoAction call UPDATE_PROPERTY.
+      // We must regist properies into renderer, and update shader.
+
+      // BlurRadius added by this action. Regist property to renderer.
+      mBlurRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelColorVisual::Property::BLUR_RADIUS, BLUR_RADIUS_NAME, mBlurRadius);
+      mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+      // Change the shader must not be occured many times. we always have to use blur feature.
+      mAlwaysUsingBlurRadius = true;
+
+      // Change shader
+      UpdateShader();
+    }
   }
 }
 
index a8de5a8..577a433 100644 (file)
@@ -46,6 +46,23 @@ ImageAtlasManager::~ImageAtlasManager()
 {
 }
 
+bool ImageAtlasManager::CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const
+{
+  ImageDimensions dimensions = size;
+  ImageDimensions zero;
+  if(size == zero)
+  {
+    dimensions = Dali::GetClosestImageSize(url.GetUrl());
+  }
+
+  // big image, atlasing is not applied
+  if(static_cast<uint32_t>(dimensions.GetWidth()) * static_cast<uint32_t>(dimensions.GetHeight()) > MAX_ITEM_AREA || dimensions.GetWidth() > DEFAULT_ATLAS_SIZE || dimensions.GetHeight() > DEFAULT_ATLAS_SIZE)
+  {
+    return false;
+  }
+  return true;
+}
+
 TextureSet ImageAtlasManager::Add(Vector4&             textureRect,
                                   const VisualUrl&     url,
                                   ImageDimensions&     size,
index b8f5a11..d2630c1 100644 (file)
@@ -52,6 +52,15 @@ public:
   ImageAtlasManager();
 
   /**
+   * @brief Check whether the image of url could be Atlas or not.
+   *
+   * @param [in] url The URL of the resource image file to use.
+   * @param [in] size The width and height to fit the loaded image to.
+   * @return True if the image could be Atlas.
+   */
+  bool CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const;
+
+  /**
    * @brief Add an image to the atlas.
    *
    * @note To make the atlasing efficient, an valid size should be provided.
index 650c508..7676bf8 100644 (file)
@@ -477,7 +477,7 @@ void ImageVisual::GetNaturalSize(Vector2& naturalSize)
     }
 
     auto textureSet = mImpl->mRenderer.GetTextures();
-    if(textureSet)
+    if(textureSet && textureSet.GetTextureCount())
     {
       auto texture = textureSet.GetTexture(0);
       if(texture)
@@ -795,11 +795,11 @@ void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
   }
 }
 
-void ImageVisual::OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes)
+void ImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
 {
   // Check if action is valid for this visual type and perform action if possible
 
-  switch(actionName)
+  switch(actionId)
   {
     case DevelImageVisual::Action::RELOAD:
     {
index c0a3f75..55fb59b 100644 (file)
@@ -171,7 +171,7 @@ public: // from Visual
   /**
    * @copydoc Visual::Base::OnDoAction
    */
-  void OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes) override;
+  void OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) override;
 
 protected:
   /**
index 093954e..49867c4 100644 (file)
@@ -122,8 +122,10 @@ TextureManager::MaskingData::MaskingData()
 }
 
 TextureManager::TextureManager()
-: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }),
-  mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }),
+: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]()
+                     { return AsyncLoadingHelper(*this); }),
+  mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]()
+                      { return AsyncLoadingHelper(*this); }),
   mExternalTextures(),
   mLifecycleObservers(),
   mLoadQueue(),
@@ -173,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, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex);
+    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false);
     TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
     if(loadState == TextureManager::LoadState::UPLOADED)
     {
@@ -220,7 +222,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   }
   else
   {
-    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
   }
 
   return pixelBuffer;
@@ -251,113 +253,100 @@ TextureSet TextureManager::LoadTexture(
       }
     }
   }
-  else if(synchronousLoading)
+  else
   {
-    PixelData data;
-    if(url.IsValid())
+    // For Atlas
+    if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize))
     {
-      Devel::PixelBuffer pixelBuffer;
-      if(url.IsBufferResource())
-      {
-        const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
-        if(encodedImageBuffer)
-        {
-          pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
-        }
-      }
-      else
-      {
-        pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
-      }
+      Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);
+
       if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
       {
-        Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
+        Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
         if(maskPixelBuffer)
         {
           pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
         }
       }
+
+      PixelData data;
       if(pixelBuffer)
       {
         PreMultiply(pixelBuffer, preMultiplyOnLoad);
         data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+
+        if(data)
+        {
+          textureSet = imageAtlasManager->Add(textureRect, data);
+          if(textureSet)
+          {
+            textureRectSize.SetWidth(data.GetWidth());
+            textureRectSize.SetHeight(data.GetHeight());
+          }
+        }
+        else
+        {
+          DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n");
+        }
       }
-    }
-    if(!data)
-    {
-      DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous loading is failed\n");
-    }
-    else
-    {
-      if(atlasingStatus) // attempt atlasing
-      {
-        textureSet = imageAtlasManager->Add(textureRect, data);
-      }
-      if(!textureSet) // big image, no atlasing or atlasing failed
-      {
-        atlasingStatus  = false;
-        Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(), data.GetWidth(), data.GetHeight());
-        texture.Upload(data);
-        textureSet = TextureSet::New();
-        textureSet.SetTexture(0u, texture);
-      }
-      else
+      if(!textureSet)
       {
-        textureRectSize.SetWidth(data.GetWidth());
-        textureRectSize.SetHeight(data.GetHeight());
+        atlasingStatus = false;
       }
     }
-  }
-  else
-  {
-    loadingStatus = true;
-    if(atlasingStatus)
-    {
-      textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
-    }
-    if(!textureSet) // big image, no atlasing or atlasing failed
+
+    if(!textureSet)
     {
-      atlasingStatus = false;
-      if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid())
+      loadingStatus = true;
+      if(atlasingStatus)
       {
-        textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad);
+        textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
       }
-      else
+      if(!textureSet) // big image, no atlasing or atlasing failed
       {
-        maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl);
-        textureId              = RequestLoad(url,
-                                maskInfo->mAlphaMaskId,
-                                maskInfo->mContentScaleFactor,
-                                desiredSize,
-                                fittingMode,
-                                samplingMode,
-                                TextureManager::NO_ATLAS,
-                                maskInfo->mCropToMask,
-                                textureObserver,
-                                orientationCorrection,
-                                reloadPolicy,
-                                preMultiplyOnLoad);
-      }
+        atlasingStatus = false;
+        if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid())
+        {
+          textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
+        }
+        else
+        {
+          maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, synchronousLoading);
+          textureId              = RequestLoad(url,
+                                  maskInfo->mAlphaMaskId,
+                                  maskInfo->mContentScaleFactor,
+                                  desiredSize,
+                                  fittingMode,
+                                  samplingMode,
+                                  TextureManager::NO_ATLAS,
+                                  maskInfo->mCropToMask,
+                                  textureObserver,
+                                  orientationCorrection,
+                                  reloadPolicy,
+                                  preMultiplyOnLoad,
+                                  synchronousLoading);
+        }
+
+        TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
+        if(loadState == TextureManager::LoadState::UPLOADED)
+        {
+          // UploadComplete has already been called - keep the same texture set
+          textureSet = GetTextureSet(textureId);
+        }
 
-      TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
-      if(loadState == TextureManager::LoadState::UPLOADED)
+        // If we are loading the texture, or waiting for the ready signal handler to complete, inform
+        // caller that they need to wait.
+        loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
+                         loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
+                         loadState == TextureManager::LoadState::MASK_APPLYING ||
+                         loadState == TextureManager::LoadState::MASK_APPLIED ||
+                         loadState == TextureManager::LoadState::NOT_STARTED ||
+                         mQueueLoadFlag);
+      }
+      else
       {
-        // UploadComplete has already been called - keep the same texture set
-        textureSet = GetTextureSet(textureId);
+        textureRectSize = desiredSize;
       }
-
-      // If we are loading the texture, or waiting for the ready signal handler to complete, inform
-      // caller that they need to wait.
-      loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
-                       loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
-                       loadState == TextureManager::LoadState::MASK_APPLYING ||
-                       loadState == TextureManager::LoadState::MASK_APPLIED ||
-                       loadState == TextureManager::LoadState::NOT_STARTED ||
-                       mQueueLoadFlag);
-    }
-    else
-    {
-      textureRectSize = desiredSize;
     }
   }
 
@@ -368,6 +357,11 @@ TextureSet TextureManager::LoadTexture(
     textureSet.SetSampler(0u, sampler);
   }
 
+  if(synchronousLoading)
+  {
+    loadingStatus = false;
+  }
+
   return textureSet;
 }
 
@@ -380,9 +374,10 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  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);
+  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);
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
@@ -397,16 +392,17 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  bool                            synchronousLoading)
 {
-  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
-TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl)
+TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl, bool synchronousLoading)
 {
   // 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, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
@@ -424,7 +420,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   TextureManager::ReloadPolicy    reloadPolicy,
   TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
   Dali::AnimatedImageLoading      animatedImageLoading,
-  uint32_t                        frameIndex)
+  uint32_t                        frameIndex,
+  bool                            synchronousLoading)
 {
   // First check if the requested Texture is cached.
   bool isAnimatedImage = (animatedImageLoading) ? true : false;
@@ -457,37 +454,30 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
   }
 
-  // Check if the requested Texture exist in Encoded Buffer
-  // This mean, that buffer is not cached, and need to be decoded.
-  if(textureId == INVALID_TEXTURE_ID && VisualUrl::BUFFER == url.GetProtocolType())
+  if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
   {
-    std::string location = url.GetLocation();
-    if(location.size() > 0u)
+    if(VisualUrl::BUFFER == url.GetProtocolType())
     {
-      TextureId                 targetId           = std::stoi(location);
-      const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId);
-      if(encodedImageBuffer)
+      std::string location = url.GetLocation();
+      if(location.size() > 0u)
       {
-        textureId = targetId;
-
-        // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
-        UseExternalResource(url.GetUrl());
-
-        // Insert this buffer at mTextureInfoContainer.
-        // This buffer will decode at ImageLoaderThread.
-        bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
-        mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
-        cacheIndex = mTextureInfoContainer.size() - 1u;
+        TextureId                 targetId           = std::stoi(location);
+        const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId);
+        if(encodedImageBuffer)
+        {
+          textureId = targetId;
 
-        DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New buffered texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
+          // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
+          UseExternalResource(url.GetUrl());
+        }
       }
     }
-  }
 
-  if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
-  {
-    // We need a new Texture.
-    textureId        = GenerateUniqueTextureId();
+    if(textureId == INVALID_TEXTURE_ID)
+    {
+      textureId = GenerateUniqueTextureId();
+    }
+
     bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
     mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
     cacheIndex = mTextureInfoContainer.size() - 1u;
@@ -518,48 +508,101 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
   }
 
-  // Check if we should add the observer.
-  // Only do this if we have not loaded yet and it will not have loaded by the end of this method.
-  switch(textureInfo.loadState)
+  if(!synchronousLoading)
   {
-    case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
-    case TextureManager::LoadState::NOT_STARTED:
-    {
-      LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
-      break;
-    }
-    case TextureManager::LoadState::LOADING:
-    case TextureManager::LoadState::WAITING_FOR_MASK:
-    case TextureManager::LoadState::MASK_APPLYING:
-    case TextureManager::LoadState::MASK_APPLIED:
+    // Check if we should add the observer.
+    // Only do this if we have not loaded yet and it will not have loaded by the end of this method.
+    switch(textureInfo.loadState)
     {
-      ObserveTexture(textureInfo, observer);
-      break;
-    }
-    case TextureManager::LoadState::UPLOADED:
-    {
-      if(observer)
+      case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
+      case TextureManager::LoadState::NOT_STARTED:
+      {
+        LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
+        break;
+      }
+      case TextureManager::LoadState::LOADING:
+      case TextureManager::LoadState::WAITING_FOR_MASK:
+      case TextureManager::LoadState::MASK_APPLYING:
+      case TextureManager::LoadState::MASK_APPLIED:
       {
-        LoadOrQueueTexture(textureInfo, observer);
+        ObserveTexture(textureInfo, observer);
+        break;
+      }
+      case TextureManager::LoadState::UPLOADED:
+      {
+        if(observer)
+        {
+          LoadOrQueueTexture(textureInfo, observer);
+        }
+        break;
+      }
+      case TextureManager::LoadState::CANCELLED:
+      {
+        // A cancelled texture hasn't finished loading yet. Treat as a loading texture
+        // (it's ref count has already been incremented, above)
+        textureInfo.loadState = TextureManager::LoadState::LOADING;
+        ObserveTexture(textureInfo, observer);
+        break;
+      }
+      case TextureManager::LoadState::LOAD_FINISHED:
+      {
+        // Loading has already completed.
+        if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+        {
+          LoadOrQueueTexture(textureInfo, observer);
+        }
+        break;
       }
-      break;
     }
-    case TextureManager::LoadState::CANCELLED:
+  }
+  else
+  {
+    // If the image is already finished to load, use cached texture.
+    // We don't need to consider Observer becaouse this is synchronous loading.
+    if(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
+       textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)
     {
-      // A cancelled texture hasn't finished loading yet. Treat as a loading texture
-      // (it's ref count has already been incremented, above)
-      textureInfo.loadState = TextureManager::LoadState::LOADING;
-      ObserveTexture(textureInfo, observer);
-      break;
+      return textureId;
     }
-    case TextureManager::LoadState::LOAD_FINISHED:
+    else
     {
-      // Loading has already completed.
-      if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+      Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);;
+
+      if(!pixelBuffer)
       {
-        LoadOrQueueTexture(textureInfo, observer);
+        // If pixelBuffer loading is failed in synchronously, call Remove() method.
+        Remove(textureId, nullptr);
+        return INVALID_TEXTURE_ID;
+      }
+
+      if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
+      {
+        textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
+        textureInfo.loadState   = LoadState::LOAD_FINISHED;
+      }
+      else // For the image loading.
+      {
+        if(maskTextureId != INVALID_TEXTURE_ID)
+        {
+          int maskCacheIndex = GetCacheIndexFromId(maskTextureId);
+          if(maskCacheIndex != INVALID_CACHE_INDEX)
+          {
+            Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
+            if(maskPixelBuffer)
+            {
+              pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask);
+            }
+          }
+          else
+          {
+            DALI_LOG_ERROR("Mask image is not stored in cache.\n");
+          }
+        }
+        PreMultiply(pixelBuffer, preMultiplyOnLoad);
+
+        // Upload texture
+        UploadTexture(pixelBuffer, textureInfo);
       }
-      break;
     }
   }
 
@@ -687,6 +730,28 @@ TextureManager::LoadState TextureManager::GetTextureStateInternal(TextureId text
   return loadState;
 }
 
+Devel::PixelBuffer TextureManager::LoadImageSynchronously(const VisualUrl&         url,
+                                                          const ImageDimensions    desiredSize,
+                                                          FittingMode::Type        fittingMode,
+                                                          Dali::SamplingMode::Type samplingMode,
+                                                          bool                     orientationCorrection)
+{
+  Devel::PixelBuffer pixelBuffer;
+  if(url.IsBufferResource())
+  {
+    const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
+    if(encodedImageBuffer)
+    {
+      pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+    }
+  }
+  else
+  {
+    pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+  }
+  return pixelBuffer;
+}
+
 TextureSet TextureManager::GetTextureSet(TextureId textureId)
 {
   TextureSet textureSet; // empty handle
@@ -759,7 +824,7 @@ std::string TextureManager::AddExternalEncodedImageBuffer(const EncodedImageBuff
     {
       // If same buffer added, increase reference count and return.
       elem.referenceCount++;
-      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));;
+      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));
     }
   }
   TextureManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer);
@@ -807,7 +872,7 @@ EncodedImageBuffer TextureManager::RemoveExternalEncodedImageBuffer(const std::s
       std::string location = VisualUrl::GetLocation(url);
       if(location.size() > 0u)
       {
-        TextureId id = std::stoi(location);
+        TextureId  id  = std::stoi(location);
         const auto end = mEncodedBufferTextures.end();
         for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter)
         {
@@ -1049,7 +1114,10 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe
       // If there is a mask texture ID associated with this texture, then apply the mask
       // if it's already loaded. If it hasn't, and the mask is still loading,
       // wait for the mask to finish loading.
-      if(textureInfo.maskTextureId != INVALID_TEXTURE_ID)
+      // note, If the texture is already uploaded synchronously during loading,
+      // we don't need to apply mask.
+      if(textureInfo.loadState != LoadState::UPLOADED &&
+         textureInfo.maskTextureId != INVALID_TEXTURE_ID)
       {
         if(textureInfo.loadState == LoadState::MASK_APPLYING)
         {
@@ -1153,7 +1221,7 @@ void TextureManager::ApplyMask(TextureInfo& textureInfo, TextureId maskTextureId
 
 void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo)
 {
-  if(textureInfo.useAtlas != USE_ATLAS)
+  if(textureInfo.loadState != LoadState::UPLOADED && textureInfo.useAtlas != USE_ATLAS)
   {
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId);
 
index 48eea78..7730d28 100644 (file)
@@ -75,9 +75,10 @@ public:
    */
   enum class StorageType : uint8_t
   {
-    KEEP_PIXEL_BUFFER,
-    RETURN_PIXEL_BUFFER,
-    UPLOAD_TO_TEXTURE
+    KEEP_PIXEL_BUFFER,    ///< Keep loaded pixel buffer inside of texture manager without making texture. This could be used for inside pixel process like mask image.
+    RETURN_PIXEL_BUFFER,  ///< Return loaded pixel buffer without making texture.
+                          ///  Because a pixel buffer cannot be used multiple texture, this pixel buffer only cached during loading, and is removed after loading is finished.
+    UPLOAD_TO_TEXTURE     ///< Loaded image will be uploaded to texture and the texture will be returned.
   };
 
   /**
@@ -177,7 +178,6 @@ public:
    *
    * @return                          The texture set containing the frame of animated image, or empty if still loading.
    */
-
   TextureSet LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading,
                                       uint32_t                   frameIndex,
                                       Dali::SamplingMode::Type   samplingMode,
@@ -206,7 +206,6 @@ public:
    *
    * @return                          The pixel buffer containing the image, or empty if still loading.
    */
-
   Devel::PixelBuffer LoadPixelBuffer(const VisualUrl&                url,
                                      Dali::ImageDimensions           desiredSize,
                                      Dali::FittingMode::Type         fittingMode,
@@ -253,7 +252,6 @@ public:
    *
    * @return                          The texture set containing the image, or empty if still loading.
    */
-
   TextureSet LoadTexture(const VisualUrl&             url,
                          Dali::ImageDimensions        desiredSize,
                          Dali::FittingMode::Type      fittingMode,
@@ -293,6 +291,7 @@ public:
    * @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
    * @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
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoad(const VisualUrl&             url,
@@ -303,7 +302,8 @@ public:
                         TextureUploadObserver*       observer,
                         bool                         orientationCorrection,
                         TextureManager::ReloadPolicy reloadPolicy,
-                        MultiplyOnLoad&              preMultiplyOnLoad);
+                        MultiplyOnLoad&              preMultiplyOnLoad,
+                        bool                         synchronousLoading = false);
 
   /**
    * @brief Requests an image load of the given URL, when the texture has
@@ -335,6 +335,7 @@ public:
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
    * @param[in] preMultiplyOnLoad     True if the image color should be multiplied by it's alpha. Set to false if the
    *                                  image has no alpha channel
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoad(const VisualUrl&             url,
@@ -348,13 +349,15 @@ public:
                         TextureUploadObserver*       observer,
                         bool                         orientationCorrection,
                         TextureManager::ReloadPolicy reloadPolicy,
-                        MultiplyOnLoad&              preMultiplyOnLoad);
+                        MultiplyOnLoad&              preMultiplyOnLoad,
+                        bool                         synchronousLoading = false);
 
   /**
    * Requests a masking image to be loaded. This mask is not uploaded to GL,
    * instead, it is stored in CPU memory, and can be used for CPU blending.
    */
-  TextureId RequestMaskLoad(const VisualUrl& maskUrl);
+  TextureId RequestMaskLoad(const VisualUrl& maskUrl,
+                            bool             synchronousLoading = false);
 
   /**
    * @brief Remove a Texture from the TextureManager.
@@ -492,6 +495,7 @@ private:
    *                                  there is no alpha
    * @param[in] animatedImageLoading  The AnimatedImageLoading to load animated image
    * @param[in] frameIndex            The frame index of a frame to be loaded frame
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoadInternal(
@@ -509,7 +513,8 @@ private:
     TextureManager::ReloadPolicy reloadPolicy,
     MultiplyOnLoad&              preMultiplyOnLoad,
     Dali::AnimatedImageLoading   animatedImageLoading,
-    uint32_t                     frameIndex);
+    uint32_t                     frameIndex,
+    bool                         synchronousLoading);
 
   /**
    * @brief Get the current state of a texture
@@ -519,6 +524,23 @@ private:
    */
   LoadState GetTextureStateInternal(TextureId textureId);
 
+  /**
+   * @brief Load a new image synchronously.
+   * @param[in] url                   The URL of the image to load
+   * @param[in] desiredSize           The size the image is likely to appear at.
+   *                                  This can be set to 0,0 for automatic
+   * @param[in] fittingMode           The FittingMode to use
+   * @param[in] samplingMode          The SamplingMode to use
+   * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image,
+   *                                  e.g., from portrait to landscape
+   * @return PixelBuffer of loaded image.
+   */
+  Devel::PixelBuffer LoadImageSynchronously(const VisualUrl&         url,
+                                            const ImageDimensions    desiredSize,
+                                            FittingMode::Type        fittingMode,
+                                            Dali::SamplingMode::Type samplingMode,
+                                            bool                     orientationCorrection);
+
   typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
 
   // Structs:
index f0a7af4..2841655 100644 (file)
@@ -168,6 +168,7 @@ void Visual::Base::SetCustomShader(const Property::Map& shaderMap)
 
 void Visual::Base::SetProperties(const Property::Map& propertyMap)
 {
+  bool needUpdateShader = false;
   for(size_t i = 0; i < propertyMap.Count(); ++i)
   {
     const KeyValuePair&    pair  = propertyMap.GetKeyValue(i);
@@ -300,6 +301,26 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
         {
           mImpl->mRenderer.SetProperty(mImpl->mBorderlineWidthIndex, mImpl->mBorderlineWidth);
         }
+        else if(DALI_UNLIKELY(mImpl->mRenderer && IsBorderlineRequired()))
+        {
+          // Unusual case. SetProperty called after OnInitialize().
+          // Assume that DoAction call UPDATE_PROPERTY.
+          // We must regist properies into renderer, and update shader.
+
+          // Borderline added by this action. Regist property to renderer.
+          mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_WIDTH, BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
+          mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_COLOR, BORDERLINE_COLOR, mImpl->mBorderlineColor);
+          mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
+
+          // Make Blend mode ON_WITHOUT_CULL for transparent mix color.
+          mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
+
+          // Change the shader must not be occured many times. we always have to use borderline feature.
+          mImpl->mAlwaysUsingBorderline = true;
+
+          // Change shader
+          needUpdateShader = true;
+        }
         break;
       }
       case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
@@ -358,6 +379,28 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
         {
           mImpl->mRenderer.SetProperty(mImpl->mCornerRadiusIndex, mImpl->mCornerRadius);
         }
+        else if(DALI_UNLIKELY(mImpl->mRenderer && IsRoundedCornerRequired()))
+        {
+          // Unusual case. SetProperty called after OnInitialize().
+          // Assume that DoAction call UPDATE_PROPERTY.
+          // We must regist properies into renderer, and update shader.
+
+          // CornerRadius added by this action. Regist property to renderer.
+          mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
+          mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
+
+          // Change the shader must not be occured many times. we always have to use corner radius feature.
+          mImpl->mAlwaysUsingCornerRadius = true;
+
+          if(!IsBorderlineRequired())
+          {
+            // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
+            mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+          }
+
+          // Change shader
+          needUpdateShader = true;
+        }
         break;
       }
       case Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY:
@@ -371,6 +414,15 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
             case Toolkit::Visual::Transform::Policy::ABSOLUTE:
             {
               mImpl->mCornerRadiusPolicy = policy;
+              if(DALI_UNLIKELY(mImpl->mRenderer && mImpl->mCornerRadiusIndex != Property::INVALID_INDEX))
+              {
+                // Unusual case. SetProperty called after OnInitialize().
+                // Assume that DoAction call UPDATE_PROPERTY.
+                // We must update properies result into renderer
+                // Note : mImpl->mCornerRadiusIndex is not INVALID_INDEX.
+                // So CornerRadiusPolicy property is already registed.
+                mImpl->mRenderer.SetProperty(mImpl->mRenderer.GetPropertyIndex(CORNER_RADIUS_POLICY), mImpl->mCornerRadiusPolicy);
+              }
               break;
             }
             default:
@@ -386,6 +438,11 @@ void Visual::Base::SetProperties(const Property::Map& propertyMap)
   }
 
   DoSetProperties(propertyMap);
+
+  if(DALI_UNLIKELY(needUpdateShader))
+  {
+    UpdateShader();
+  }
 }
 
 void Visual::Base::SetTransformAndSize(const Property::Map& transform, Size controlSize)
index d04d3df..b7b72e1 100644 (file)
@@ -92,12 +92,12 @@ public:
   void SetTransformAndSize(const Property::Map& transform, Size controlSize);
 
   /**
-   * @brief Performs an action on the visual with the given action name and attributes.
+   * @brief Performs an action on the visual with the given action id and attributes.
    *
-   * @param[in] actionName The name of the action to perform this API only takes an Index
+   * @param[in] actionId The id of the action to perform this API only takes an Index
    * @param[in] attributes The list of attributes for the action. ( optional for this data structure to have content )
    */
-  void DoAction(const Dali::Property::Index actionName, const Dali::Property::Value attributes);
+  void DoAction(const Dali::Property::Index actionId, const Dali::Property::Value attributes);
 
   /**
    * @copydoc Toolkit::Visual::Base::GetHeightForWidth
index 0f08266..12a4324 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 = 1;
+const unsigned int TOOLKIT_MICRO_VERSION = 2;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 0de4006..c007a73 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.1.1
+Version:    2.1.2
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT