Merge "use string_view to avoid string copy" into devel/master
authorDavid Steele <david.steele@samsung.com>
Wed, 16 Dec 2020 18:08:56 +0000 (18:08 +0000)
committerGerrit Code Review <gerrit@review>
Wed, 16 Dec 2020 18:08:56 +0000 (18:08 +0000)
56 files changed:
automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-Control.cpp
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
dali-toolkit/devel-api/controls/control-devel.cpp
dali-toolkit/devel-api/controls/control-devel.h
dali-toolkit/devel-api/text/text-utils-devel.cpp [changed mode: 0644->0755]
dali-toolkit/devel-api/text/text-utils-devel.h [changed mode: 0644->0755]
dali-toolkit/devel-api/visuals/color-visual-properties-devel.h
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/devel-api/visuals/visual-properties-devel.h
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/control/control-data-impl.h
dali-toolkit/internal/file.list
dali-toolkit/internal/text/text-controller-impl-event-handler.cpp
dali-toolkit/internal/text/text-controller-impl-event-handler.h
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-selection-handle-controller.cpp [new file with mode: 0644]
dali-toolkit/internal/text/text-selection-handle-controller.h [new file with mode: 0644]
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
dali-toolkit/internal/visuals/animated-image/animated-image-visual.h
dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h
dali-toolkit/internal/visuals/animated-image/image-cache.h
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/color/color-visual.cpp
dali-toolkit/internal/visuals/color/color-visual.h
dali-toolkit/internal/visuals/gradient/gradient-visual.cpp
dali-toolkit/internal/visuals/gradient/gradient-visual.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/npatch-data.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch-data.h [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch-loader.cpp
dali-toolkit/internal/visuals/npatch-loader.h
dali-toolkit/internal/visuals/npatch/npatch-visual.cpp
dali-toolkit/internal/visuals/npatch/npatch-visual.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h
dali-toolkit/internal/visuals/visual-base-data-impl.cpp
dali-toolkit/internal/visuals/visual-base-data-impl.h
dali-toolkit/internal/visuals/visual-base-impl.cpp
dali-toolkit/internal/visuals/visual-base-impl.h
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h
dali-toolkit/public-api/dali-toolkit-version.cpp
dali-toolkit/public-api/visuals/visual-properties.h
packaging/dali-toolkit.spec

index 6756093..a94f92b 100644 (file)
@@ -76,7 +76,7 @@ static Geometry CreateGeometryMapInternal(const void* opacityMap,
   return Dali::Geometry::New();
 }
 
-static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchLoader::Data* data )
+static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchData* data )
 {
   gCallStack.emplace_back( "BuildNPatch" );
   fprintf(stderr, "AddOn::NPatchBuild()\n");
index a6d827e..90ee00e 100644 (file)
@@ -392,6 +392,77 @@ int UtcDaliAnimatedImageVisualStopBehavior(void)
 }
 
 
+int UtcDaliAnimatedImageVisualStopBehavior02(void)
+{
+  ToolkitTestApplication application;
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+
+  Property::Array urls;
+  CopyUrlsIntoArray( urls );
+
+  {
+    Property::Map propertyMap;
+    propertyMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
+    propertyMap.Insert( ImageVisual::Property::URL, Property::Value(urls) );
+    propertyMap.Insert( DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME);
+    propertyMap.Insert( ImageVisual::Property::BATCH_SIZE, 2);
+    propertyMap.Insert( ImageVisual::Property::CACHE_SIZE, 2);
+    propertyMap.Insert( ImageVisual::Property::FRAME_DELAY, 20);
+
+    VisualFactory factory = VisualFactory::Get();
+    Visual::Base visual = factory.CreateVisual( propertyMap );
+
+    // Expect that a batch of 4 textures has been requested. These will be serially loaded
+    // below.
+
+    DummyControl dummyControl = DummyControl::New(true);
+    Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+    dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    dummyControl.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
+    application.GetScene().Add( dummyControl );
+
+    tet_infoline( "Ready the visual after the visual is on stage" );
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+    TraceCallStack& textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    application.SendNotification();
+    application.Render(20);
+
+    DALI_TEST_EQUALS( gl.GetLastGenTextureId(), 2, TEST_LOCATION );
+
+    Test::EmitGlobalTimerSignal();
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render(20);
+
+    DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 2, TEST_LOCATION );
+
+    DevelControl::DoAction( dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedImageVisual::Action::STOP, Property::Map() );
+
+    tet_infoline( "Ready the visual after the visual is on stage" );
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render(20);
+
+    DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 2, TEST_LOCATION );
+
+    dummyControl.Unparent();
+  }
+  tet_infoline("Test that removing the visual from stage deletes all textures");
+  application.SendNotification();
+  application.Render(16);
+  DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 0, TEST_LOCATION );
+
+  END_TEST;
+}
+
+
 int UtcDaliAnimatedImageVisualAnimatedImage01(void)
 {
   ToolkitTestApplication application;
index dd7827f..534aeb6 100644 (file)
@@ -180,7 +180,8 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
              .Add( "loopCount", 3 )
              .Add( "playRange", playRange )
              .Add( "stopBehavior", DevelImageVisual::StopBehavior::FIRST_FRAME )
-             .Add( "loopingMode", DevelImageVisual::LoopingMode::AUTO_REVERSE );
+             .Add( "loopingMode", DevelImageVisual::LoopingMode::AUTO_REVERSE )
+             .Add( "redrawInScalingDown", false );
 
   Visual::Base visual = VisualFactory::Get().CreateVisual( propertyMap );
   DALI_TEST_CHECK( visual );
@@ -232,6 +233,10 @@ int UtcDaliVisualFactoryGetAnimatedVectorImageVisual04(void)
   DALI_TEST_CHECK( value );
   DALI_TEST_CHECK( value->Get< int >() == DevelImageVisual::LoopingMode::AUTO_REVERSE );
 
+  value = resultMap.Find( DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, Property::BOOLEAN );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get< bool >() == false );
+
   actor.Unparent( );
   DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
 
@@ -308,6 +313,10 @@ int UtcDaliAnimatedVectorImageVisualGetPropertyMap01(void)
   value = resultMap.Find( DevelImageVisual::Property::CONTENT_INFO, Property::MAP );
   DALI_TEST_CHECK( value );
 
+  value = resultMap.Find( DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, Property::BOOLEAN );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get< bool >() == true );    // Check default value
+
   // request AnimatedVectorImageVisual with an URL
   Visual::Base visual2 = factory.CreateVisual( TEST_VECTOR_IMAGE_FILE_NAME, ImageDimensions() );
 
index ddca23f..396bab4 100644 (file)
@@ -1244,28 +1244,3 @@ int UtcDaliControlDoActionWhenNotStage(void)
 
   END_TEST;
 }
-
-int UtcDaliControlStopObservingVisual(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "Test to stop observing a visual when a control is destroyed" );
-
-  Control control = Control::New();
-  control[Actor::Property::SIZE] = Vector2( 200.f, 200.f );
-  control[Control::Property::BACKGROUND] = "invalid.svg";
-
-  application.GetScene().Add( control );
-
-  application.SendNotification();
-  application.Render();
-
-  // Delete control
-  control.Unparent();
-  control.Reset();
-
-  // SVG rasterization may be finished after the control is deleted.
-  // Ensure it doesn't cause a crash.
-  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
-
-  END_TEST;
-}
index fde1197..c135c9f 100644 (file)
@@ -3934,3 +3934,261 @@ int UtcDaliVisualGetType(void)
 
   END_TEST;
 }
+
+int UtcDaliVisualGetVisualProperty01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualGetVisualProperty01: Test animatable property, Visual::Base, ColorVisual" );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
+  propertyMap.Insert(Visual::Property::MIX_COLOR, Color::BLUE);
+  propertyMap.Insert(DevelVisual::Property::CORNER_RADIUS, 10.0f);
+  propertyMap.Insert(DevelVisual::Property::CORNER_RADIUS_POLICY, Toolkit::Visual::Transform::Policy::RELATIVE);
+  propertyMap.Insert(DevelColorVisual::Property::BLUR_RADIUS, 20.0f);
+  Visual::Base colorVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, colorVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 targetColor(1.0f, 1.0f, 1.0f);
+  float targetOpacity = 0.5f;
+  float targetCornerRadius = 20.0f;
+  float targetBlurRadius = 10.0f;
+
+  Animation animation = Animation::New(1.0f);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::MIX_COLOR), targetColor);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), targetOpacity);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), targetCornerRadius);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelColorVisual::Property::BLUR_RADIUS), targetBlurRadius);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render();
+  application.Render(1001u); // End of animation
+
+  Property::Map resultMap;
+  colorVisual.CreatePropertyMap( resultMap );
+
+  // Test property values: they should be updated
+  Property::Value* colorValue = resultMap.Find(ColorVisual::Property::MIX_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(colorValue);
+  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetColor.r, targetColor.g, targetColor.b, targetOpacity), TEST_LOCATION);
+
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get< float >(), targetCornerRadius, TEST_LOCATION);
+
+  Property::Value* blurRadiusValue = resultMap.Find(DevelColorVisual::Property::BLUR_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(blurRadiusValue);
+  DALI_TEST_EQUALS(blurRadiusValue->Get< float >(), targetBlurRadius, TEST_LOCATION);
+
+  // Test uniform values
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", targetColor), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("blurRadius", targetBlurRadius), true, TEST_LOCATION);
+
+  // Test not-supported property
+  Property property1 = DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::PREMULTIPLIED_ALPHA);
+  DALI_TEST_CHECK(!property1.object);
+  DALI_TEST_CHECK(property1.propertyIndex == Property::INVALID_INDEX);
+
+  // Test not-supported property
+  Property property2 = DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelColorVisual::Property::RENDER_IF_TRANSPARENT);
+  DALI_TEST_CHECK(!property2.object);
+  DALI_TEST_CHECK(property2.propertyIndex == Property::INVALID_INDEX);
+
+  // Test unregistered visual
+  Property property3 = DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL2, Visual::Property::MIX_COLOR);
+  DALI_TEST_CHECK(!property3.object);
+  DALI_TEST_CHECK(property3.propertyIndex == Property::INVALID_INDEX);
+
+  // Test after the control is unparented
+  dummyControl.Unparent();
+
+  Property property4 = DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::MIX_COLOR);
+  DALI_TEST_CHECK(!property4.object);
+  DALI_TEST_CHECK(property4.propertyIndex == Property::INVALID_INDEX);
+
+  END_TEST;
+}
+
+int UtcDaliVisualGetVisualProperty02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualGetVisualProperty02: Test animatable property" );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE, Visual::COLOR);
+  Visual::Base colorVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, colorVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 targetColor(1.0f, 1.0f, 1.0f);
+  float targetOpacity = 0.5f;
+  float targetCornerRadius = 20.0f;
+  float targetBlurRadius = 10.0f;
+
+  // Should work when the properties are not set before
+  Animation animation = Animation::New(1.0f);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "mixColor"), targetColor);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "opacity"), targetOpacity);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "cornerRadius"), targetCornerRadius);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "blurRadius"), targetBlurRadius);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render();
+  application.Render(1001u); // End of animation
+
+  Property::Map resultMap;
+  colorVisual.CreatePropertyMap(resultMap);
+
+  // Test property values: they should be updated
+  Property::Value* colorValue = resultMap.Find(ColorVisual::Property::MIX_COLOR, Property::VECTOR4);
+  DALI_TEST_CHECK(colorValue);
+  DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetColor.r, targetColor.g, targetColor.b, targetOpacity), TEST_LOCATION);
+
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get< float >(), targetCornerRadius, TEST_LOCATION);
+
+  Property::Value* blurRadiusValue = resultMap.Find(DevelColorVisual::Property::BLUR_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(blurRadiusValue);
+  DALI_TEST_EQUALS(blurRadiusValue->Get< float >(), targetBlurRadius, TEST_LOCATION);
+
+  // Test uniform values
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", targetColor), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("blurRadius", targetBlurRadius), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualGetVisualProperty03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualGetVisualProperty01: Test animatable property, ImageVisual" );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(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);
+
+  // Wait for image loading
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  float targetOpacity = 0.5f;
+  float targetCornerRadius = 20.0f;
+
+  Animation animation = Animation::New(1.0f);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), targetOpacity);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), targetCornerRadius);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render();
+  application.Render(1001u); // End of animation
+
+  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(1.0f, 1.0f, 1.0f, targetOpacity), TEST_LOCATION);
+
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get< float >(), targetCornerRadius, TEST_LOCATION);
+
+  // Test uniform value
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVisualGetVisualProperty04(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualGetVisualProperty01: Test animatable property, GradientVisual" );
+
+  Vector2 start(-1.f, -1.f);
+  Vector2 end(1.f, 1.f);
+  Property::Array stopColors;
+  stopColors.PushBack( Color::RED );
+  stopColors.PushBack( Color::GREEN );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE,  Visual::GRADIENT);
+  propertyMap.Insert(GradientVisual::Property::START_POSITION, start);
+  propertyMap.Insert(GradientVisual::Property::END_POSITION, end);
+  propertyMap.Insert(GradientVisual::Property::STOP_OFFSET, Vector2(0.f, 1.f));
+  propertyMap.Insert(GradientVisual::Property::SPREAD_METHOD, GradientVisual::SpreadMethod::REPEAT);
+  propertyMap.Insert(GradientVisual::Property::STOP_COLOR, stopColors);
+  Visual::Base gradientVisual = factory.CreateVisual(propertyMap);
+
+  DummyControl dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, gradientVisual);
+  dummyControl[Actor::Property::SIZE] = Vector2(200.f, 200.f);
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+
+  float targetOpacity = 0.5f;
+  float targetCornerRadius = 20.0f;
+
+  Animation animation = Animation::New(1.0f);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), targetOpacity);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), targetCornerRadius);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render();
+  application.Render(1001u); // End of animation
+
+  Property::Map resultMap;
+  gradientVisual.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(1.0f, 1.0f, 1.0f, targetOpacity), TEST_LOCATION);
+
+  Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::FLOAT);
+  DALI_TEST_CHECK(cornerRadiusValue);
+  DALI_TEST_EQUALS(cornerRadiusValue->Get< float >(), targetCornerRadius, TEST_LOCATION);
+
+  // Test uniform value
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
+
+  END_TEST;
+}
index db708c1..929825c 100644 (file)
@@ -771,9 +771,6 @@ int UtcDaliVisualFactoryGetNPatchVisual3(void)
 
     DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
 
-    application.GetScene().Remove( actor );
-    DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
-
     Vector2 naturalSize( 0.0f, 0.0f );
     visual.GetNaturalSize( naturalSize );
     DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth() - 2.0f, imageSize.GetHeight() - 2.0f ), TEST_LOCATION );
@@ -984,6 +981,79 @@ int UtcDaliVisualFactoryGetNPatchVisual7(void)
   END_TEST;
 }
 
+int UtcDaliVisualFactoryGetNPatchVisual8(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual8: Add 9-patch visual on stage, instantly remove it and add new 9-patch visual with same propertyMap" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  // Get actual size of test image
+  ImageDimensions imageSize = Dali::GetClosestImageSize( TEST_9_PATCH_FILE_NAME );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Toolkit::Visual::Property::TYPE, Visual::N_PATCH );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_9_PATCH_FILE_NAME );
+  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, false );
+  {
+    Visual::Base visual = factory.CreateVisual( propertyMap );
+    DALI_TEST_CHECK( visual );
+
+    Vector2 naturalSize( 0.0f, 0.0f );
+    visual.GetNaturalSize( naturalSize );
+    DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth(), imageSize.GetHeight() ), TEST_LOCATION );
+
+    TestGlAbstraction& gl = application.GetGlAbstraction();
+    TraceCallStack& textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+
+    DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+    DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+    application.GetScene().Add( actor );
+    actor.Unparent();
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    visual = factory.CreateVisual( propertyMap );
+    DALI_TEST_CHECK( visual );
+
+    visual.GetNaturalSize( naturalSize );
+    DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth(), imageSize.GetHeight() ), TEST_LOCATION );
+
+    actor = DummyControl::New(true);
+
+    DummyControlImpl& dummyImpl2 = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl2.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+    DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+    application.GetScene().Add( actor );
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger(1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    Renderer renderer = actor.GetRendererAt( 0 );
+    auto textures = renderer.GetTextures();
+
+    DALI_TEST_EQUALS( textures.GetTextureCount(), 1, TEST_LOCATION );
+  }
+
+  END_TEST;
+}
+
 int UtcDaliNPatchVisualAuxiliaryImage01(void)
 {
   ToolkitTestApplication application;
@@ -1061,7 +1131,7 @@ int UtcDaliNPatchVisualAuxiliaryImage02(void)
 
   Renderer renderer2 = imageView2.GetRendererAt( 0 );
   auto textureSet2 = renderer2.GetTextures();
-  DALI_TEST_EQUALS( textureSet1 == textureSet2, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureSet1 != textureSet2, true, TEST_LOCATION );
 
   END_TEST;
 }
index a855b2d..0670e68 100644 (file)
@@ -115,6 +115,13 @@ VisualEventSignalType& VisualEventSignal(Control control)
   return controlDataImpl.VisualEventSignal();
 }
 
+Dali::Property GetVisualProperty(Control control, Dali::Property::Index index, Dali::Property::Key visualPropertyKey)
+{
+  Internal::Control&       internalControl = Toolkit::Internal::GetImplementation(control);
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(internalControl);
+  return controlDataImpl.GetVisualProperty(index, visualPropertyKey);
+}
+
 static Toolkit::Internal::Control::Impl *GetControlImplementationIfAny( Dali::Actor actor)
 {
   Dali::Toolkit::Control c = Toolkit::Control::DownCast( actor );
index 5753a69..af6c76c 100644 (file)
@@ -358,6 +358,18 @@ using VisualEventSignalType = Signal<void(Control, Dali::Property::Index, Dali::
 DALI_TOOLKIT_API VisualEventSignalType& VisualEventSignal(Control control);
 
 /**
+ * @brief Retrieve the property object associated with the given property index and the visual property key.
+ *
+ * @param[in] control The control
+ * @param[in] index The Property index of the visual.
+ * @param[in] visualPropertyKey The key of the visual's property.
+ * @return The Property object
+ * @pre The control should be added to the Scene.
+ * @pre The returned object is valid for as long as the control is on the Scene.
+ */
+DALI_TOOLKIT_API Dali::Property GetVisualProperty(Control control, Dali::Property::Index index, Dali::Property::Key visualPropertyKey);
+
+/**
  * @brief The signal is emmited as a succession of "activate" signal send by accessibility client.
  * @return The signal to connect to
  */
old mode 100644 (file)
new mode 100755 (executable)
index 97a1b57..e5ad925
@@ -1012,9 +1012,14 @@ Size LayoutText(const RendererParameters& textParameters, TextAbstraction::TextR
   const unsigned int radius = textParameters.radius - static_cast<unsigned int>(maxAscenderDescender);
 
   // Set the layout parameters.
-  internalDataModel.textLayoutArea = Size(static_cast<float>(textParameters.textWidth),
+  Size textLayoutArea = Size(static_cast<float>(textParameters.textWidth),
                                           static_cast<float>(textParameters.textHeight));
 
+  // padding
+  Extents padding = textParameters.padding;
+  internalDataModel.textLayoutArea = Size(textLayoutArea.x - ( padding.start + padding.end ), textLayoutArea.y - ( padding.top + padding.bottom ) );
+
+
   if(isCircularTextLayout)
   {
     // In a circular layout, the length of the text area depends on the radius.
@@ -1498,7 +1503,7 @@ Dali::Property::Array RenderForLastIndex(RendererParameters& textParameters)
   ////////////////////////////////////////////////////////////////////////////////
   // Layout the text
   ////////////////////////////////////////////////////////////////////////////////
-  int boundingBox           = textParameters.textHeight;
+  int boundingBox           = textParameters.textHeight - (textParameters.padding.top + textParameters.padding.bottom);
   textParameters.textHeight = MAX_INT; // layout for the entire area.
   LayoutText(textParameters, rendererParameters, embeddedItemLayout, internalData);
 
old mode 100644 (file)
new mode 100755 (executable)
index 6b63039..e4bf375
@@ -55,7 +55,8 @@ struct DALI_TOOLKIT_API RendererParameters
     ellipsisEnabled{true},
     markupEnabled{false},
     isTextColorSet{false},
-    minLineSize{0.f}
+    minLineSize{0.f},
+    padding{0u, 0u, 0u, 0u}
   {
   }
 
@@ -87,6 +88,8 @@ struct DALI_TOOLKIT_API RendererParameters
   bool isTextColorSet : 1;         ///< Whether a default color has been set.
                                    //
   float minLineSize;               ///< The line's minimum size (in points).
+
+  Extents padding;                 ///< The padding of the boundaries where the text is going to be laid-out.
 };
 
 /**
index d325a9c..448bf88 100644 (file)
@@ -49,7 +49,7 @@ enum
 
   /**
    * @brief The blur radius of the visual.
-   * @details Name "blurRadius", type Property::FLOAT.
+   * @details Name "blurRadius", type Property::FLOAT, animatable.
    *          If the value is 0, the edge is sharp. Otherwise, the larger the value, the more the edge is blurred.
    * @note Optional.
    * @note The default is 0.
index 63efcb3..a10a3f8 100644 (file)
@@ -138,7 +138,14 @@ enum Type
    * And the array contains 2 integer values which are the frame numbers, the start frame number and the end frame number of the layer.
    * @note This property is read-only.
    */
-  CONTENT_INFO = ORIENTATION_CORRECTION + 10
+  CONTENT_INFO = ORIENTATION_CORRECTION + 10,
+
+  /**
+   * @brief Whether to redraw the image when the visual is scaled down.
+   * @details Name "redrawInScalingDown", type Property::BOOLEAN.
+   * @note It is used in the AnimatedVectorImageVisual. The default is true.
+   */
+  REDRAW_IN_SCALING_DOWN
 };
 
 } //namespace Property
index 99e3837..6a5f51d 100644 (file)
@@ -73,7 +73,7 @@ enum Type
 
   /**
    * @brief The radius for the rounded corners of the visual
-   * @details Name "cornerRadius", type Property::FLOAT.
+   * @details Name "cornerRadius", type Property::FLOAT, animatable
    * @note Optional.
    */
   CORNER_RADIUS = OPACITY + 2,
index 4e7dff9..4e12efb 100755 (executable)
@@ -104,25 +104,30 @@ void Remove( DictionaryKeys& keys, const std::string& name )
   }
 }
 
-Toolkit::Visual::Type GetVisualTypeFromMap( const Property::Map& map )
+/**
+ *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
+ */
+bool FindVisual( Property::Index targetIndex, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
 {
-  Property::Value* typeValue = map.Find( Toolkit::Visual::Property::TYPE, VISUAL_TYPE  );
-  Toolkit::Visual::Type type = Toolkit::Visual::IMAGE;
-  if( typeValue )
+  for ( iter = visuals.Begin(); iter != visuals.End(); iter++ )
   {
-    Scripting::GetEnumerationProperty( *typeValue, VISUAL_TYPE_TABLE, VISUAL_TYPE_TABLE_COUNT, type );
+    if ( (*iter)->index ==  targetIndex )
+    {
+      return true;
+    }
   }
-  return type;
+  return false;
 }
 
 /**
  *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
  */
-bool FindVisual( Property::Index targetIndex, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
+bool FindVisual( std::string visualName, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
 {
   for ( iter = visuals.Begin(); iter != visuals.End(); iter++ )
   {
-    if ( (*iter)->index ==  targetIndex )
+    Toolkit::Visual::Base visual = (*iter)->visual;
+    if( visual && visual.GetName() == visualName )
     {
       return true;
     }
@@ -169,6 +174,24 @@ Toolkit::Visual::Base GetVisualByName(
   return visualHandle;
 }
 
+Toolkit::Visual::Base GetVisualByIndex(
+  const RegisteredVisualContainer& visuals,
+  Property::Index                  index)
+{
+  Toolkit::Visual::Base visualHandle;
+
+  RegisteredVisualContainer::Iterator iter;
+  for(iter = visuals.Begin(); iter != visuals.End(); iter++)
+  {
+    if((*iter)->index == index)
+    {
+      visualHandle = (*iter)->visual;
+      break;
+    }
+  }
+  return visualHandle;
+}
+
 /**
  * Move visual from source to destination container
  */
@@ -1540,59 +1563,47 @@ void Control::Impl::RecreateChangedVisuals( Dictionary<Property::Map>& stateVisu
     const std::string& visualName = (*iter).key;
     const Property::Map& toMap = (*iter).entry;
 
-    // is it a candidate for re-creation?
-    bool recreate = false;
-
-    Toolkit::Visual::Base visual = GetVisualByName( mVisuals, visualName );
-    if( visual )
+    Actor self = mControlImpl.Self();
+    RegisteredVisualContainer::Iterator registeredVisualsiter;
+    // Check if visual (visualName) is already registered, this is the current visual.
+    if(FindVisual(visualName, mVisuals, registeredVisualsiter))
     {
-      Property::Map fromMap;
-      visual.CreatePropertyMap( fromMap );
-
-      Toolkit::Visual::Type fromType = GetVisualTypeFromMap( fromMap );
-      Toolkit::Visual::Type toType = GetVisualTypeFromMap( toMap );
-
-      if( fromType != toType )
+      Toolkit::Visual::Base& visual = (*registeredVisualsiter)->visual;
+      if(visual)
       {
-        recreate = true;
-      }
-      else
-      {
-        if( fromType == Toolkit::Visual::IMAGE || fromType == Toolkit::Visual::N_PATCH
-            || fromType == Toolkit::Visual::SVG || fromType == Toolkit::Visual::ANIMATED_IMAGE )
-        {
-          Property::Value* fromUrl = fromMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
-          Property::Value* toUrl = toMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
+        // No longer required to know if the replaced visual's resources are ready
+        StopObservingVisual(visual);
 
-          if( fromUrl && toUrl )
+        // If control staged then visuals will be swapped once ready
+        if(self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+        {
+          // Check if visual is currently in the process of being replaced ( is in removal container )
+          RegisteredVisualContainer::Iterator visualQueuedForRemoval;
+          if(FindVisual(visualName, mRemoveVisuals, visualQueuedForRemoval))
           {
-            std::string fromUrlString;
-            std::string toUrlString;
-            fromUrl->Get(fromUrlString);
-            toUrl->Get(toUrlString);
-
-            if( fromUrlString != toUrlString )
-            {
-              recreate = true;
-            }
+            // Visual with same visual name is already in removal container so current visual pending
+            // Only the the last requested visual will be displayed so remove current visual which is staged but not ready.
+            Toolkit::GetImplementation(visual).SetOffScene(self);
+            (*registeredVisualsiter)->visual.Reset();
+            mVisuals.Erase(registeredVisualsiter);
+          }
+          else
+          {
+            // current visual not already in removal container so add now.
+            DALI_LOG_INFO(gLogFilter, Debug::Verbose, "RegisterVisual Move current registered visual to removal Queue: %s \n", visualName.c_str());
+            MoveVisual(registeredVisualsiter, mVisuals, mRemoveVisuals);
           }
         }
+        else
+        {
+          // Control not staged or visual disabled so can just erase from registered visuals and new visual will be added later.
+          (*registeredVisualsiter)->visual.Reset();
+          mVisuals.Erase(registeredVisualsiter);
+        }
       }
 
-      const Property::Map* instancedMap = instancedProperties.FindConst( visualName );
-      if( recreate || instancedMap )
-      {
-        RemoveVisual( mVisuals, visualName );
-        Style::ApplyVisual( handle, visualName, toMap, instancedMap );
-      }
-      else
-      {
-        // @todo check to see if we can apply toMap without recreating the visual
-        // e.g. by setting only animatable properties
-        // For now, recreate all visuals, but merge in instance data.
-        RemoveVisual( mVisuals, visualName );
-        Style::ApplyVisual( handle, visualName, toMap, instancedMap );
-      }
+      const Property::Map* instancedMap = instancedProperties.FindConst(visualName);
+      Style::ApplyVisual(handle, visualName, toMap, instancedMap);
     }
   }
 }
@@ -1820,6 +1831,19 @@ void Control::Impl::ClearShadow()
    mControlImpl.RelayoutRequest();
 }
 
+Dali::Property Control::Impl::GetVisualProperty(Dali::Property::Index index, Dali::Property::Key visualPropertyKey)
+{
+  Toolkit::Visual::Base visual = GetVisualByIndex(mVisuals, index);
+  if(visual)
+  {
+    Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
+    return visualImpl.GetPropertyObject(visualPropertyKey);
+  }
+
+  Handle handle;
+  return Dali::Property(handle, Property::INVALID_INDEX);
+}
+
 void Control::Impl::EmitResourceReadySignal()
 {
   if(!mIsEmittingResourceReadySignal)
index aefb484..aa16060 100755 (executable)
@@ -391,6 +391,11 @@ public:
    */
   void ClearShadow();
 
+  /**
+   * @copydoc DevelControl::GetVisualProperty()
+   */
+  Dali::Property GetVisualProperty(Dali::Property::Index index, Dali::Property::Key visualPropertyKey);
+
 private:
 
   /**
index 0081833..8cc93bc 100644 (file)
@@ -34,6 +34,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/visuals/image-atlas-manager.cpp
    ${toolkit_src_dir}/visuals/image/image-visual.cpp
    ${toolkit_src_dir}/visuals/mesh/mesh-visual.cpp
+   ${toolkit_src_dir}/visuals/npatch-data.cpp
    ${toolkit_src_dir}/visuals/npatch-loader.cpp
    ${toolkit_src_dir}/visuals/npatch/npatch-visual.cpp
    ${toolkit_src_dir}/visuals/primitive/primitive-visual.cpp
@@ -152,6 +153,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/text/text-io.cpp
    ${toolkit_src_dir}/text/text-model.cpp
    ${toolkit_src_dir}/text/text-scroller.cpp
+   ${toolkit_src_dir}/text/text-selection-handle-controller.cpp
    ${toolkit_src_dir}/text/text-vertical-scroller.cpp
    ${toolkit_src_dir}/text/text-view.cpp
    ${toolkit_src_dir}/text/text-view-interface.cpp
index 7b50a5c..475f3fc 100644 (file)
@@ -24,6 +24,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/text-editable-control-interface.h>
 
 using namespace Dali;
 
@@ -45,6 +46,225 @@ namespace Toolkit
 namespace Text
 {
 
+bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
+{
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
+
+  EventData*& eventData = impl.mEventData;
+  if( NULL == eventData )
+  {
+    // Nothing to do if there is no text input.
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
+    return false;
+  }
+
+  if( eventData->mDecorator )
+  {
+    for( std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
+         iter != eventData->mEventQueue.end();
+         ++iter )
+    {
+      switch( iter->type )
+      {
+        case Event::CURSOR_KEY_EVENT:
+        {
+          OnCursorKeyEvent(impl, *iter);
+          break;
+        }
+        case Event::TAP_EVENT:
+        {
+          OnTapEvent(impl, *iter);
+          break;
+        }
+        case Event::LONG_PRESS_EVENT:
+        {
+          OnLongPressEvent(impl, *iter);
+          break;
+        }
+        case Event::PAN_EVENT:
+        {
+          OnPanEvent(impl, *iter);
+          break;
+        }
+        case Event::GRAB_HANDLE_EVENT:
+        case Event::LEFT_SELECTION_HANDLE_EVENT:
+        case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
+        {
+          OnHandleEvent(impl, *iter);
+          break;
+        }
+        case Event::SELECT:
+        {
+          OnSelectEvent(impl, *iter);
+          break;
+        }
+        case Event::SELECT_ALL:
+        {
+          OnSelectAllEvent(impl);
+          break;
+        }
+        case Event::SELECT_NONE:
+        {
+          OnSelectNoneEvent(impl);
+          break;
+        }
+      }
+    }
+  }
+
+  if( eventData->mUpdateCursorPosition ||
+      eventData->mUpdateHighlightBox )
+  {
+    impl.NotifyInputMethodContext();
+  }
+
+  // The cursor must also be repositioned after inserts into the model
+  if( eventData->mUpdateCursorPosition )
+  {
+    // Updates the cursor position and scrolls the text to make it visible.
+    CursorInfo cursorInfo;
+    // Calculate the cursor position from the new cursor index.
+    impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
+
+    if(nullptr != impl.mEditableControlInterface)
+    {
+      impl.mEditableControlInterface->CaretMoved( eventData->mPrimaryCursorPosition );
+    }
+
+    if( eventData->mUpdateCursorHookPosition )
+    {
+      // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
+      eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
+      eventData->mUpdateCursorHookPosition = false;
+    }
+
+    // Scroll first the text after delete ...
+    if( eventData->mScrollAfterDelete )
+    {
+      impl.ScrollTextToMatchCursor(cursorInfo);
+    }
+
+    // ... then, text can be scrolled to make the cursor visible.
+    if( eventData->mScrollAfterUpdatePosition )
+    {
+      const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
+      impl.ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
+    }
+    eventData->mScrollAfterUpdatePosition = false;
+    eventData->mScrollAfterDelete = false;
+
+    impl.UpdateCursorPosition( cursorInfo );
+
+    eventData->mDecoratorUpdated = true;
+    eventData->mUpdateCursorPosition = false;
+    eventData->mUpdateGrabHandlePosition = false;
+  }
+  else
+  {
+    CursorInfo leftHandleInfo;
+    CursorInfo rightHandleInfo;
+
+    if( eventData->mUpdateHighlightBox )
+    {
+      impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
+
+      impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
+
+      if( eventData->mScrollAfterUpdatePosition && ( eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition ) )
+      {
+        if( eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected )
+        {
+          CursorInfo& infoLeft = leftHandleInfo;
+
+          const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
+          impl.ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
+
+          CursorInfo& infoRight = rightHandleInfo;
+
+          const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
+          impl.ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
+        }
+        else
+        {
+          CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
+
+          const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
+          impl. ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
+        }
+      }
+    }
+
+    if( eventData->mUpdateLeftSelectionPosition )
+    {
+      impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
+
+      impl.SetPopupButtons();
+      eventData->mDecoratorUpdated = true;
+      eventData->mUpdateLeftSelectionPosition = false;
+    }
+
+    if( eventData->mUpdateRightSelectionPosition )
+    {
+      impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
+
+      impl.SetPopupButtons();
+      eventData->mDecoratorUpdated = true;
+      eventData->mUpdateRightSelectionPosition = false;
+    }
+
+    if( eventData->mUpdateHighlightBox )
+    {
+      impl.RepositionSelectionHandles();
+
+      eventData->mUpdateLeftSelectionPosition = false;
+      eventData->mUpdateRightSelectionPosition = false;
+      eventData->mUpdateHighlightBox = false;
+      eventData->mIsLeftHandleSelected = false;
+      eventData->mIsRightHandleSelected = false;
+    }
+
+    eventData->mScrollAfterUpdatePosition = false;
+  }
+
+  if( eventData->mUpdateInputStyle )
+  {
+    // Keep a copy of the current input style.
+    InputStyle currentInputStyle;
+    currentInputStyle.Copy( eventData->mInputStyle );
+
+    // Set the default style first.
+    impl.RetrieveDefaultInputStyle( eventData->mInputStyle );
+
+    // Get the character index from the cursor index.
+    const CharacterIndex styleIndex = ( eventData->mPrimaryCursorPosition > 0u ) ? eventData->mPrimaryCursorPosition - 1u : 0u;
+
+    // Retrieve the style from the style runs stored in the logical model.
+    impl.mModel->mLogicalModel->RetrieveStyle( styleIndex, eventData->mInputStyle );
+
+    // Compare if the input style has changed.
+    const bool hasInputStyleChanged = !currentInputStyle.Equal( eventData->mInputStyle );
+
+    if( hasInputStyleChanged )
+    {
+      const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( eventData->mInputStyle );
+      // Queue the input style changed signal.
+      eventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
+    }
+
+    eventData->mUpdateInputStyle = false;
+  }
+
+  eventData->mEventQueue.clear();
+
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
+
+  const bool decoratorUpdated = eventData->mDecoratorUpdated;
+  eventData->mDecoratorUpdated = false;
+
+  return decoratorUpdated;
+}
+
+
 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
 {
   if( NULL == impl.mEventData || !impl.IsShowingRealText() )
index 52ac94a..7399fdd 100644 (file)
@@ -36,6 +36,14 @@ namespace Text
 struct ControllerImplEventHandler
 {
   /**
+   * @brief Processes input events
+   *
+   * @param[in] impl A reference to Controller::Impl
+   * @return True if the decorator has been updated
+   */
+  static bool ProcessInputEvents(Controller::Impl& impl);
+
+  /**
    * @brief Called by Controller::Impl when a cursor key event is received.
    *
    * @param controllerImpl A reference to Controller::Impl
index d5edfc3..6554a2c 100644 (file)
@@ -21,7 +21,6 @@
 // EXTERNAL INCLUDES
 #include <dali/public-api/rendering/renderer.h>
 #include <dali/integration-api/debug.h>
-#include <limits>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
 #include <dali-toolkit/internal/text/text-control-interface.h>
 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
 #include <dali-toolkit/internal/text/text-run-container.h>
-#include <dali-toolkit/internal/text/text-editable-control-interface.h>
+#include <dali-toolkit/internal/text/text-selection-handle-controller.h>
 
 using namespace Dali;
 
 namespace
 {
 
-/**
- * @brief Struct used to calculate the selection box.
- */
-struct SelectionBoxInfo
-{
-  float lineOffset;
-  float lineHeight;
-  float minX;
-  float maxX;
-};
-
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
-const float MAX_FLOAT = std::numeric_limits<float>::max();
-const float MIN_FLOAT = std::numeric_limits<float>::min();
-const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
-
 #define MAKE_SHADER(A)#A
 
 const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
@@ -165,228 +149,9 @@ EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodCon
 {
 }
 
-EventData::~EventData()
-{}
-
 bool Controller::Impl::ProcessInputEvents()
 {
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
-    return false;
-  }
-
-  if( mEventData->mDecorator )
-  {
-    for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
-         iter != mEventData->mEventQueue.end();
-         ++iter )
-    {
-      switch( iter->type )
-      {
-        case Event::CURSOR_KEY_EVENT:
-        {
-          OnCursorKeyEvent( *iter );
-          break;
-        }
-        case Event::TAP_EVENT:
-        {
-          OnTapEvent( *iter );
-          break;
-        }
-        case Event::LONG_PRESS_EVENT:
-        {
-          OnLongPressEvent( *iter );
-          break;
-        }
-        case Event::PAN_EVENT:
-        {
-          OnPanEvent( *iter );
-          break;
-        }
-        case Event::GRAB_HANDLE_EVENT:
-        case Event::LEFT_SELECTION_HANDLE_EVENT:
-        case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
-        {
-          OnHandleEvent( *iter );
-          break;
-        }
-        case Event::SELECT:
-        {
-          OnSelectEvent( *iter );
-          break;
-        }
-        case Event::SELECT_ALL:
-        {
-          OnSelectAllEvent();
-          break;
-        }
-        case Event::SELECT_NONE:
-        {
-          OnSelectNoneEvent();
-          break;
-        }
-      }
-    }
-  }
-
-  if( mEventData->mUpdateCursorPosition ||
-      mEventData->mUpdateHighlightBox )
-  {
-    NotifyInputMethodContext();
-  }
-
-  // The cursor must also be repositioned after inserts into the model
-  if( mEventData->mUpdateCursorPosition )
-  {
-    // Updates the cursor position and scrolls the text to make it visible.
-    CursorInfo cursorInfo;
-    // Calculate the cursor position from the new cursor index.
-    GetCursorPosition( mEventData->mPrimaryCursorPosition,
-                       cursorInfo );
-
-    if( NULL != mEditableControlInterface )
-    {
-      mEditableControlInterface->CaretMoved( mEventData->mPrimaryCursorPosition );
-    }
-
-    if( mEventData->mUpdateCursorHookPosition )
-    {
-      // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
-      mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
-      mEventData->mUpdateCursorHookPosition = false;
-    }
-
-    // Scroll first the text after delete ...
-    if( mEventData->mScrollAfterDelete )
-    {
-      ScrollTextToMatchCursor( cursorInfo );
-    }
-
-    // ... then, text can be scrolled to make the cursor visible.
-    if( mEventData->mScrollAfterUpdatePosition )
-    {
-      const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
-      ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
-    }
-    mEventData->mScrollAfterUpdatePosition = false;
-    mEventData->mScrollAfterDelete = false;
-
-    UpdateCursorPosition( cursorInfo );
-
-    mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateCursorPosition = false;
-    mEventData->mUpdateGrabHandlePosition = false;
-  }
-  else
-  {
-    CursorInfo leftHandleInfo;
-    CursorInfo rightHandleInfo;
-
-    if( mEventData->mUpdateHighlightBox )
-    {
-      GetCursorPosition( mEventData->mLeftSelectionPosition,
-                         leftHandleInfo );
-
-      GetCursorPosition( mEventData->mRightSelectionPosition,
-                         rightHandleInfo );
-
-      if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
-      {
-        if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected )
-        {
-          CursorInfo& infoLeft = leftHandleInfo;
-
-          const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset );
-          ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight );
-
-          CursorInfo& infoRight = rightHandleInfo;
-
-          const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset );
-          ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight );
-        }
-        else
-        {
-          CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
-
-          const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
-          ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
-        }
-      }
-    }
-
-    if( mEventData->mUpdateLeftSelectionPosition )
-    {
-      UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
-                             leftHandleInfo );
-
-      SetPopupButtons();
-      mEventData->mDecoratorUpdated = true;
-      mEventData->mUpdateLeftSelectionPosition = false;
-    }
-
-    if( mEventData->mUpdateRightSelectionPosition )
-    {
-      UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
-                             rightHandleInfo );
-
-      SetPopupButtons();
-      mEventData->mDecoratorUpdated = true;
-      mEventData->mUpdateRightSelectionPosition = false;
-    }
-
-    if( mEventData->mUpdateHighlightBox )
-    {
-      RepositionSelectionHandles();
-
-      mEventData->mUpdateLeftSelectionPosition = false;
-      mEventData->mUpdateRightSelectionPosition = false;
-      mEventData->mUpdateHighlightBox = false;
-      mEventData->mIsLeftHandleSelected = false;
-      mEventData->mIsRightHandleSelected = false;
-    }
-
-    mEventData->mScrollAfterUpdatePosition = false;
-  }
-
-  if( mEventData->mUpdateInputStyle )
-  {
-    // Keep a copy of the current input style.
-    InputStyle currentInputStyle;
-    currentInputStyle.Copy( mEventData->mInputStyle );
-
-    // Set the default style first.
-    RetrieveDefaultInputStyle( mEventData->mInputStyle );
-
-    // Get the character index from the cursor index.
-    const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
-
-    // Retrieve the style from the style runs stored in the logical model.
-    mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
-
-    // Compare if the input style has changed.
-    const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
-
-    if( hasInputStyleChanged )
-    {
-      const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
-      // Queue the input style changed signal.
-      mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
-    }
-
-    mEventData->mUpdateInputStyle = false;
-  }
-
-  mEventData->mEventQueue.clear();
-
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
-
-  const bool decoratorUpdated = mEventData->mDecoratorUpdated;
-  mEventData->mDecoratorUpdated = false;
-
-  return decoratorUpdated;
+  return ControllerImplEventHandler::ProcessInputEvents(*this);
 }
 
 void Controller::Impl::NotifyInputMethodContext()
@@ -1345,46 +1110,6 @@ float Controller::Impl::GetDefaultFontLineHeight()
   return( fontMetrics.ascender - fontMetrics.descender );
 }
 
-void Controller::Impl::OnCursorKeyEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
-}
-
-void Controller::Impl::OnTapEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnTapEvent(*this, event);
-}
-
-void Controller::Impl::OnPanEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnPanEvent(*this, event);
-}
-
-void Controller::Impl::OnLongPressEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnLongPressEvent(*this, event);
-}
-
-void Controller::Impl::OnHandleEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnHandleEvent(*this, event);
-}
-
-void Controller::Impl::OnSelectEvent( const Event& event )
-{
-  ControllerImplEventHandler::OnSelectEvent(*this, event);
-}
-
-void Controller::Impl::OnSelectAllEvent()
-{
-  ControllerImplEventHandler::OnSelectAllEvent(*this);
-}
-
-void Controller::Impl::OnSelectNoneEvent()
-{
-  ControllerImplEventHandler::OnSelectNoneEvent(*this);
-}
-
 void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
 {
   if( nullptr == mEventData )
@@ -1614,477 +1339,11 @@ void Controller::Impl::RequestGetTextFromClipboard()
 
 void Controller::Impl::RepositionSelectionHandles()
 {
-  CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
-  CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
-
-  if( selectionStart == selectionEnd )
-  {
-    // Nothing to select if handles are in the same place.
-    // So, deactive Highlight box.
-    mEventData->mDecorator->SetHighlightActive( false );
-    return;
-  }
-
-  mEventData->mDecorator->ClearHighlights();
-
-  const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
-  const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
-  const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
-  const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
-  const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
-  const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
-  const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
-
-  const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
-  const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
-  const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
-
-  // Swap the indices if the start is greater than the end.
-  const bool indicesSwapped = selectionStart > selectionEnd;
-
-  // Tell the decorator to flip the selection handles if needed.
-  mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
-
-  if( indicesSwapped )
-  {
-    std::swap( selectionStart, selectionEnd );
-  }
-
-  // Get the indices to the first and last selected glyphs.
-  const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
-  const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
-  const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
-  const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
-
-  // Get the lines where the glyphs are laid-out.
-  const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
-
-  LineIndex lineIndex = 0u;
-  Length numberOfLines = 0u;
-  mModel->mVisualModel->GetNumberOfLines( glyphStart,
-                                          1u + glyphEnd - glyphStart,
-                                          lineIndex,
-                                          numberOfLines );
-  const LineIndex firstLineIndex = lineIndex;
-
-  // Create the structure to store some selection box info.
-  Vector<SelectionBoxInfo> selectionBoxLinesInfo;
-  selectionBoxLinesInfo.Resize( numberOfLines );
-
-  SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
-  selectionBoxInfo->minX = MAX_FLOAT;
-  selectionBoxInfo->maxX = MIN_FLOAT;
-
-  // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
-  float minHighlightX = std::numeric_limits<float>::max();
-  float maxHighlightX = std::numeric_limits<float>::min();
-  Size highLightSize;
-  Vector2 highLightPosition; // The highlight position in decorator's coords.
-
-  // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
-
-  // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
-  selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
-                                                      firstLineIndex );
-
-  // Transform to decorator's (control) coords.
-  selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
-
-  lineRun += firstLineIndex;
-
-  // The line height is the addition of the line ascender and the line descender.
-  // However, the line descender has a negative value, hence the subtraction.
-  selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
-
-  GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
-
-  // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
-  const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
-  bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
-
-  // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
-  const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
-  bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
-
-  // The number of quads of the selection box.
-  const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
-  mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
-
-  // Count the actual number of quads.
-  unsigned int actualNumberOfQuads = 0u;
-  Vector4 quad;
-
-  // Traverse the glyphs.
-  for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
-  {
-    const GlyphInfo& glyph = *( glyphsBuffer + index );
-    const Vector2& position = *( positionsBuffer + index );
-
-    if( splitStartGlyph )
-    {
-      // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
-
-      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
-      const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
-      // Get the direction of the character.
-      CharacterDirection isCurrentRightToLeft = false;
-      if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
-      {
-        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
-      }
-
-      // The end point could be in the middle of the ligature.
-      // Calculate the number of characters selected.
-      const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
-
-      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
-      quad.y = selectionBoxInfo->lineOffset;
-      quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
-      quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
-
-      // Store the min and max 'x' for each line.
-      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
-      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
-
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
-      ++actualNumberOfQuads;
-
-      splitStartGlyph = false;
-      continue;
-    }
-
-    if( splitEndGlyph && ( index == glyphEnd ) )
-    {
-      // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
-
-      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
-      const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
-      // Get the direction of the character.
-      CharacterDirection isCurrentRightToLeft = false;
-      if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
-      {
-        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
-      }
-
-      const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
-
-      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
-      quad.y = selectionBoxInfo->lineOffset;
-      quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
-      quad.w = quad.y + selectionBoxInfo->lineHeight;
-
-      // Store the min and max 'x' for each line.
-      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
-      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
-
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      splitEndGlyph = false;
-      continue;
-    }
-
-    quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
-    quad.y = selectionBoxInfo->lineOffset;
-    quad.z = quad.x + glyph.advance;
-    quad.w = quad.y + selectionBoxInfo->lineHeight;
-
-    // Store the min and max 'x' for each line.
-    selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
-    selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
-
-    mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                          quad );
-    ++actualNumberOfQuads;
-
-    // Whether to retrieve the next line.
-    if( index == lastGlyphOfLine )
-    {
-      ++lineIndex;
-      if( lineIndex < firstLineIndex + numberOfLines )
-      {
-        // Retrieve the next line.
-        ++lineRun;
-
-        // Get the last glyph of the new line.
-        lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
-
-        // Keep the offset and height of the current selection box.
-        const float currentLineOffset = selectionBoxInfo->lineOffset;
-        const float currentLineHeight = selectionBoxInfo->lineHeight;
-
-        // Get the selection box info for the next line.
-        ++selectionBoxInfo;
-
-        selectionBoxInfo->minX = MAX_FLOAT;
-        selectionBoxInfo->maxX = MIN_FLOAT;
-
-        // Update the line's vertical offset.
-        selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
-
-        // The line height is the addition of the line ascender and the line descender.
-        // However, the line descender has a negative value, hence the subtraction.
-        selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
-      }
-    }
-  }
-
-  // Traverses all the lines and updates the min and max 'x' positions and the total height.
-  // The final width is calculated after 'boxifying' the selection.
-  for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
-         endIt = selectionBoxLinesInfo.End();
-       it != endIt;
-       ++it )
-  {
-    const SelectionBoxInfo& info = *it;
-
-    // Update the size of the highlighted text.
-    highLightSize.height += info.lineHeight;
-    minHighlightX = std::min( minHighlightX, info.minX );
-    maxHighlightX = std::max( maxHighlightX, info.maxX );
-  }
-
-  // Add extra geometry to 'boxify' the selection.
-
-  if( 1u < numberOfLines )
-  {
-    // Boxify the first line.
-    lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
-    const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
-
-    bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
-    bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
-
-    if( boxifyBegin )
-    {
-      quad.x = 0.f;
-      quad.y = firstSelectionBoxLineInfo.lineOffset;
-      quad.z = firstSelectionBoxLineInfo.minX;
-      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the beginning of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      minHighlightX = 0.f;
-    }
-
-    if( boxifyEnd )
-    {
-      quad.x = firstSelectionBoxLineInfo.maxX;
-      quad.y = firstSelectionBoxLineInfo.lineOffset;
-      quad.z = mModel->mVisualModel->mControlSize.width;
-      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the end of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      maxHighlightX = mModel->mVisualModel->mControlSize.width;
-    }
-
-    // Boxify the central lines.
-    if( 2u < numberOfLines )
-    {
-      for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
-             endIt = selectionBoxLinesInfo.End() - 1u;
-           it != endIt;
-           ++it )
-      {
-        const SelectionBoxInfo& info = *it;
-
-        quad.x = 0.f;
-        quad.y = info.lineOffset;
-        quad.z = info.minX;
-        quad.w = info.lineOffset + info.lineHeight;
-
-        mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                              quad );
-        ++actualNumberOfQuads;
-
-        quad.x = info.maxX;
-        quad.y = info.lineOffset;
-        quad.z = mModel->mVisualModel->mControlSize.width;
-        quad.w = info.lineOffset + info.lineHeight;
-
-        mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                              quad );
-        ++actualNumberOfQuads;
-      }
-
-      // Update the size of the highlighted text.
-      minHighlightX = 0.f;
-      maxHighlightX = mModel->mVisualModel->mControlSize.width;
-    }
-
-    // Boxify the last line.
-    lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
-    const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
-
-    boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
-    boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
-
-    if( boxifyBegin )
-    {
-      quad.x = 0.f;
-      quad.y = lastSelectionBoxLineInfo.lineOffset;
-      quad.z = lastSelectionBoxLineInfo.minX;
-      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the beginning of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      minHighlightX = 0.f;
-    }
-
-    if( boxifyEnd )
-    {
-      quad.x = lastSelectionBoxLineInfo.maxX;
-      quad.y = lastSelectionBoxLineInfo.lineOffset;
-      quad.z = mModel->mVisualModel->mControlSize.width;
-      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
-
-      // Boxify at the end of the line.
-      mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
-                                            quad );
-      ++actualNumberOfQuads;
-
-      // Update the size of the highlighted text.
-      maxHighlightX = mModel->mVisualModel->mControlSize.width;
-    }
-  }
-
-  // Set the actual number of quads.
-  mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
-
-  // Sets the highlight's size and position. In decorator's coords.
-  // The highlight's height has been calculated above (before 'boxifying' the highlight).
-  highLightSize.width = maxHighlightX - minHighlightX;
-
-  highLightPosition.x = minHighlightX;
-  const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
-  highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
-
-  mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
-
-  if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
-  {
-    CursorInfo primaryCursorInfo;
-    GetCursorPosition( mEventData->mLeftSelectionPosition,
-                       primaryCursorInfo );
-
-    const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
-
-    mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
-                                         primaryPosition.x,
-                                         primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
-                                         primaryCursorInfo.lineHeight );
-
-    CursorInfo secondaryCursorInfo;
-    GetCursorPosition( mEventData->mRightSelectionPosition,
-                       secondaryCursorInfo );
-
-    const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
-
-    mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
-                                         secondaryPosition.x,
-                                         secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
-                                         secondaryCursorInfo.lineHeight );
-  }
-
-  // Set the flag to update the decorator.
-  mEventData->mDecoratorUpdated = true;
+  SelectionHandleController::Reposition(*this);
 }
-
 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action )
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  if( IsShowingPlaceholderText() )
-  {
-    // Nothing to do if there is the place-holder text.
-    return;
-  }
-
-  const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
-  const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
-  if( ( 0 == numberOfGlyphs ) ||
-      ( 0 == numberOfLines ) )
-  {
-    // Nothing to do if there is no text.
-    return;
-  }
-
-  // Find which word was selected
-  CharacterIndex selectionStart( 0 );
-  CharacterIndex selectionEnd( 0 );
-  CharacterIndex noTextHitIndex( 0 );
-  const bool characterHit = FindSelectionIndices( mModel->mVisualModel,
-                                                  mModel->mLogicalModel,
-                                                  mMetrics,
-                                                  visualX,
-                                                  visualY,
-                                                  selectionStart,
-                                                  selectionEnd,
-                                                  noTextHitIndex );
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
-
-  if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
-  {
-    ChangeState( EventData::SELECTING );
-
-    mEventData->mLeftSelectionPosition = selectionStart;
-    mEventData->mRightSelectionPosition = selectionEnd;
-
-    mEventData->mUpdateLeftSelectionPosition = true;
-    mEventData->mUpdateRightSelectionPosition = true;
-    mEventData->mUpdateHighlightBox = true;
-
-    // It may happen an InputMethodContext commit event arrives before the selection event
-    // if the InputMethodContext is in pre-edit state. The commit event will set the
-    // mEventData->mUpdateCursorPosition flag to true. If it's not set back
-    // to false, the highlight box won't be updated.
-    mEventData->mUpdateCursorPosition = false;
-
-    mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
-
-    // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
-    mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
-  }
-  else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
-  {
-    // Nothing to select. i.e. a white space, out of bounds
-    ChangeState( EventData::EDITING_WITH_POPUP );
-
-    mEventData->mPrimaryCursorPosition = noTextHitIndex;
-
-    mEventData->mUpdateCursorPosition = true;
-    mEventData->mUpdateGrabHandlePosition = true;
-    mEventData->mScrollAfterUpdatePosition = true;
-    mEventData->mUpdateInputStyle = true;
-  }
-  else if( Controller::NoTextTap::NO_ACTION == action )
-  {
-    // Nothing to select. i.e. a white space, out of bounds
-    mEventData->mPrimaryCursorPosition = noTextHitIndex;
-
-    mEventData->mUpdateCursorPosition = true;
-    mEventData->mUpdateGrabHandlePosition = true;
-    mEventData->mScrollAfterUpdatePosition = true;
-    mEventData->mUpdateInputStyle = true;
-  }
+  SelectionHandleController::Reposition(*this, visualX, visualY, action);
 }
 
 void Controller::Impl::SetPopupButtons()
@@ -2582,24 +1841,7 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
                                               const CursorInfo& cursorInfo )
 {
-  if( ( LEFT_SELECTION_HANDLE != handleType ) &&
-      ( RIGHT_SELECTION_HANDLE != handleType ) )
-  {
-    return;
-  }
-
-  const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
-
-  // Sets the handle's position.
-  mEventData->mDecorator->SetPosition( handleType,
-                                       cursorPosition.x,
-                                       cursorInfo.lineOffset + mModel->mScrollPosition.y,
-                                       cursorInfo.lineHeight );
-
-  // If selection handle at start of the text and other at end of the text then all text is selected.
-  const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
-  const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
-  mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
+  SelectionHandleController::Update(*this, handleType, cursorInfo);
 }
 
 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
index 63a897c..db02721 100755 (executable)
@@ -49,6 +49,7 @@ const float DEFAULT_FONT_SIZE_SCALE = 1.f;
 struct CursorInfo;
 struct FontDefaults;
 struct ControllerImplEventHandler;
+struct SelectionHandleController;
 
 class SelectableControlInterface;
 
@@ -109,7 +110,7 @@ struct EventData
 
   EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext );
 
-  ~EventData();
+  ~EventData() = default;
 
   static bool IsEditingState( State stateToCheck )
   {
@@ -174,7 +175,7 @@ struct EventData
   bool mPlaceholderEllipsisFlag         : 1;   ///< True if the text controller sets the placeholder ellipsis.
   bool mShiftSelectionFlag              : 1;   ///< True if the text selection using Shift key is enabled.
   bool mUpdateAlignment                 : 1;   ///< True if the whole text needs to be full aligned..
-  bool mEditingEnabled                   : 1;   ///< True if the editing is enabled, false otherwise.
+  bool mEditingEnabled                  : 1;   ///< True if the editing is enabled, false otherwise.
 };
 
 struct ModifyEvent
@@ -614,22 +615,6 @@ struct Controller::Impl
    */
   float GetDefaultFontLineHeight();
 
-  void OnCursorKeyEvent( const Event& event );
-
-  void OnTapEvent( const Event& event );
-
-  void OnPanEvent( const Event& event );
-
-  void OnLongPressEvent( const Event& event );
-
-  void OnHandleEvent( const Event& event );
-
-  void OnSelectEvent( const Event& event );
-
-  void OnSelectAllEvent();
-
-  void OnSelectNoneEvent();
-
   /**
    * @copydoc Text::Controller::GetPrimaryCursorPosition()
    */
@@ -849,6 +834,7 @@ public:
 
 private:
   friend ControllerImplEventHandler;
+  friend SelectionHandleController;
 };
 
 } // namespace Text
diff --git a/dali-toolkit/internal/text/text-selection-handle-controller.cpp b/dali-toolkit/internal/text/text-selection-handle-controller.cpp
new file mode 100644 (file)
index 0000000..664b59e
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 2020 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-selection-handle-controller.h>
+
+#include <dali/integration-api/debug.h>
+#include <limits>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
+
+using namespace Dali;
+
+namespace
+{
+
+/**
+ * @brief Struct used to calculate the selection box.
+ */
+struct SelectionBoxInfo
+{
+  float lineOffset;
+  float lineHeight;
+  float minX;
+  float maxX;
+};
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
+#endif
+
+const float MAX_FLOAT = std::numeric_limits<float>::max();
+const float MIN_FLOAT = std::numeric_limits<float>::min();
+const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
+
+} // namespace
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Text
+{
+
+void SelectionHandleController::Reposition(Controller::Impl& impl)
+{
+  EventData*& eventData = impl.mEventData;
+
+  CharacterIndex selectionStart = eventData->mLeftSelectionPosition;
+  CharacterIndex selectionEnd = eventData->mRightSelectionPosition;
+
+  DecoratorPtr& decorator = eventData->mDecorator;
+
+  if( selectionStart == selectionEnd )
+  {
+    // Nothing to select if handles are in the same place.
+    // So, deactive Highlight box.
+    decorator->SetHighlightActive( false );
+    return;
+  }
+
+  decorator->ClearHighlights();
+
+  ModelPtr& model = impl.mModel;
+  VisualModelPtr& visualModel = model->mVisualModel;
+  LogicalModelPtr& logicalModel = model->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;
+
+  const bool isLastCharacter = selectionEnd >= logicalModel->mText.Count();
+  const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
+  const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
+
+  // Swap the indices if the start is greater than the end.
+  const bool indicesSwapped = selectionStart > selectionEnd;
+
+  // Tell the decorator to flip the selection handles if needed.
+  decorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
+
+  if( indicesSwapped )
+  {
+    std::swap( selectionStart, selectionEnd );
+  }
+
+  // Get the indices to the first and last selected glyphs.
+  const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
+  const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
+  const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
+  const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
+
+  // Get the lines where the glyphs are laid-out.
+  const LineRun* lineRun = visualModel->mLines.Begin();
+
+  LineIndex lineIndex = 0u;
+  Length numberOfLines = 0u;
+  visualModel->GetNumberOfLines( glyphStart,
+                                 1u + glyphEnd - glyphStart,
+                                 lineIndex,
+                                 numberOfLines );
+  const LineIndex firstLineIndex = lineIndex;
+
+  // Create the structure to store some selection box info.
+  Vector<SelectionBoxInfo> selectionBoxLinesInfo;
+  selectionBoxLinesInfo.Resize( numberOfLines );
+
+  SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
+  selectionBoxInfo->minX = MAX_FLOAT;
+  selectionBoxInfo->maxX = MIN_FLOAT;
+
+  // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
+  float minHighlightX = std::numeric_limits<float>::max();
+  float maxHighlightX = std::numeric_limits<float>::min();
+  Size highLightSize;
+  Vector2 highLightPosition; // The highlight position in decorator's coords.
+
+  // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
+
+  // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
+  selectionBoxInfo->lineOffset = CalculateLineOffset( visualModel->mLines,
+                                                      firstLineIndex );
+
+  // Transform to decorator's (control) coords.
+  selectionBoxInfo->lineOffset += model->mScrollPosition.y;
+
+  lineRun += firstLineIndex;
+
+  // The line height is the addition of the line ascender and the line descender.
+  // However, the line descender has a negative value, hence the subtraction.
+  selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+
+  GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+
+  // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
+  const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
+  bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( logicalModel->GetScript( selectionStart ) );
+
+  // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code.
+  const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
+  bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( logicalModel->GetScript( selectionEndMinusOne ) );
+
+  // The number of quads of the selection box.
+  const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
+  decorator->ResizeHighlightQuads( numberOfQuads );
+
+  // Count the actual number of quads.
+  unsigned int actualNumberOfQuads = 0u;
+  Vector4 quad;
+
+  // Traverse the glyphs.
+  for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
+  {
+    const GlyphInfo& glyph = *( glyphsBuffer + index );
+    const Vector2& position = *( positionsBuffer + index );
+
+    if( splitStartGlyph )
+    {
+      // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+
+      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
+      const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
+      // Get the direction of the character.
+      CharacterDirection isCurrentRightToLeft = false;
+      if( nullptr != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+      {
+        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
+      }
+
+      // The end point could be in the middle of the ligature.
+      // Calculate the number of characters selected.
+      const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
+
+      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
+      quad.y = selectionBoxInfo->lineOffset;
+      quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
+      quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
+
+      // Store the min and max 'x' for each line.
+      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
+      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
+
+      decorator->AddHighlight( actualNumberOfQuads, quad );
+      ++actualNumberOfQuads;
+
+      splitStartGlyph = false;
+      continue;
+    }
+
+    if( splitEndGlyph && ( index == glyphEnd ) )
+    {
+      // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
+
+      const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
+      const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
+      // Get the direction of the character.
+      CharacterDirection isCurrentRightToLeft = false;
+      if( nullptr != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
+      {
+        isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
+      }
+
+      const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
+
+      quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
+      quad.y = selectionBoxInfo->lineOffset;
+      quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
+      quad.w = quad.y + selectionBoxInfo->lineHeight;
+
+      // Store the min and max 'x' for each line.
+      selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
+      selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
+
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      splitEndGlyph = false;
+      continue;
+    }
+
+    quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + model->mScrollPosition.x;
+    quad.y = selectionBoxInfo->lineOffset;
+    quad.z = quad.x + glyph.advance;
+    quad.w = quad.y + selectionBoxInfo->lineHeight;
+
+    // Store the min and max 'x' for each line.
+    selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
+    selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
+
+    decorator->AddHighlight( actualNumberOfQuads,
+                                          quad );
+    ++actualNumberOfQuads;
+
+    // Whether to retrieve the next line.
+    if( index == lastGlyphOfLine )
+    {
+      ++lineIndex;
+      if( lineIndex < firstLineIndex + numberOfLines )
+      {
+        // Retrieve the next line.
+        ++lineRun;
+
+        // Get the last glyph of the new line.
+        lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
+
+        // Keep the offset and height of the current selection box.
+        const float currentLineOffset = selectionBoxInfo->lineOffset;
+        const float currentLineHeight = selectionBoxInfo->lineHeight;
+
+        // Get the selection box info for the next line.
+        ++selectionBoxInfo;
+
+        selectionBoxInfo->minX = MAX_FLOAT;
+        selectionBoxInfo->maxX = MIN_FLOAT;
+
+        // Update the line's vertical offset.
+        selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
+
+        // The line height is the addition of the line ascender and the line descender.
+        // However, the line descender has a negative value, hence the subtraction.
+        selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
+      }
+    }
+  }
+
+  // Traverses all the lines and updates the min and max 'x' positions and the total height.
+  // The final width is calculated after 'boxifying' the selection.
+  for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
+         endIt = selectionBoxLinesInfo.End();
+       it != endIt;
+       ++it )
+  {
+    const SelectionBoxInfo& info = *it;
+
+    // Update the size of the highlighted text.
+    highLightSize.height += info.lineHeight;
+    minHighlightX = std::min( minHighlightX, info.minX );
+    maxHighlightX = std::max( maxHighlightX, info.maxX );
+  }
+
+  // Add extra geometry to 'boxify' the selection.
+
+  if( 1u < numberOfLines )
+  {
+    // Boxify the first line.
+    lineRun = visualModel->mLines.Begin() + firstLineIndex;
+    const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
+
+    bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
+    bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
+
+    if( boxifyBegin )
+    {
+      quad.x = 0.f;
+      quad.y = firstSelectionBoxLineInfo.lineOffset;
+      quad.z = firstSelectionBoxLineInfo.minX;
+      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the beginning of the line.
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      minHighlightX = 0.f;
+    }
+
+    if( boxifyEnd )
+    {
+      quad.x = firstSelectionBoxLineInfo.maxX;
+      quad.y = firstSelectionBoxLineInfo.lineOffset;
+      quad.z = visualModel->mControlSize.width;
+      quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the end of the line.
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      maxHighlightX = visualModel->mControlSize.width;
+    }
+
+    // Boxify the central lines.
+    if( 2u < numberOfLines )
+    {
+      for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
+             endIt = selectionBoxLinesInfo.End() - 1u;
+           it != endIt;
+           ++it )
+      {
+        const SelectionBoxInfo& info = *it;
+
+        quad.x = 0.f;
+        quad.y = info.lineOffset;
+        quad.z = info.minX;
+        quad.w = info.lineOffset + info.lineHeight;
+
+        decorator->AddHighlight( actualNumberOfQuads,
+                                              quad );
+        ++actualNumberOfQuads;
+
+        quad.x = info.maxX;
+        quad.y = info.lineOffset;
+        quad.z = visualModel->mControlSize.width;
+        quad.w = info.lineOffset + info.lineHeight;
+
+        decorator->AddHighlight( actualNumberOfQuads,
+                                              quad );
+        ++actualNumberOfQuads;
+      }
+
+      // Update the size of the highlighted text.
+      minHighlightX = 0.f;
+      maxHighlightX = visualModel->mControlSize.width;
+    }
+
+    // Boxify the last line.
+    lineRun = visualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
+    const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
+
+    boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
+    boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
+
+    if( boxifyBegin )
+    {
+      quad.x = 0.f;
+      quad.y = lastSelectionBoxLineInfo.lineOffset;
+      quad.z = lastSelectionBoxLineInfo.minX;
+      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the beginning of the line.
+      decorator->AddHighlight( actualNumberOfQuads,
+                                            quad );
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      minHighlightX = 0.f;
+    }
+
+    if( boxifyEnd )
+    {
+      quad.x = lastSelectionBoxLineInfo.maxX;
+      quad.y = lastSelectionBoxLineInfo.lineOffset;
+      quad.z = visualModel->mControlSize.width;
+      quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
+
+      // Boxify at the end of the line.
+      decorator->AddHighlight(actualNumberOfQuads, quad);
+      ++actualNumberOfQuads;
+
+      // Update the size of the highlighted text.
+      maxHighlightX = visualModel->mControlSize.width;
+    }
+  }
+
+  // Set the actual number of quads.
+  decorator->ResizeHighlightQuads( actualNumberOfQuads );
+
+  // Sets the highlight's size and position. In decorator's coords.
+  // The highlight's height has been calculated above (before 'boxifying' the highlight).
+  highLightSize.width = maxHighlightX - minHighlightX;
+
+  highLightPosition.x = minHighlightX;
+  const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
+  highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
+
+  decorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( model->GetOutlineWidth() ) );
+
+  if( !decorator->IsSmoothHandlePanEnabled() )
+  {
+    CursorInfo primaryCursorInfo;
+    impl.GetCursorPosition(eventData->mLeftSelectionPosition, primaryCursorInfo);
+
+    const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + model->mScrollPosition;
+
+    decorator->SetPosition(LEFT_SELECTION_HANDLE,
+                           primaryPosition.x,
+                           primaryCursorInfo.lineOffset + model->mScrollPosition.y,
+                           primaryCursorInfo.lineHeight );
+
+    CursorInfo secondaryCursorInfo;
+    impl.GetCursorPosition(eventData->mRightSelectionPosition, secondaryCursorInfo);
+
+    const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + model->mScrollPosition;
+
+    decorator->SetPosition(RIGHT_SELECTION_HANDLE,
+                           secondaryPosition.x,
+                           secondaryCursorInfo.lineOffset + model->mScrollPosition.y,
+                           secondaryCursorInfo.lineHeight );
+  }
+
+  // Set the flag to update the decorator.
+  eventData->mDecoratorUpdated = true;
+}
+
+void SelectionHandleController::Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action)
+{
+  EventData*& eventData = impl.mEventData;
+  if(nullptr == eventData)
+  {
+    // Nothing to do if there is no text input.
+    return;
+  }
+
+  if(impl.IsShowingPlaceholderText())
+  {
+    // Nothing to do if there is the place-holder text.
+    return;
+  }
+
+  ModelPtr& model = impl.mModel;
+  VisualModelPtr& visualModel = model->mVisualModel;
+  const Length numberOfGlyphs = visualModel->mGlyphs.Count();
+  const Length numberOfLines  = visualModel->mLines.Count();
+  if( ( 0 == numberOfGlyphs ) ||
+      ( 0 == numberOfLines ) )
+  {
+    // Nothing to do if there is no text.
+    return;
+  }
+
+  // Find which word was selected
+  CharacterIndex selectionStart( 0 );
+  CharacterIndex selectionEnd( 0 );
+  CharacterIndex noTextHitIndex( 0 );
+  const bool characterHit = FindSelectionIndices( visualModel,
+                                                  model->mLogicalModel,
+                                                  impl.mMetrics,
+                                                  visualX,
+                                                  visualY,
+                                                  selectionStart,
+                                                  selectionEnd,
+                                                  noTextHitIndex );
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", &impl, selectionStart, selectionEnd );
+
+  if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) )
+  {
+    impl.ChangeState( EventData::SELECTING );
+
+    eventData->mLeftSelectionPosition = selectionStart;
+    eventData->mRightSelectionPosition = selectionEnd;
+
+    eventData->mUpdateLeftSelectionPosition = true;
+    eventData->mUpdateRightSelectionPosition = true;
+    eventData->mUpdateHighlightBox = true;
+
+    // It may happen an InputMethodContext commit event arrives before the selection event
+    // if the InputMethodContext is in pre-edit state. The commit event will set the
+    // eventData->mUpdateCursorPosition flag to true. If it's not set back
+    // to false, the highlight box won't be updated.
+    eventData->mUpdateCursorPosition = false;
+
+    eventData->mScrollAfterUpdatePosition = ( eventData->mLeftSelectionPosition != eventData->mRightSelectionPosition );
+
+    // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
+    eventData->mPrimaryCursorPosition = std::max( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
+  }
+  else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
+  {
+    // Nothing to select. i.e. a white space, out of bounds
+    impl.ChangeState( EventData::EDITING_WITH_POPUP );
+
+    eventData->mPrimaryCursorPosition = noTextHitIndex;
+
+    eventData->mUpdateCursorPosition = true;
+    eventData->mUpdateGrabHandlePosition = true;
+    eventData->mScrollAfterUpdatePosition = true;
+    eventData->mUpdateInputStyle = true;
+  }
+  else if( Controller::NoTextTap::NO_ACTION == action )
+  {
+    // Nothing to select. i.e. a white space, out of bounds
+    eventData->mPrimaryCursorPosition = noTextHitIndex;
+
+    eventData->mUpdateCursorPosition = true;
+    eventData->mUpdateGrabHandlePosition = true;
+    eventData->mScrollAfterUpdatePosition = true;
+    eventData->mUpdateInputStyle = true;
+  }
+}
+
+void SelectionHandleController::Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo)
+{
+  if( ( LEFT_SELECTION_HANDLE != handleType ) &&
+      ( RIGHT_SELECTION_HANDLE != handleType ) )
+  {
+    return;
+  }
+
+  ModelPtr& model = impl.mModel;
+  const Vector2 cursorPosition = cursorInfo.primaryPosition + model->mScrollPosition;
+
+  // Sets the handle's position.
+  EventData*& eventData = impl.mEventData;
+  eventData->mDecorator->SetPosition(handleType,
+                                     cursorPosition.x,
+                                     cursorInfo.lineOffset + model->mScrollPosition.y,
+                                     cursorInfo.lineHeight );
+
+  // If selection handle at start of the text and other at end of the text then all text is selected.
+  const CharacterIndex startOfSelection = std::min( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
+  const CharacterIndex endOfSelection = std::max ( eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition );
+  eventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == model->mLogicalModel->mText.Count() );
+}
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/text/text-selection-handle-controller.h b/dali-toolkit/internal/text/text-selection-handle-controller.h
new file mode 100644 (file)
index 0000000..2988c85
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef DALI_TOOLKIT_TEXT_SELECTION_HANDLE_CONTROLLER_H
+#define DALI_TOOLKIT_TEXT_SELECTION_HANDLE_CONTROLLER_H
+
+/*
+ * Copyright (c) 2020 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/decorator/text-decorator.h>
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+#include <dali-toolkit/internal/text/text-controller.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Text
+{
+
+/**
+ * @brief Updates the Selection Handles.
+ */
+struct SelectionHandleController
+{
+  /// @copydoc Controller::Impl::RepositionSelectionHandles
+  /// @param[in] impl The Text controller Impl.
+  static void Reposition(Controller::Impl& impl);
+
+  /// @copydoc Controller::Impl::RepositionSelectionHandles
+  /// @param[in] impl The Text controller Impl.
+  static void Reposition(Controller::Impl& impl, float visualX, float visualY, Controller::NoTextTap::Action action);
+
+  /// @copydoc Controller::Impl::UpdateSelectionHandle
+  /// @param[in] impl The Text controller Impl.
+  static void Update(Controller::Impl& impl, HandleType handleType, const CursorInfo& cursorInfo);
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_SELECTION_HANDLE_CONTROLLER_H
index 456b89e..cb900a4 100755 (executable)
@@ -174,7 +174,7 @@ AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache, Imag
   mPixelArea( FULL_TEXTURE_RECT ),
   mImageUrl(),
   mAnimatedImageLoading(),
-  mCurrentFrameIndex( 0 ),
+  mFrameIndexForJumpTo( 0 ),
   mImageUrls( NULL ),
   mImageCache( NULL ),
   mCacheSize( 2 ),
@@ -302,7 +302,7 @@ void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, cons
         else
         {
           mIsJumpTo = true;
-          mCurrentFrameIndex = frameNumber;
+          mFrameIndexForJumpTo = frameNumber;
           if( IsOnScene() )
           {
             DisplayNextFrame();
@@ -544,8 +544,6 @@ void AnimatedImageVisual::CreateRenderer()
   {
     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
   }
-
-  mCurrentFrameIndex = 0;
 }
 
 void AnimatedImageVisual::LoadFirstBatch()
@@ -618,14 +616,12 @@ void AnimatedImageVisual::StartFirstFrame( TextureSet& textureSet )
     mPlacementActor.Reset();
   }
 
-  mCurrentFrameIndex = 0;
-
   if( mFrameCount > 1 )
   {
-    int frameDelay = mFrameDelay; // from URL array
-    if( mAnimatedImageLoading && mImageCache )
+    int frameDelay = mImageCache->GetFrameInterval( 0 );
+    if( frameDelay == 0u )
     {
-      frameDelay = mImageCache->GetFrameInterval( 0 );
+      frameDelay = mFrameDelay; // from URL array
     }
     mFrameDelayTimer = Timer::New( frameDelay );
     mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
@@ -642,15 +638,11 @@ TextureSet AnimatedImageVisual::PrepareTextureSet()
   {
     textureSet = mImageCache->FirstFrame();
   }
+
   if( textureSet )
   {
     SetImageSize( textureSet );
   }
-  else
-  {
-    DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
-    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
-  }
 
   return textureSet;
 }
@@ -670,99 +662,115 @@ void AnimatedImageVisual::SetImageSize( TextureSet& textureSet )
 
 void AnimatedImageVisual::FrameReady( TextureSet textureSet )
 {
-  SetImageSize( textureSet );
-
-  if( mStartFirstFrame )
+  if(textureSet)
   {
-    StartFirstFrame( textureSet );
+    SetImageSize(textureSet);
+
+    if(mStartFirstFrame)
+    {
+      StartFirstFrame(textureSet);
+    }
+    else
+    {
+      if(mImpl->mRenderer)
+      {
+        mImpl->mRenderer.SetTextures(textureSet);
+      }
+    }
   }
   else
   {
-    if( mImpl->mRenderer )
-    {
-      mImpl->mRenderer.SetTextures( textureSet );
-    }
+    DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
   }
 }
 
 bool AnimatedImageVisual::DisplayNextFrame()
 {
-  if( mIsJumpTo )
-  {
-    mIsJumpTo = false;
-  }
-  else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
-  {
-    return false;
-  }
-  else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
+  bool continueTimer = false;
+
+  if(mImageCache)
   {
-    mCurrentLoopIndex = 0;
-    if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
-    {
-      mCurrentFrameIndex = 0;
-    }
-    else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
+    bool nextFrame = false;
+    uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
+
+    if( mIsJumpTo )
     {
-      mCurrentFrameIndex = mFrameCount - 1;
+      mIsJumpTo = false;
+      frameIndex = mFrameIndexForJumpTo;
     }
-    else
+    else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
     {
-      return false; // Do not draw already rendered scene twice.
+      return false;
     }
-  }
-  else
-  {
-    if( mFrameCount > 1 )
+    else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
     {
-      // Wrap the frame index
-      bool finished = false;
-      ++mCurrentFrameIndex;
-      if( mCurrentFrameIndex >= mFrameCount )
+      frameIndex = 0;
+      if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
       {
-        ++mCurrentLoopIndex;
-        finished = true;
+        frameIndex = 0;
       }
-
-      if( mLoopCount < 0 || mCurrentLoopIndex < mLoopCount)
+      else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
       {
-        if( finished )
-        {
-          mCurrentFrameIndex = 0; // Back to the first frame
-        }
+        frameIndex = mFrameCount - 1;
       }
       else
       {
-        // This will stop timer
-        mActionStatus = DevelAnimatedImageVisual::Action::STOP;
-        return DisplayNextFrame();
+        return false; // Do not draw already rendered scene twice.
       }
     }
-    // TODO : newly added one.
-    if( mAnimatedImageLoading && mImageCache )
+    else
     {
-      unsigned int delay = mImageCache->GetFrameInterval( mCurrentFrameIndex );
-      if( mFrameDelayTimer.GetInterval() != delay )
+      if( mFrameCount > 1 )
       {
-        mFrameDelayTimer.SetInterval( delay );
+        nextFrame = true;
+        frameIndex++;
+        if( frameIndex >= mFrameCount )
+        {
+          frameIndex %= mFrameCount;
+          ++mCurrentLoopIndex;
+        }
+
+        if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
+        {
+          // This will stop timer
+          mActionStatus = DevelAnimatedImageVisual::Action::STOP;
+          return DisplayNextFrame();
+        }
+      }
+
+      unsigned int delay = mImageCache->GetFrameInterval( frameIndex );
+      if( delay > 0u )
+      {
+        if( mFrameDelayTimer.GetInterval() != delay )
+        {
+          mFrameDelayTimer.SetInterval( delay );
+        }
       }
     }
-  }
 
-  DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, mCurrentFrameIndex);
+    DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
+
+    TextureSet textureSet;
+    if(nextFrame)
+    {
+      textureSet = mImageCache->NextFrame();
+    }
+    else
+    {
+      textureSet = mImageCache->Frame( frameIndex );
+    }
 
-  TextureSet textureSet;
-  if( mImageCache )
-  {
-    textureSet = mImageCache->Frame( mCurrentFrameIndex );
     if( textureSet )
     {
       SetImageSize( textureSet );
       mImpl->mRenderer.SetTextures( textureSet );
     }
+
+    continueTimer = ( mActionStatus == DevelAnimatedImageVisual::Action::PLAY ) ? true : false;
   }
 
-  return ( mActionStatus == DevelAnimatedImageVisual::Action::PLAY ) ? true : false;
+  return continueTimer;
 }
 
 
index 8e91623..862dbb8 100644 (file)
@@ -246,7 +246,7 @@ private:
   Vector4 mPixelArea;
   VisualUrl mImageUrl;
   Dali::AnimatedImageLoading mAnimatedImageLoading; // Only needed for animated image
-  uint32_t mCurrentFrameIndex; // Frame index into textureRects
+  uint32_t mFrameIndexForJumpTo; // Frame index into textureRects
 
   // Variables for Multi-Image player
   ImageCache::UrlList* mImageUrls;
index 726cd39..a90b31e 100644 (file)
@@ -93,11 +93,23 @@ TextureSet FixedImageCache::FirstFrame()
   return textureSet;
 }
 
-uint32_t FixedImageCache::GetFrameInterval( uint32_t frameIndex )
+TextureSet FixedImageCache::NextFrame()
+{
+  TextureSet textureSet = Frame((mFront + 1) % mImageUrls.size());
+
+  return textureSet;
+}
+
+uint32_t FixedImageCache::GetFrameInterval( uint32_t frameIndex ) const
 {
   return 0u;
 }
 
+int32_t FixedImageCache::GetCurrentFrameIndex() const
+{
+  return static_cast<int32_t>(mFront);
+}
+
 bool FixedImageCache::IsFrontReady() const
 {
   return ( mReadyFlags.size() > 0 && mReadyFlags[mFront] == true );
index ec7f419..bd232b4 100644 (file)
@@ -61,9 +61,21 @@ public:
   TextureSet FirstFrame() override;
 
   /**
+   * Get the next frame. If it's not ready, this will trigger the
+   * sending of FrameReady() when the image becomes ready.
+   */
+  TextureSet NextFrame() override;
+
+  /**
    * Get the interval of Nth frame.
    */
-  uint32_t GetFrameInterval( uint32_t frameIndex ) override;
+  uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+
+  /**
+   * Get the current rendered frame index.
+   * If there isn't any loaded frame, returns -1.
+   */
+  int32_t GetCurrentFrameIndex() const override;
 
 private:
   /**
index 1d385c8..b16b94e 100644 (file)
@@ -79,6 +79,12 @@ public:
   virtual TextureSet FirstFrame() = 0;
 
   /**
+   * Get the next frame. If it's not ready, this will trigger the
+   * sending of FrameReady() when the image becomes ready.
+   */
+  virtual TextureSet NextFrame() = 0;
+
+  /**
    * Get the Nth frame. If it's not ready, this will trigger the
    * sending of FrameReady() when the image becomes ready.
    */
@@ -87,7 +93,13 @@ public:
   /**
    * Get the interval of Nth frame.
    */
-  virtual uint32_t GetFrameInterval( uint32_t frameIndex ) = 0;
+  virtual uint32_t GetFrameInterval( uint32_t frameIndex ) const = 0;
+
+  /**
+   * Get the current rendered frame index.
+   * If there isn't any loaded frame, returns -1.
+   */
+  virtual int32_t GetCurrentFrameIndex() const = 0;
 
 private:
 
index 3992b08..2733d7a 100644 (file)
@@ -65,6 +65,7 @@ RollingAnimatedImageCache::RollingAnimatedImageCache(
   mAnimatedImageLoading( animatedImageLoading ),
   mFrameCount( frameCount ),
   mFrameIndex( 0 ),
+  mCacheSize( cacheSize ),
   mQueue( cacheSize ),
   mIsSynchronousLoading( isSynchronousLoading ),
   mOnLoading( false )
@@ -114,10 +115,18 @@ TextureSet RollingAnimatedImageCache::Frame( uint32_t frameIndex )
     // If the frame of frameIndex was already loaded, load batch from the last frame of queue
     if( !mQueue.IsEmpty() )
     {
-      mFrameIndex = ( mQueue.Back().mFrameNumber + 1 ) % mFrameCount;
+      if(!mLoadWaitingQueue.empty())
+      {
+        mFrameIndex = ( mLoadWaitingQueue.back() + 1 ) % mFrameCount;
+      }
+      else
+      {
+        mFrameIndex = ( mQueue.Back().mFrameNumber + 1 ) % mFrameCount;
+      }
     }
     else
     {
+      mOnLoading = false;
       // If the request is for the first frame or a jumped frame(JUMP_TO) remove current waiting queue.
       mLoadWaitingQueue.clear();
       // If the queue is empty, and the frame of frameIndex is not loaded synchronously. load batch from the frame of frameIndex
@@ -149,11 +158,40 @@ TextureSet RollingAnimatedImageCache::FirstFrame()
   return Frame( 0u );
 }
 
-uint32_t RollingAnimatedImageCache::GetFrameInterval( uint32_t frameIndex )
+TextureSet RollingAnimatedImageCache::NextFrame()
+{
+  TextureSet textureSet;
+  if(!mQueue.IsEmpty())
+  {
+    uint32_t frameIndex = mQueue.Front().mFrameNumber;
+    if(IsFrontReady())
+    {
+      frameIndex = (frameIndex + 1) % mFrameCount;
+    }
+    textureSet = Frame(frameIndex);
+  }
+  else
+  {
+    DALI_LOG_ERROR("Cache is empty.");
+  }
+
+  return textureSet;
+}
+
+uint32_t RollingAnimatedImageCache::GetFrameInterval( uint32_t frameIndex ) const
 {
   return mAnimatedImageLoading.GetFrameInterval( frameIndex );
 }
 
+int32_t RollingAnimatedImageCache::GetCurrentFrameIndex() const
+{
+  if(mQueue.IsEmpty())
+  {
+    return -1;
+  }
+  return mQueue.Front().mFrameNumber;
+}
+
 bool RollingAnimatedImageCache::IsFrontReady() const
 {
   return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
@@ -161,6 +199,12 @@ bool RollingAnimatedImageCache::IsFrontReady() const
 
 void RollingAnimatedImageCache::RequestFrameLoading( uint32_t frameIndex )
 {
+  ImageFrame imageFrame;
+  imageFrame.mFrameNumber = frameIndex;
+  imageFrame.mReady       = false;
+
+  mQueue.PushBack(imageFrame);
+
   mRequestingLoad = true;
 
   bool synchronousLoading = false;
@@ -178,14 +222,8 @@ void RollingAnimatedImageCache::LoadBatch()
   // removed, and another frame is loaded
 
   bool frontFrameReady = IsFrontReady();
-  for( unsigned int i=0; i< mBatchSize && !mQueue.IsFull(); ++i )
+  for( unsigned int i=0; i< mBatchSize && mQueue.Count() + mLoadWaitingQueue.size() < static_cast<uint32_t>(mCacheSize) && !mQueue.IsFull(); ++i )
   {
-    ImageFrame imageFrame;
-    imageFrame.mFrameNumber = mFrameIndex;
-    imageFrame.mReady = false;
-
-    mQueue.PushBack( imageFrame );
-
     if( !mOnLoading )
     {
       mOnLoading = true;
index 3a24eb6..db35741 100644 (file)
@@ -79,9 +79,21 @@ public:
   TextureSet FirstFrame() override;
 
   /**
+   * Get the next frame. If it's not ready, this will trigger the
+   * sending of FrameReady() when the image becomes ready.
+   */
+  TextureSet NextFrame() override;
+
+  /**
    * Get the interval of Nth frame.
    */
-  uint32_t GetFrameInterval( uint32_t frameIndex ) override;
+  uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+
+  /**
+   * Get the current rendered frame index.
+   * If there isn't any loaded frame, returns -1.
+   */
+  int32_t GetCurrentFrameIndex() const override;
 
 private:
   /**
@@ -149,6 +161,7 @@ private:
   Dali::AnimatedImageLoading  mAnimatedImageLoading;
   uint32_t                    mFrameCount;
   int                         mFrameIndex;
+  int                         mCacheSize;
   std::vector<UrlStore>       mImageUrls;
   std::vector<int32_t>        mIntervals;
   std::vector<uint32_t>       mLoadWaitingQueue;
index 2284778..e838060 100644 (file)
@@ -131,11 +131,40 @@ TextureSet RollingImageCache::FirstFrame()
   return Frame( 0u );
 }
 
-uint32_t RollingImageCache::GetFrameInterval( uint32_t frameIndex )
+TextureSet RollingImageCache::NextFrame()
+{
+  TextureSet textureSet;
+  if(!mQueue.IsEmpty())
+  {
+    uint32_t frameIndex = mQueue.Front().mUrlIndex;
+    if(IsFrontReady())
+    {
+      frameIndex = (frameIndex + 1) % mImageUrls.size();
+    }
+    textureSet = Frame(frameIndex);
+  }
+  else
+  {
+    DALI_LOG_ERROR("Cache is empty.");
+  }
+
+  return textureSet;
+}
+
+uint32_t RollingImageCache::GetFrameInterval( uint32_t frameIndex ) const
 {
   return 0u;
 }
 
+int32_t RollingImageCache::GetCurrentFrameIndex() const
+{
+  if(mQueue.IsEmpty())
+  {
+    return -1;
+  }
+  return mQueue.Front().mUrlIndex;
+}
+
 bool RollingImageCache::IsFrontReady() const
 {
   return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
index 5f4aa46..c20586f 100644 (file)
@@ -72,9 +72,21 @@ public:
   TextureSet FirstFrame() override;
 
   /**
+   * Get the next frame. If it's not ready, this will trigger the
+   * sending of FrameReady() when the image becomes ready.
+   */
+  TextureSet NextFrame() override;
+
+  /**
    * Get the interval of Nth frame.
    */
-  uint32_t GetFrameInterval( uint32_t frameIndex ) override;
+  uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+
+  /**
+   * Get the current rendered frame index.
+   * If there isn't any loaded frame, returns -1.
+   */
+  int32_t GetCurrentFrameIndex() const override;
 
 private:
   /**
index 93c0966..a2d102a 100644 (file)
@@ -95,7 +95,8 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual( VisualFactoryCache& factor
   mPlayState( DevelImageVisual::PlayState::STOPPED ),
   mEventCallback( nullptr ),
   mRendererAdded( false ),
-  mCoreShutdown(false)
+  mCoreShutdown(false),
+  mRedrawInScalingDown(true)
 {
   // the rasterized image is with pre-multiplied alpha format
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
@@ -172,6 +173,7 @@ void AnimatedVectorImageVisual::DoCreatePropertyMap( Property::Map& map ) const
 
   map.Insert( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mAnimationData.stopBehavior );
   map.Insert( Toolkit::DevelImageVisual::Property::LOOPING_MODE, mAnimationData.loopingMode );
+  map.Insert( Toolkit::DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, mRedrawInScalingDown );
 
   Property::Map layerInfo;
   mVectorAnimationTask->GetLayerInfo( layerInfo );
@@ -211,6 +213,10 @@ void AnimatedVectorImageVisual::DoSetProperties( const Property::Map& propertyMa
        {
           DoSetProperty( Toolkit::DevelImageVisual::Property::LOOPING_MODE, keyValue.second );
        }
+       else if( keyValue.first == REDRAW_IN_SCALING_DOWN_NAME )
+       {
+          DoSetProperty( Toolkit::DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN, keyValue.second );
+       }
     }
   }
 
@@ -261,6 +267,15 @@ void AnimatedVectorImageVisual::DoSetProperty( Property::Index index, const Prop
       }
       break;
     }
+    case Toolkit::DevelImageVisual::Property::REDRAW_IN_SCALING_DOWN:
+    {
+      bool redraw;
+      if( value.Get( redraw ) )
+      {
+        mRedrawInScalingDown = redraw;
+      }
+      break;
+    }
   }
 }
 
@@ -531,15 +546,19 @@ void AnimatedVectorImageVisual::OnScaleNotification( PropertyNotification& sourc
   if( actor )
   {
     Vector3 scale = actor.GetProperty< Vector3 >( Actor::Property::WORLD_SCALE );
-    mVisualScale.width = scale.width;
-    mVisualScale.height = scale.height;
 
-    DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnScaleNotification: scale = %f, %f [%p]\n", mVisualScale.width, mVisualScale.height, this );
+    if(mRedrawInScalingDown || scale.width >= 1.0f || scale.height >= 1.0f)
+    {
+      mVisualScale.width = scale.width;
+      mVisualScale.height = scale.height;
 
-    SetVectorImageSize();
-    SendAnimationData();
+      DALI_LOG_INFO( gVectorAnimationLogFilter, Debug::Verbose, "AnimatedVectorImageVisual::OnScaleNotification: scale = %f, %f [%p]\n", mVisualScale.width, mVisualScale.height, this );
 
-    Stage::GetCurrent().KeepRendering( 0.0f );  // Trigger event processing
+      SetVectorImageSize();
+      SendAnimationData();
+
+      Stage::GetCurrent().KeepRendering( 0.0f );  // Trigger event processing
+    }
   }
 }
 
index 6196e39..5bf0f4e 100644 (file)
@@ -229,6 +229,7 @@ private:
   CallbackBase*                                mEventCallback;    // Not owned
   bool                                         mRendererAdded;
   bool                                         mCoreShutdown;
+  bool                                         mRedrawInScalingDown;
 };
 
 } // namespace Internal
index edebccf..68dc04a 100644 (file)
@@ -187,10 +187,12 @@ ColorVisualPtr ColorVisual::New( VisualFactoryCache& factoryCache, const Propert
   return colorVisualPtr;
 }
 
-ColorVisual::ColorVisual( VisualFactoryCache& factoryCache )
-: Visual::Base( factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::COLOR ),
-  mBlurRadius( 0.0f ),
-  mRenderIfTransparent( false )
+ColorVisual::ColorVisual(VisualFactoryCache& factoryCache)
+: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::COLOR),
+  mBlurRadius(0.0f),
+  mBlurRadiusIndex(Property::INVALID_INDEX),
+  mRenderIfTransparent(false),
+  mNeedBlurRadius(false)
 {
 }
 
@@ -259,13 +261,36 @@ void ColorVisual::DoSetOnScene( Actor& actor )
   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
 }
 
+void ColorVisual::DoSetOffScene(Actor& actor)
+{
+  if(mImpl->mRenderer && mBlurRadiusIndex != Property::INVALID_INDEX)
+  {
+    // Update values from Renderer
+    mBlurRadius = mImpl->mRenderer.GetProperty<float>(mBlurRadiusIndex);
+  }
+
+  actor.RemoveRenderer(mImpl->mRenderer);
+  mImpl->mRenderer.Reset();
+  mBlurRadiusIndex = Property::INVALID_INDEX;
+}
+
 void ColorVisual::DoCreatePropertyMap( Property::Map& map ) const
 {
   map.Clear();
   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::COLOR );
   map.Insert( Toolkit::ColorVisual::Property::MIX_COLOR, mImpl->mMixColor );
   map.Insert( Toolkit::DevelColorVisual::Property::RENDER_IF_TRANSPARENT, mRenderIfTransparent );
-  map.Insert( Toolkit::DevelColorVisual::Property::BLUR_RADIUS, mBlurRadius );
+
+  if(mImpl->mRenderer && mBlurRadiusIndex != Property::INVALID_INDEX)
+  {
+    // Update values from Renderer
+    float blurRadius = mImpl->mRenderer.GetProperty<float>(mBlurRadiusIndex);
+    map.Insert(Toolkit::DevelColorVisual::Property::BLUR_RADIUS, blurRadius);
+  }
+  else
+  {
+    map.Insert(Toolkit::DevelColorVisual::Property::BLUR_RADIUS, mBlurRadius);
+  }
 }
 
 void ColorVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
@@ -273,7 +298,6 @@ void ColorVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
   // Do nothing
 }
 
-
 void ColorVisual::OnSetTransform()
 {
   if( mImpl->mRenderer )
@@ -299,12 +323,42 @@ void ColorVisual::OnDoAction( const Property::Index actionId, const Property::Va
   }
 }
 
+void ColorVisual::UpdateShader()
+{
+  if(mImpl->mRenderer)
+  {
+    Shader shader = GetShader();
+    mImpl->mRenderer.SetShader(shader);
+  }
+}
+
 void ColorVisual::InitializeRenderer()
 {
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
 
+  Shader shader = GetShader();
+
+  mImpl->mRenderer = Renderer::New(geometry, shader);
+
+  // ColorVisual has it's own index key for mix color - use this instead
+  // of using the new base index to avoid changing existing applications
+  // String keys will get to this property.
+  mImpl->mMixColorIndex = mImpl->mRenderer.RegisterProperty(Toolkit::ColorVisual::Property::MIX_COLOR, MIX_COLOR, Vector3(mImpl->mMixColor));
+
+  if(!EqualsZero(mBlurRadius))
+  {
+    mBlurRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelColorVisual::Property::BLUR_RADIUS, BLUR_RADIUS_NAME, mBlurRadius);
+    mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+  }
+
+  // Register transform properties
+  mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
+}
+
+Shader ColorVisual::GetShader()
+{
   Shader shader;
-  if( !EqualsZero( mBlurRadius ) )
+  if(!EqualsZero(mBlurRadius) || mNeedBlurRadius)
   {
     shader = mFactoryCache.GetShader( VisualFactoryCache::COLOR_SHADER_BLUR_EDGE );
     if( !shader )
@@ -332,22 +386,33 @@ void ColorVisual::InitializeRenderer()
     }
   }
 
-  mImpl->mRenderer = Renderer::New( geometry, shader );
-
-  // ColorVisual has it's own index key for mix color - use this instead
-  // of using the new base index to avoid changing existing applications
-  // String keys will get to this property.
-  mImpl->mMixColorIndex = mImpl->mRenderer.RegisterProperty( Toolkit::ColorVisual::Property::MIX_COLOR, MIX_COLOR, Vector3(mImpl->mMixColor) );
+  return shader;
+}
 
-  mImpl->mRenderer.RegisterProperty( BLUR_RADIUS_NAME, mBlurRadius );
+Dali::Property ColorVisual::OnGetPropertyObject(Dali::Property::Key key)
+{
+  if(!mImpl->mRenderer)
+  {
+    Handle handle;
+    return Dali::Property(handle, Property::INVALID_INDEX);
+  }
 
-  if( !EqualsZero( mBlurRadius ) )
+  if((key.type == Property::Key::INDEX && key.indexKey == DevelColorVisual::Property::BLUR_RADIUS) || (key.type == Property::Key::STRING && key.stringKey == BLUR_RADIUS_NAME))
   {
-    mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
+    mBlurRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelColorVisual::Property::BLUR_RADIUS, BLUR_RADIUS_NAME, mBlurRadius);
+
+    mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+    mNeedBlurRadius = true;
+
+    // Change shader
+    UpdateShader();
+
+    return Dali::Property(mImpl->mRenderer, mBlurRadiusIndex);
   }
 
-  // Register transform properties
-  mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
+  Handle handle;
+  return Dali::Property(handle, Property::INVALID_INDEX);
 }
 
 } // namespace Internal
index d5b2a6f..bf28d25 100644 (file)
@@ -95,6 +95,11 @@ protected:
   void DoSetOnScene( Actor& actor ) override;
 
   /**
+   * @copydoc Visual::Base::DoSetOffScene
+   */
+  void DoSetOffScene(Actor& actor) override;
+
+  /**
    * @copydoc Visual::Base::OnSetTransform
    */
   void OnSetTransform() override;
@@ -104,12 +109,28 @@ protected:
    */
   void OnDoAction( const Property::Index actionId, const Property::Value& attributes ) override;
 
+  /**
+   * @copydoc Visual::Base::UpdateShader
+   */
+  void UpdateShader() override;
+
+  /**
+   * @copydoc Visual::Base::OnGetPropertyObject
+   */
+  Dali::Property OnGetPropertyObject(Dali::Property::Key key) override;
+
 private:
   /**
    * @brief Initialize the renderer with the geometry and shader from the cache, if not available, create and save to the cache for sharing.
    */
   void InitializeRenderer();
 
+  /**
+   * @brief Get a shader for the current properties.
+   * @return The shader for the current properties.
+   */
+  Shader GetShader();
+
 private:
 
   // Undefined
@@ -119,9 +140,10 @@ private:
   ColorVisual& operator=( const ColorVisual& colorRenderer );
 
 private:
-
-  float mBlurRadius;         ///< The blur radius
-  bool mRenderIfTransparent; ///< Whether we should render even if the mix-color is transparent.
+  float           mBlurRadius;          ///< The blur radius
+  Property::Index mBlurRadiusIndex;     ///< The blur radius property index
+  bool            mRenderIfTransparent; ///< Whether we should render even if the mix-color is transparent.
+  bool            mNeedBlurRadius;      ///< Whether we need the blur radius in shader.
 };
 
 } // namespace Internal
index d0807df..b86f9a7 100644 (file)
@@ -385,6 +385,15 @@ void GradientVisual::DoSetOnScene( Actor& actor )
   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
 }
 
+void GradientVisual::UpdateShader()
+{
+  if(mImpl->mRenderer)
+  {
+    Shader shader = GetShader();
+    mImpl->mRenderer.SetShader(shader);
+  }
+}
+
 void GradientVisual::DoCreatePropertyMap( Property::Map& map ) const
 {
   map.Clear();
@@ -436,16 +445,7 @@ void GradientVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
 void GradientVisual::InitializeRenderer()
 {
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-
-  Toolkit::GradientVisual::Units::Type gradientUnits = mGradient->GetGradientUnits();
-  int roundedCorner = IsRoundedCornerRequired() ? 1 : 0;
-  VisualFactoryCache::ShaderType shaderType = SHADER_TYPE_TABLE[mGradientType][gradientUnits + roundedCorner * 2];
-  Shader shader = mFactoryCache.GetShader( shaderType );
-  if( !shader )
-  {
-    shader = Shader::New( VERTEX_SHADER[gradientUnits + roundedCorner * 2], FRAGMENT_SHADER[ mGradientType + roundedCorner * 2 ] );
-    mFactoryCache.SaveShader( shaderType, shader );
-  }
+  Shader   shader   = GetShader();
 
   //Set up the texture set
   TextureSet textureSet = TextureSet::New();
@@ -554,6 +554,21 @@ bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propert
   return true;
 }
 
+Shader GradientVisual::GetShader()
+{
+  Toolkit::GradientVisual::Units::Type gradientUnits = mGradient->GetGradientUnits();
+  int                                  roundedCorner = IsRoundedCornerRequired() ? 1 : 0;
+  VisualFactoryCache::ShaderType       shaderType    = SHADER_TYPE_TABLE[mGradientType][gradientUnits + roundedCorner * 2];
+  Shader                               shader        = mFactoryCache.GetShader(shaderType);
+  if(!shader)
+  {
+    shader = Shader::New(VERTEX_SHADER[gradientUnits + roundedCorner * 2], FRAGMENT_SHADER[mGradientType + roundedCorner * 2]);
+    mFactoryCache.SaveShader(shaderType, shader);
+  }
+
+  return shader;
+}
+
 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
 {
 
index 70cdb4e..71ae1d6 100644 (file)
@@ -134,6 +134,11 @@ protected:
    */
   void DoSetOnScene( Actor& actor ) override;
 
+  /**
+   * @copydoc Visual::Base::UpdateShader
+   */
+  void UpdateShader() override;
+
 private:
 
   /**
@@ -149,6 +154,12 @@ private:
   bool NewGradient(Type gradientType, const Property::Map& propertyMap);
 
   /**
+   * @brief Get a shader for the current properties.
+   * @return The shader for the current properties.
+   */
+  Shader GetShader();
+
+  /**
    * Get the stop-offsets from the property.
    * The valid property type are ARRAY, VECTOR2, VECTOR3, VECTOR4.
    *
index ec130e2..22a54e6 100644 (file)
@@ -513,7 +513,6 @@ void ImageVisual::GetNaturalSize( Vector2& naturalSize )
 void ImageVisual::CreateRenderer( TextureSet& textureSet )
 {
   Geometry geometry;
-  Shader shader;
 
   // Get the geometry
   if( mImpl->mCustomShader )
@@ -534,65 +533,7 @@ void ImageVisual::CreateRenderer( TextureSet& textureSet )
     }
   }
 
-  std::string vertexShader;
-  bool usesWholeTexture = true;
-  if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
-  {
-    vertexShader = mImpl->mCustomShader->mVertexShader;
-    usesWholeTexture = false; // Impossible to tell.
-  }
-  else
-  {
-    vertexShader = mImageVisualShaderFactory.GetVertexShaderSource().data();
-  }
-
-  std::string fragmentShader;
-  if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
-  {
-    fragmentShader = mImpl->mCustomShader->mFragmentShader;
-  }
-  else
-  {
-    fragmentShader = mImageVisualShaderFactory.GetFragmentShaderSource().data();
-  }
-
-  // If the texture is native, we may need to change prefix and sampler in
-  // the fragment shader
-  bool modifiedFragmentShader = false;
-  if(mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)))
-  {
-    Texture nativeTexture = mTextures.GetTexture(0);
-    modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShader);
-  }
-
-  const bool useStandardShader = !mImpl->mCustomShader && !modifiedFragmentShader;
-  if(useStandardShader)
-  {
-    // Create and cache the standard shader
-    shader = mImageVisualShaderFactory.GetShader(
-      mFactoryCache,
-      mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
-      mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE,
-      IsRoundedCornerRequired() );
-  }
-  else if(mImpl->mCustomShader)
-  {
-    shader = Shader::New(vertexShader, fragmentShader, mImpl->mCustomShader->mHints);
-  }
-  else
-  {
-    shader = Shader::New(vertexShader, fragmentShader);
-  }
-
-  if(usesWholeTexture)
-  {
-    shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
-  }
-
-  // Set pixel align off as default.
-  // ToDo: Pixel align causes issues such as rattling image animation.
-  // We should trun it off until issues are resolved
-  shader.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
+  Shader shader = GetShader();
 
   // Create the renderer
   mImpl->mRenderer = Renderer::New( geometry, shader );
@@ -828,6 +769,15 @@ bool ImageVisual::IsResourceReady() const
            mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED );
 }
 
+void ImageVisual::UpdateShader()
+{
+  if(mImpl->mRenderer)
+  {
+    Shader shader = GetShader();
+    mImpl->mRenderer.SetShader(shader);
+  }
+}
+
 // From existing atlas manager
 void ImageVisual::UploadCompleted()
 {
@@ -962,6 +912,73 @@ void ImageVisual::RemoveTexture()
   }
 }
 
+Shader ImageVisual::GetShader()
+{
+  Shader shader;
+
+  std::string vertexShader;
+  bool        usesWholeTexture = true;
+  if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
+  {
+    vertexShader     = mImpl->mCustomShader->mVertexShader;
+    usesWholeTexture = false; // Impossible to tell.
+  }
+  else
+  {
+    vertexShader = mImageVisualShaderFactory.GetVertexShaderSource().data();
+  }
+
+  std::string fragmentShader;
+  if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
+  {
+    fragmentShader = mImpl->mCustomShader->mFragmentShader;
+  }
+  else
+  {
+    fragmentShader = mImageVisualShaderFactory.GetFragmentShaderSource().data();
+  }
+
+  // If the texture is native, we may need to change prefix and sampler in
+  // the fragment shader
+  bool modifiedFragmentShader = false;
+  if(mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)))
+  {
+    Texture nativeTexture  = mTextures.GetTexture(0);
+    modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShader);
+  }
+
+  const bool useStandardShader = !mImpl->mCustomShader && !modifiedFragmentShader;
+  if(useStandardShader)
+  {
+    // Create and cache the standard shader
+    shader = mImageVisualShaderFactory.GetShader(
+      mFactoryCache,
+      mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
+      mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE,
+      IsRoundedCornerRequired());
+  }
+  else if(mImpl->mCustomShader)
+  {
+    shader = Shader::New(vertexShader, fragmentShader, mImpl->mCustomShader->mHints);
+  }
+  else
+  {
+    shader = Shader::New(vertexShader, fragmentShader);
+  }
+
+  if(usesWholeTexture)
+  {
+    shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
+  }
+
+  // Set pixel align off as default.
+  // ToDo: Pixel align causes issues such as rattling image animation.
+  // We should trun it off until issues are resolved
+  shader.RegisterProperty(PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF);
+
+  return shader;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index f52bd69..9ae50df 100644 (file)
@@ -229,6 +229,11 @@ protected:
    */
   bool IsResourceReady() const override;
 
+  /**
+   * @copydoc Visual::Base::UpdateShader
+   */
+  void UpdateShader() override;
+
 public:
 
   /**
@@ -318,6 +323,12 @@ private:
    */
   void DoSetProperty( Property::Index index, const Property::Value& value );
 
+  /**
+   * @brief Get a shader for the current properties.
+   * @return The shader for the current properties.
+   */
+  Shader GetShader();
+
 private:
 
   Vector4 mPixelArea;
diff --git a/dali-toolkit/internal/visuals/npatch-data.cpp b/dali-toolkit/internal/visuals/npatch-data.cpp
new file mode 100644 (file)
index 0000000..1097395
--- /dev/null
@@ -0,0 +1,251 @@
+ /*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/npatch-data.h>
+
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/rendering-addon.h>
+
+// EXTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+NPatchData::NPatchData()
+: mId(INVALID_NPATCH_DATA_ID),
+  mUrl(),
+  mTextureSet(),
+  mHash(0),
+  mCroppedWidth(0),
+  mCroppedHeight(0),
+  mBorder(0, 0, 0, 0),
+  mLoadingState(LoadingState::LOADING),
+  mRenderingMap{nullptr}
+{
+}
+
+NPatchData::~NPatchData()
+{
+  // If there is an opacity map, it has to be destroyed using addon call
+  if( mRenderingMap )
+  {
+    RenderingAddOn::Get().DestroyNPatch( mRenderingMap );
+  }
+}
+
+void NPatchData::SetId(const NPatchDataId id)
+{
+  mId = id;
+}
+
+NPatchData::NPatchDataId NPatchData::GetId() const
+{
+  return mId;
+}
+
+void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
+{
+  mObserverList.PushBack( textureObserver );
+}
+
+void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver)
+{
+  for(uint32_t index = 0; index < mObserverList.Count(); ++index )
+  {
+    if(textureObserver == mObserverList[index])
+    {
+      mObserverList.Erase( mObserverList.begin() + index );
+      break;
+    }
+  }
+}
+
+uint32_t NPatchData::GetObserverCount() const
+{
+  return mObserverList.Count();
+}
+
+void NPatchData::SetUrl(const std::string url)
+{
+  mUrl = url;
+}
+
+std::string NPatchData::GetUrl() const
+{
+  return mUrl;
+}
+
+void NPatchData::SetTextures(const TextureSet textureSet)
+{
+  mTextureSet = textureSet;
+}
+
+TextureSet NPatchData::GetTextures() const
+{
+  return mTextureSet;
+}
+
+void NPatchData::SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX)
+{
+  mStretchPixelsX = stretchPixelsX;
+}
+
+void NPatchData::SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY)
+{
+  mStretchPixelsY = stretchPixelsY;
+}
+
+NPatchUtility::StretchRanges NPatchData::GetStretchPixelsX() const
+{
+  return mStretchPixelsX;
+}
+
+NPatchUtility::StretchRanges NPatchData::GetStretchPixelsY() const
+{
+  return mStretchPixelsY;
+}
+
+void NPatchData::SetHash(std::size_t hash)
+{
+  mHash = hash;
+}
+
+std::size_t NPatchData::GetHash() const
+{
+  return mHash;
+}
+
+void NPatchData::SetCroppedWidth(uint32_t croppedWidth)
+{
+  mCroppedWidth = croppedWidth;
+}
+
+void NPatchData::SetCroppedHeight(uint32_t croppedHeight)
+{
+  mCroppedHeight = croppedHeight;
+}
+
+uint32_t NPatchData::GetCroppedWidth() const
+{
+  return mCroppedWidth;
+}
+
+uint32_t NPatchData::GetCroppedHeight() const
+{
+  return mCroppedHeight;
+}
+
+void NPatchData::SetBorder(const Rect<int> border)
+{
+  mBorder = border;
+}
+
+Rect<int> NPatchData::GetBorder() const
+{
+  return mBorder;
+}
+
+void NPatchData::SetPreMultiplyOnLoad(bool preMultiplyOnLoad)
+{
+  mPreMultiplyOnLoad = preMultiplyOnLoad;
+}
+
+bool NPatchData::IsPreMultiplied() const
+{
+  return mPreMultiplyOnLoad;
+}
+
+void NPatchData::SetLoadingState(const LoadingState loadingState)
+{
+  mLoadingState = loadingState;
+}
+
+NPatchData::LoadingState NPatchData::GetLoadingState() const
+{
+  return mLoadingState;
+}
+
+void* NPatchData::GetRenderingMap() const
+{
+  return mRenderingMap;
+}
+
+void NPatchData::SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied )
+{
+  if( mBorder == Rect< int >( 0, 0, 0, 0 ) )
+  {
+    NPatchUtility::ParseBorders( pixelBuffer, mStretchPixelsX, mStretchPixelsY );
+
+    // Crop the image
+    pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
+  }
+  else
+  {
+    mStretchPixelsX.PushBack( Uint16Pair( mBorder.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( mBorder.right )) ? pixelBuffer.GetWidth() - mBorder.right : 0 ) ) );
+    mStretchPixelsY.PushBack( Uint16Pair( mBorder.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( mBorder.bottom )) ? pixelBuffer.GetHeight() - mBorder.bottom : 0 ) ) );
+  }
+
+  mCroppedWidth = pixelBuffer.GetWidth();
+  mCroppedHeight = pixelBuffer.GetHeight();
+
+  // Create opacity map
+  mRenderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, this) : nullptr;
+
+  PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
+
+  Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
+  texture.Upload( pixels );
+
+  mTextureSet = TextureSet::New();
+  mTextureSet.SetTexture( 0u, texture );
+
+  mPreMultiplyOnLoad = preMultiplied;
+
+  mLoadingState = LoadingState::LOAD_COMPLETE;
+}
+
+void NPatchData::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
+{
+  if(loadSuccess)
+  {
+    SetLoadedNPatchData( pixelBuffer, preMultiplied );
+  }
+  else
+  {
+    mLoadingState = LoadingState::LOAD_FAILED;
+  }
+
+  for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+  {
+    TextureUploadObserver* observer = mObserverList[index];
+    observer->UploadComplete(loadSuccess, TextureManager::INVALID_TEXTURE_ID, mTextureSet, false, Vector4(), preMultiplied);
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/npatch-data.h b/dali-toolkit/internal/visuals/npatch-data.h
new file mode 100644 (file)
index 0000000..60e6e6b
--- /dev/null
@@ -0,0 +1,303 @@
+#ifndef DALI_TOOLKIT_NPATCH_DATA_H
+#define DALI_TOOLKIT_NPATCH_DATA_H
+
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <string>
+#include <dali/public-api/rendering/texture-set.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/devel-api/utility/npatch-utilities.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class NPatchData : public Dali::Toolkit::TextureUploadObserver
+{
+public:
+  typedef int32_t NPatchDataId;                 ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data.
+  static const int INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error
+
+  /**
+   * @brief Loading State of the NPatch image.
+   */
+  enum class LoadingState
+  {
+    LOADING = 0,   ///< NPatch is on loading.
+    LOAD_COMPLETE, ///< NPatch loading is completed successfully.
+    LOAD_FAILED    ///< NPatch loading is failed.
+  };
+
+public:
+
+  /**
+   * Constructor
+   */
+  NPatchData();
+
+  /**
+   * Destructor, non-virtual as not a base class
+   */
+  ~NPatchData();
+
+public:
+
+  /**
+   * @brief Set cache data id.
+   *
+   * @param [in] id cache data id
+   */
+  void SetId(const NPatchDataId id);
+
+  /**
+   * @brief Retrieve cache data id
+   *
+   * @return cache data id.
+   */
+  NPatchDataId GetId() const;
+
+  /**
+   * @brief Add TextureUploadObserver that uses the image of this cache data.
+   *
+   * @param [in] textureObserver the TextureUploadObserver that uses the image of this cache data.
+   */
+  void AddObserver(TextureUploadObserver* textureObserver);
+
+  /**
+   * @brief Remove TextureUploadObserver.
+   *
+   * @param [in] textureObserver the TextureUploadObserver that will be removed in this cache data.
+   */
+  void RemoveObserver(TextureUploadObserver* textureObserver);
+
+  /**
+   * @brief Retrieve the number of observer.
+   *
+   * @return Return the number of observer.
+   */
+  uint32_t GetObserverCount() const;
+
+  /**
+   * @brief Set NPatch image url.
+   *
+   * @param [in] url NPatch image url
+   */
+  void SetUrl(const std::string url);
+
+  /**
+   * @brief Retrieve the image url.
+   *
+   * @return Return the image url.
+   */
+  std::string GetUrl() const;
+
+  /**
+   * @brief Set texture set on the cache data
+   *
+   * @param [in] textureSet loaded texture set
+   */
+  void SetTextures(const TextureSet textureSet);
+
+  /**
+   * @brief Retrieve loaded texture set.
+   *
+   * @return Return loaded texture set.
+   */
+  TextureSet GetTextures() const;
+
+  /**
+   * @brief Set X directional stretchPixels
+   *
+   * @param [in] stretchPixelsX stretchPixels for X direction
+   */
+  void SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX);
+
+  /**
+   * @brief Set Y directional stretchPixels
+   *
+   * @param [in] stretchPixelsY stretchPixels for Y direction
+   */
+  void SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY);
+
+  /**
+   * @brief Retrieve stretchPixels for X direction.
+   *
+   * @return Return stretchPixels for X direction.
+   */
+  NPatchUtility::StretchRanges GetStretchPixelsX() const;
+
+  /**
+   * @brief Retrieve stretchPixels for Y direction.
+   *
+   * @return Return stretchPixels for Y direction.
+   */
+  NPatchUtility::StretchRanges GetStretchPixelsY() const;
+
+  /**
+   * @brief Set cache data hash.
+   *
+   * @param [in] hash cache hash
+   */
+  void SetHash(std::size_t hash);
+
+  /**
+   * @brief Retrieve hash value of the cache.
+   *
+   * @return Return hash value of the cache.
+   */
+  std::size_t GetHash() const;
+
+  /**
+   * @brief Set croppedWidth of NPatch
+   *
+   * @param [in] croppedWidth croppedWidth of NPatch
+   */
+  void SetCroppedWidth(uint32_t croppedWidth);
+
+  /**
+   * @brief Set croppedHeight of NPatch
+   *
+   * @param [in] croppedHeight croppedHeight of NPatch
+   */
+  void SetCroppedHeight(uint32_t croppedHeight);
+
+  /**
+   * @brief Retrieve croppedWidth of NPatch.
+   *
+   * @return Return croppedWidth of NPatch.
+   */
+  uint32_t GetCroppedWidth() const;
+
+  /**
+   * @brief Retrieve croppedHeight of NPatch.
+   *
+   * @return Return croppedHeight of NPatch.
+   */
+  uint32_t GetCroppedHeight() const;
+
+  /**
+   * @brief Set border of NPatch.
+   *
+   * @param [in] border border of NPatch
+   */
+  void SetBorder(const Rect<int> border);
+
+  /**
+   * @brief Retrieve border of NPatch.
+   *
+   * @return Return border of NPatch.
+   */
+  Rect<int> GetBorder() const;
+
+  /**
+   * @brief Set whether the loaded image is premultiplied or not
+   *
+   * @param [in] preMultiplyOnLoad whether the loaded image is premultiplied or not
+   */
+  void SetPreMultiplyOnLoad(bool preMultiplyOnLoad);
+
+  /**
+   * @brief Retrieve whether the loaded image is premultiplied or not.
+   *
+   * @return Return true if the image is premultiplied alpha.
+   */
+  bool IsPreMultiplied() const;
+
+  /**
+   * @brief Set current loading state.
+   *
+   * @param [in] loadingState current loading state
+   */
+  void SetLoadingState(const LoadingState loadingState);
+
+  /**
+   * @brief Retrieve current loading state.
+   *
+   * @return Return current loading state.
+   */
+  LoadingState GetLoadingState() const;
+
+
+  /**
+   * @brief Retrieve NPatch rendering data.
+   *
+   * @return Return NPatch rendering data.
+   */
+  void* GetRenderingMap() const;
+
+  /**
+   * @brief Set loaded pixel buffer for the cache data.
+   *
+   * @param [in] pixelBuffer loaded pixel buffer.
+   * @param [in] preMultiplied whether the loaded image is premultiplied or not
+   */
+  void SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied );
+
+private:
+
+  /**
+   * @copydoc TextureUploadObserver::UploadCompleted
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override {}
+
+  /**
+   * @copydoc TextureUploadObserver::LoadComplete
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override;
+
+
+private:
+
+  using ObserverListType = Dali::Vector<TextureUploadObserver*>;
+
+  NPatchDataId                 mId;
+  ObserverListType             mObserverList;      ///< Container used to store all observer clients of this Texture
+  std::string                  mUrl;               ///< Url of the N-Patch
+  TextureSet                   mTextureSet;        ///< Texture containing the cropped image
+  NPatchUtility::StretchRanges mStretchPixelsX;    ///< X stretch pixels
+  NPatchUtility::StretchRanges mStretchPixelsY;    ///< Y stretch pixels
+  std::size_t                  mHash;              ///< Hash code for the Url
+  uint32_t                     mCroppedWidth;      ///< Width of the cropped middle part of N-patch
+  uint32_t                     mCroppedHeight;     ///< Height of the cropped middle part of N-patch
+  Rect<int>                    mBorder;            ///< The size of the border
+  LoadingState                 mLoadingState;      ///< True if the data loading is completed
+  bool                         mPreMultiplyOnLoad; ///< Whether to multiply alpha into color channels on load
+  void*                        mRenderingMap;      ///< NPatch rendering data
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_NPATCH_DATA_H
index 2550001..f5d92ab 100644 (file)
@@ -34,179 +34,160 @@ namespace Toolkit
 namespace Internal
 {
 
-namespace NPatchBuffer
+namespace
 {
 
-void SetLoadedNPatchData( NPatchLoader::Data* data, Devel::PixelBuffer& pixelBuffer )
-{
-  if( data->border == Rect< int >( 0, 0, 0, 0 ) )
-  {
-    NPatchUtility::ParseBorders( pixelBuffer, data->stretchPixelsX, data->stretchPixelsY );
-
-    // Crop the image
-    pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
-  }
-  else
-  {
-    data->stretchPixelsX.PushBack( Uint16Pair( data->border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( data->border.right )) ? pixelBuffer.GetWidth() - data->border.right : 0 ) ) );
-    data->stretchPixelsY.PushBack( Uint16Pair( data->border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( data->border.bottom )) ? pixelBuffer.GetHeight() - data->border.bottom : 0 ) ) );
-  }
-
-  data->croppedWidth = pixelBuffer.GetWidth();
-  data->croppedHeight = pixelBuffer.GetHeight();
-
-  // Create opacity map
-  data->renderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, data ) : nullptr;
-
-  PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
-
-  Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
-  texture.Upload( pixels );
+constexpr auto INVALID_CACHE_INDEX = int32_t{-1}; ///< Invalid Cache index
+constexpr auto UNINITIALIZED_ID = int32_t{0}; ///< uninitialised id, use to initialize ids
 
-  data->textureSet = TextureSet::New();
-  data->textureSet.SetTexture( 0u, texture );
+} // Anonymous namespace
 
-  data->loadCompleted = true;
-}
-
-} // namespace NPatchBuffer
-
-NPatchLoader::Data::~Data()
+NPatchLoader::NPatchLoader()
+: mCurrentNPatchDataId(0)
 {
-  // If there is an opacity map, it has to be destroyed using addon call
-  if( renderingMap )
-  {
-    RenderingAddOn::Get().DestroyNPatch( renderingMap );
-  }
 }
 
-NPatchLoader::NPatchLoader()
+NPatchLoader::~NPatchLoader()
 {
 }
 
-NPatchLoader::~NPatchLoader()
+NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
 {
+  return mCurrentNPatchDataId++;
 }
 
 std::size_t NPatchLoader::Load( TextureManager& textureManager, TextureUploadObserver* textureObserver, const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad, bool synchronousLoading )
 {
   std::size_t hash = CalculateHash( url );
-  OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID;
-  const OwnerContainer< Data* >::SizeType count = mCache.Count();
-  int cachedIndex = -1;
-  Data* data;
+  OwnerContainer< NPatchData* >::SizeType index = UNINITIALIZED_ID;
+  const OwnerContainer< NPatchData* >::SizeType count = mCache.Count();
 
   for( ; index < count; ++index )
   {
-    if( mCache[ index ]->hash == hash )
+    if( mCache[ index ]->GetHash() == hash )
     {
       // hash match, check url as well in case of hash collision
-      if( mCache[ index ]->url == url )
+      if(mCache[ index ]->GetUrl() == url)
       {
         // Use cached data
-        if( mCache[ index ]->border == border )
+        if( mCache[ index ]->GetBorder() == border )
         {
-          if( mCache[ index ]->loadCompleted )
+          if( mCache[ index ]->GetLoadingState() == NPatchData::LoadingState::LOADING )
           {
-            return index + 1u; // valid indices are from 1 onwards
+            mCache[ index ]->AddObserver( textureObserver );
           }
-          mCache[ index ]->observerList.PushBack( textureObserver );
-          data = mCache[ index ];
-          return index + 1u; // valid indices are from 1 onwards
+          return mCache[ index ]->GetId(); // valid indices are from 1 onwards
         }
         else
         {
-          if( mCache[ index ]->loadCompleted )
+          if( mCache[ index ]->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
           {
             // Same url but border is different - use the existing texture
-            Data* data = new Data();
-            data->hash = hash;
-            data->url = url;
-            data->croppedWidth = mCache[ index ]->croppedWidth;
-            data->croppedHeight = mCache[ index ]->croppedHeight;
+            NPatchData* newData = new NPatchData();
+            newData->SetId(GenerateUniqueNPatchDataId());
+            newData->SetHash(hash);
+            newData->SetUrl(url);
+            newData->SetCroppedWidth(mCache[ index ]->GetCroppedWidth());
+            newData->SetCroppedHeight(mCache[ index ]->GetCroppedHeight());
 
-            data->textureSet = mCache[ index ]->textureSet;
+            newData->SetTextures(mCache[ index ]->GetTextures());
 
             NPatchUtility::StretchRanges stretchRangesX;
-            stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) );
+            stretchRangesX.PushBack( Uint16Pair( border.left, ( (newData->GetCroppedWidth() >= static_cast< unsigned int >( border.right )) ? newData->GetCroppedHeight() - border.right : 0 ) ) );
 
             NPatchUtility::StretchRanges stretchRangesY;
-            stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) );
+            stretchRangesY.PushBack( Uint16Pair( border.top, ( (newData->GetCroppedWidth() >= static_cast< unsigned int >( border.bottom )) ? newData->GetCroppedHeight() - border.bottom : 0 ) ) );
+
+            newData->SetStretchPixelsX(stretchRangesX);
+            newData->SetStretchPixelsY(stretchRangesY);
+            newData->SetBorder(border);
 
-            data->stretchPixelsX = stretchRangesX;
-            data->stretchPixelsY = stretchRangesY;
-            data->border = border;
+            newData->SetPreMultiplyOnLoad(mCache[ index ]->IsPreMultiplied());
 
-            data->loadCompleted = mCache[ index ]->loadCompleted;
+            newData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
+            newData->AddObserver( textureObserver );
 
-            mCache.PushBack( data );
+            mCache.PushBack( newData );
 
-            return mCache.Count(); // valid ids start from 1u
+            return newData->GetId(); // valid ids start from 1u
           }
         }
       }
     }
   }
 
-  if( cachedIndex == -1 )
-  {
-    data = new Data();
-    data->loadCompleted = false;
-    data->hash = hash;
-    data->url = url;
-    data->border = border;
-
-    mCache.PushBack( data );
-
-    cachedIndex = mCache.Count();
-  }
+  // If this is new image loading, make new cache data
+  NPatchData* data;
+  data = new NPatchData();
+  data->SetId(GenerateUniqueNPatchDataId());
+  data->SetHash(hash);
+  data->SetUrl(url);
+  data->SetBorder(border);
+  data->SetPreMultiplyOnLoad(preMultiplyOnLoad);
+  data->AddObserver(textureObserver);
+  mCache.PushBack(data);
 
   auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
                                                 : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
   Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer( url, Dali::ImageDimensions(), FittingMode::DEFAULT,
                                                                    SamplingMode::BOX_THEN_LINEAR, synchronousLoading,
-                                                                   textureObserver, true, preMultiplyOnLoading );
+                                                                   data, true, preMultiplyOnLoading );
 
   if( pixelBuffer )
   {
-    NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
     preMultiplyOnLoad = ( preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ) ? true : false;
+    data->SetLoadedNPatchData( pixelBuffer, preMultiplyOnLoad );
   }
 
-  return cachedIndex;
+  return data->GetId();
 }
 
-void NPatchLoader::SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied )
+int32_t NPatchLoader::GetCacheIndexFromId( const NPatchData::NPatchDataId id )
 {
-  Data* data;
-  data = mCache[ id - 1u ];
+  const unsigned int size = mCache.Count();
 
-  // To prevent recursion.
-  // data->loadCompleted will be set true in the NPatchBuffer::SetLoadedNPatchData when the first observer called this method.
-  if( data->loadCompleted )
+  for( unsigned int i = 0; i < size; ++i )
   {
-    return;
+    if( mCache[i]->GetId() == id )
+    {
+      return i;
+    }
   }
 
-  NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
+  DALI_LOG_ERROR("Wrong NPatchDataId is used\n");
+  return INVALID_CACHE_INDEX;
+}
 
-  while( data->observerList.Count() )
+bool NPatchLoader::GetNPatchData( const NPatchData::NPatchDataId id, const NPatchData*& data )
+{
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if( cacheIndex != INVALID_CACHE_INDEX )
   {
-    TextureUploadObserver* observer = data->observerList[0];
-    observer->LoadComplete( loadSuccess, Devel::PixelBuffer(), url, preMultiplied );
-    data->observerList.Erase( data->observerList.begin() );
+    data = mCache[cacheIndex];
+    return true;
   }
+  data = nullptr;
+  return false;
 }
 
-bool NPatchLoader::GetNPatchData( std::size_t id, const Data*& data )
+void NPatchLoader::Remove( std::size_t id, TextureUploadObserver* textureObserver )
 {
-  if( ( id > UNINITIALIZED_ID )&&( id <= mCache.Count() ) )
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if( cacheIndex == INVALID_CACHE_INDEX )
   {
-    data = mCache[ id - 1u ]; // id's start from 1u
-    return true;
+    return;
+  }
+
+  NPatchData* data;
+  data = mCache[cacheIndex];
+
+  data->RemoveObserver(textureObserver);
+
+  if(data->GetObserverCount() == 0)
+  {
+      mCache.Erase( mCache.Begin() + cacheIndex );
   }
-  data = NULL;
-  return false;
 }
 
 } // namespace Internal
index a3aa464..b2b4a7c 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/npatch-data.h>
 #include <dali-toolkit/internal/visuals/texture-manager-impl.h>
 #include <dali-toolkit/devel-api/utility/npatch-utilities.h>
 
@@ -49,43 +50,6 @@ class NPatchLoader
 {
 public:
 
-  enum
-  {
-    UNINITIALIZED_ID = 0 ///< uninitialised id, use to initialize ids
-  };
-
-  struct Data
-  {
-    Data()
-    : url(),
-      textureSet(),
-      hash( 0 ),
-      croppedWidth( 0 ),
-      croppedHeight( 0 ),
-      border( 0, 0, 0, 0 ),
-      loadCompleted( false ),
-      renderingMap{ nullptr }
-    {}
-
-    ~Data();
-
-    using ObserverListType = Dali::Vector< TextureUploadObserver* >;
-
-    ObserverListType observerList;                 ///< Container used to store all observer clients of this Texture
-    std::string url;                               ///< Url of the N-Patch
-    TextureSet textureSet;                         ///< Texture containing the cropped image
-    NPatchUtility::StretchRanges stretchPixelsX;   ///< X stretch pixels
-    NPatchUtility::StretchRanges stretchPixelsY;   ///< Y stretch pixels
-    std::size_t hash;                              ///< Hash code for the Url
-    uint32_t croppedWidth;                         ///< Width of the cropped middle part of N-patch
-    uint32_t croppedHeight;                        ///< Height of the cropped middle part of N-patch
-    Rect< int > border;                            ///< The size of the border
-    bool loadCompleted;                            ///< True if the data loading is completed
-    void* renderingMap;                            ///< NPatch rendering data
-  };
-
-public:
-
   /**
    * Constructor
    */
@@ -113,21 +77,35 @@ public:
   /**
    * @brief Set loaded PixelBuffer and its information
    *
-   * @param [in] loadSuccess True if the texture load was successful (i.e. the resource is available). If false, then the resource failed to load.
    * @param [in] id cache data id
    * @param [in] pixelBuffer of loaded image
-   * @param [in] url           The url address of the loaded image.
    * @param [in] preMultiplied True if the image had pre-multiplied alpha applied
    */
-  void SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied );
+  void SetNPatchData( std::size_t id, Devel::PixelBuffer& pixelBuffer, bool preMultiplied );
 
   /**
    * @brief Retrieve N patch data matching to an id
    * @param [in] id of data
-   * @param [out] data const pointer to the data
+   * @param [out] data const pointer to the NPatchData
    * @return true if data matching to id was really found
    */
-  bool GetNPatchData( std::size_t id, const Data*& data );
+  bool GetNPatchData( const NPatchData::NPatchDataId id, const NPatchData*& data );
+
+  /**
+   * @brief Remove a texture matching id.
+   * Erase the observer from the observer list of cache.
+   * If the observer list is empty, the textureSet will be reset.
+   *
+   * @param [in] id cache data id
+   * @param [in] textureObserver The NPatchVisual that requested loading.
+   */
+  void Remove( std::size_t id, TextureUploadObserver* textureObserver );
+
+private:
+
+  NPatchData::NPatchDataId GenerateUniqueNPatchDataId();
+
+  int32_t GetCacheIndexFromId( const NPatchData::NPatchDataId id );
 
 protected:
 
@@ -143,8 +121,8 @@ protected:
 
 private:
 
-  OwnerContainer< Data* > mCache;
-
+  NPatchData::NPatchDataId mCurrentNPatchDataId;
+  OwnerContainer< NPatchData* > mCache;
 };
 
 } // name Internal
index 5c03ac2..6dcf020 100644 (file)
@@ -24,7 +24,6 @@
 #include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/public-api/visuals/visual-properties.h>
 #include <dali-toolkit/internal/visuals/npatch-loader.h>
@@ -278,15 +277,15 @@ void NPatchVisual::LoadImages()
   TextureManager& textureManager = mFactoryCache.GetTextureManager();
   bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
 
-  if( NPatchLoader::UNINITIALIZED_ID == mId && mImageUrl.IsLocalResource() )
+  if( mId == NPatchData::INVALID_NPATCH_DATA_ID && mImageUrl.IsLocalResource() )
   {
     bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
     mId = mLoader.Load( textureManager, this, mImageUrl.GetUrl(), mBorder, preMultiplyOnLoad, synchronousLoading );
 
-    const NPatchLoader::Data* data;
-    if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+    const NPatchData* data;
+    if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
     {
-      EnablePreMultipliedAlpha( preMultiplyOnLoad );
+      EnablePreMultipliedAlpha( data->IsPreMultiplied() );
     }
   }
 
@@ -306,11 +305,11 @@ void NPatchVisual::GetNaturalSize( Vector2& naturalSize )
   naturalSize.y = 0u;
 
   // load now if not already loaded
-  const NPatchLoader::Data* data;
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  const NPatchData* data;
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() != NPatchData::LoadingState::LOADING )
   {
-    naturalSize.x = data->croppedWidth;
-    naturalSize.y = data->croppedHeight;
+    naturalSize.x = data->GetCroppedWidth();
+    naturalSize.y = data->GetCroppedHeight();
   }
   else
   {
@@ -386,6 +385,12 @@ void NPatchVisual::DoSetProperties( const Property::Map& propertyMap )
       mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
     }
   }
+
+  Property::Value* releasePolicy = propertyMap.Find( Toolkit::ImageVisual::Property::RELEASE_POLICY, RELEASE_POLICY_NAME );
+  if( releasePolicy )
+  {
+    releasePolicy->Get( mReleasePolicy );
+  }
 }
 
 void NPatchVisual::DoSetOnScene( Actor& actor )
@@ -393,7 +398,7 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
   // load when first go on stage
   LoadImages();
 
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   if( mLoader.GetNPatchData( mId, data ) )
   {
     Geometry geometry = CreateGeometry();
@@ -402,11 +407,11 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
     mImpl->mRenderer = Renderer::New( geometry, shader );
 
     mPlacementActor = actor;
-    if( data->loadCompleted )
+    if( data->GetLoadingState() != NPatchData::LoadingState::LOADING )
     {
       if( RenderingAddOn::Get().IsValid() )
       {
-        RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->renderingMap );
+        RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->GetRenderingMap() );
       }
 
       ApplyTextureAndUniforms();
@@ -421,6 +426,13 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
 
 void NPatchVisual::DoSetOffScene( Actor& actor )
 {
+  if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
+  {
+    mLoader.Remove(mId, this);
+    mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
+    mId = NPatchData::INVALID_NPATCH_DATA_ID;
+  }
+
   actor.RemoveRenderer( mImpl->mRenderer );
   mImpl->mRenderer.Reset();
   mPlacementActor.Reset();
@@ -443,6 +455,7 @@ void NPatchVisual::DoCreatePropertyMap( Property::Map& map ) const
   map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
   map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly );
   map.Insert( Toolkit::ImageVisual::Property::BORDER, mBorder );
+  map.Insert( Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy );
 
   if( mAuxiliaryUrl.IsValid() )
   {
@@ -466,25 +479,31 @@ NPatchVisual::NPatchVisual( VisualFactoryCache& factoryCache )
   mLoader( factoryCache.GetNPatchLoader() ),
   mImageUrl(),
   mAuxiliaryUrl(),
-  mId( NPatchLoader::UNINITIALIZED_ID ),
+  mId(NPatchData::INVALID_NPATCH_DATA_ID),
   mBorderOnly( false ),
   mBorder(),
-  mAuxiliaryImageAlpha( 0.0f )
+  mAuxiliaryImageAlpha( 0.0f ),
+  mReleasePolicy( Toolkit::ImageVisual::ReleasePolicy::DETACHED )
 {
   EnablePreMultipliedAlpha( mFactoryCache.GetPreMultiplyOnLoad() );
 }
 
 NPatchVisual::~NPatchVisual()
 {
+  if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && ( mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER ))
+  {
+    mLoader.Remove(mId, this);
+    mId = NPatchData::INVALID_NPATCH_DATA_ID;
+  }
 }
 
 Geometry NPatchVisual::CreateGeometry()
 {
   Geometry geometry;
-  const NPatchLoader::Data* data;
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  const NPatchData* data;
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
   {
-    if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 )
+    if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
     {
       if( DALI_UNLIKELY( mBorderOnly ) )
       {
@@ -492,13 +511,13 @@ Geometry NPatchVisual::CreateGeometry()
       }
       else
       {
-        if( data->renderingMap )
+        if( data->GetRenderingMap() )
         {
           uint32_t elementCount[2];
-          geometry = RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, Uint16Pair(3, 3), elementCount );
+          geometry = RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), Uint16Pair(3, 3), elementCount );
           if( mImpl->mRenderer )
           {
-            RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->renderingMap);
+            RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
           }
         }
         else
@@ -507,10 +526,10 @@ Geometry NPatchVisual::CreateGeometry()
         }
       }
     }
-    else if( data->stretchPixelsX.Size() > 0 || data->stretchPixelsY.Size() > 0)
+    else if( data->GetStretchPixelsX().Size() > 0 || data->GetStretchPixelsY().Size() > 0)
     {
-      Uint16Pair gridSize( 2 * data->stretchPixelsX.Size() + 1,  2 * data->stretchPixelsY.Size() + 1 );
-      if( !data->renderingMap )
+      Uint16Pair gridSize( 2 * data->GetStretchPixelsX().Size() + 1,  2 * data->GetStretchPixelsY().Size() + 1 );
+      if( !data->GetRenderingMap() )
       {
         geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize );
       }
@@ -518,10 +537,10 @@ Geometry NPatchVisual::CreateGeometry()
       {
         uint32_t elementCount[2];
         geometry = !mBorderOnly ?
-                   RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, gridSize, elementCount ) : CreateBorderGeometry(gridSize );
+                   RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), gridSize, elementCount ) : CreateBorderGeometry(gridSize );
         if( mImpl->mRenderer )
         {
-          RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->renderingMap);
+          RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
         }
       }
     }
@@ -537,7 +556,7 @@ Geometry NPatchVisual::CreateGeometry()
 Shader NPatchVisual::CreateShader()
 {
   Shader shader;
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   // 0 is either no data (load failed?) or no stretch regions on image
   // for both cases we use the default shader
   NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
@@ -551,8 +570,8 @@ Shader NPatchVisual::CreateShader()
   // ask loader for the regions
   if( mLoader.GetNPatchData( mId, data ) )
   {
-    xStretchCount = data->stretchPixelsX.Count();
-    yStretchCount = data->stretchPixelsY.Count();
+    xStretchCount = data->GetStretchPixelsX().Count();
+    yStretchCount = data->GetStretchPixelsY().Count();
   }
 
   if( DALI_LIKELY( !mImpl->mCustomShader ) )
@@ -616,25 +635,25 @@ Shader NPatchVisual::CreateShader()
 
 void NPatchVisual::ApplyTextureAndUniforms()
 {
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   TextureSet textureSet;
 
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
   {
-    textureSet = data->textureSet;
+    textureSet = data->GetTextures();
 
-    if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 )
+    if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
     {
       //special case for 9 patch
-      Uint16Pair stretchX = data->stretchPixelsX[ 0 ];
-      Uint16Pair stretchY = data->stretchPixelsY[ 0 ];
+      Uint16Pair stretchX = data->GetStretchPixelsX()[ 0 ];
+      Uint16Pair stretchY = data->GetStretchPixelsY()[ 0 ];
 
       uint16_t stretchWidth = ( stretchX.GetY() >= stretchX.GetX() ) ? stretchX.GetY() - stretchX.GetX() : 0;
       uint16_t stretchHeight = ( stretchY.GetY() >= stretchY.GetX() ) ? stretchY.GetY() - stretchY.GetX() : 0;
 
       mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO );
       mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
-      mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->croppedWidth - stretchWidth, data->croppedHeight - stretchHeight ) );
+      mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->GetCroppedWidth() - stretchWidth, data->GetCroppedHeight() - stretchHeight ) );
       mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
     }
     else
@@ -642,8 +661,8 @@ void NPatchVisual::ApplyTextureAndUniforms()
       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO );
       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO );
 
-      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->stretchPixelsX, data->croppedWidth );
-      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->stretchPixelsY, data->croppedHeight );
+      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->GetStretchPixelsX(), data->GetCroppedWidth() );
+      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->GetStretchPixelsY(), data->GetCroppedHeight() );
     }
   }
   else
@@ -664,10 +683,10 @@ void NPatchVisual::ApplyTextureAndUniforms()
     // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
     // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
     // on GL interpolation alone.
-    if( mAuxiliaryPixelBuffer.GetWidth() < data->croppedWidth &&
-        mAuxiliaryPixelBuffer.GetHeight() < data->croppedHeight )
+    if( mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
+        mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight() )
     {
-      mAuxiliaryPixelBuffer.Resize( data->croppedWidth, data->croppedHeight );
+      mAuxiliaryPixelBuffer.Resize( data->GetCroppedWidth(), data->GetCroppedHeight() );
     }
 
     // Note, this resets mAuxiliaryPixelBuffer handle
@@ -838,7 +857,7 @@ Geometry NPatchVisual::CreateBorderGeometry( Uint16Pair gridSize )
 
 void NPatchVisual::SetResource()
 {
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   if( mImpl->mRenderer && mLoader.GetNPatchData( mId, data ) )
   {
     Geometry geometry = CreateGeometry();
@@ -860,35 +879,32 @@ void NPatchVisual::SetResource()
   }
 }
 
+void NPatchVisual::UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied )
+{
+  EnablePreMultipliedAlpha( preMultiplied );
+  if(!loadSuccess)
+  {
+    // Image loaded and ready to display
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
+  }
+
+  if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
+  {
+    SetResource();
+  }
+}
+
 void NPatchVisual::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
 {
-  if( url.GetUrl() == mAuxiliaryUrl.GetUrl() )
+  if( loadSuccess && url.GetUrl() == mAuxiliaryUrl.GetUrl() )
   {
     mAuxiliaryPixelBuffer = pixelBuffer;
-    const NPatchLoader::Data* data;
-    if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
-    {
-      SetResource();
-    }
+    SetResource();
   }
   else
   {
-    Devel::PixelBuffer loadedPixelBuffer;
-    if( loadSuccess )
-    {
-      loadedPixelBuffer = pixelBuffer;
-      EnablePreMultipliedAlpha( preMultiplied );
-    }
-    else
-    {
-      loadedPixelBuffer = LoadImageFromFile( mFactoryCache.GetTextureManager().GetBrokenImageUrl() );
-    }
-    mLoader.SetNPatchData( loadSuccess, mId, loadedPixelBuffer, url, preMultiplied );
-
-    if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
-    {
-      SetResource();
-    }
+    // Image loaded and ready to display
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
   }
 }
 
index 41252f6..19bdaff 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali/public-api/object/weak-handle.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/internal/visuals/texture-upload-observer.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
@@ -211,7 +212,7 @@ private:
    * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
    * This callback is the place to add the renderer as it would be called once the loading is finished.
    */
-  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override {}
+  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override;
 
   /**
    * @copydoc TextureUploadObserver::LoadComplete
@@ -222,16 +223,16 @@ private:
   void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override;
 
 private:
-
-  WeakHandle<Actor>  mPlacementActor;       ///< Weakhandle to contain Actor during texture loading
-  NPatchLoader&      mLoader;               ///< reference to N patch loader for fast access
-  VisualUrl          mImageUrl;             ///< The url to the N patch to load
-  VisualUrl          mAuxiliaryUrl;         ///< An auxiliary image that can be displayed on top of the N-Patch
-  std::size_t        mId;                   ///< id of the N patch (from loader/cache)
-  Devel::PixelBuffer mAuxiliaryPixelBuffer; ///< pixel buffer of the auxiliary mask image
-  bool               mBorderOnly;           ///< if only border is desired
-  Rect<int>          mBorder;               ///< The size of the border
-  float              mAuxiliaryImageAlpha;  ///< The alpha value for the auxiliary image only
+  WeakHandle<Actor>        mPlacementActor;                 ///< Weakhandle to contain Actor during texture loading
+  NPatchLoader&            mLoader;                         ///< reference to N patch loader for fast access
+  VisualUrl                mImageUrl;                       ///< The url to the N patch to load
+  VisualUrl                mAuxiliaryUrl;                   ///< An auxiliary image that can be displayed on top of the N-Patch
+  NPatchData::NPatchDataId mId;                             ///< id of the N patch (from loader/cache)
+  Devel::PixelBuffer       mAuxiliaryPixelBuffer;           ///< pixel buffer of the auxiliary mask image
+  bool                     mBorderOnly;                     ///< if only border is desired
+  Rect<int>                mBorder;                         ///< The size of the border
+  float                    mAuxiliaryImageAlpha;            ///< The alpha value for the auxiliary image only
+  Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy; ///< The release policy to determine when an image should no longer be cached.
 };
 
 } // namespace Internal
index 2d82b41..bcde348 100644 (file)
@@ -458,16 +458,15 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
 
   TextureHash textureHash = INITIAL_CACHE_NUMBER;
   int cacheIndex = INVALID_CACHE_INDEX;
-  if(storageType != StorageType::RETURN_PIXEL_BUFFER)
+  if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage)
   {
-    textureHash = GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, isAnimatedImage, frameIndex);
+    textureHash = GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId);
 
     // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
-    cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, isAnimatedImage, frameIndex);
+    cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad);
   }
 
   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
-
   // Check if the requested Texture exists in the cache.
   if( cacheIndex != INVALID_CACHE_INDEX )
   {
@@ -1000,7 +999,6 @@ void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pix
   }
   else
   {
-    // @todo If the load was unsuccessful, upload the broken image.
     textureInfo.loadState = LoadState::LOAD_FAILED;
     CheckForWaitingTexture( textureInfo );
     NotifyObservers( textureInfo, false );
@@ -1175,9 +1173,7 @@ TextureManager::TextureHash TextureManager::GenerateHash(
   const FittingMode::Type        fittingMode,
   const Dali::SamplingMode::Type samplingMode,
   const UseAtlas                 useAtlas,
-  TextureId                      maskTextureId,
-  bool                           isAnimationImage,
-  uint32_t                       frameIndex )
+  TextureId                      maskTextureId)
 {
   std::string hashTarget( url );
   const size_t urlLength = hashTarget.length();
@@ -1222,19 +1218,6 @@ TextureManager::TextureHash TextureManager::GenerateHash(
     }
   }
 
-  if( isAnimationImage )
-  {
-    auto textureIdIndex = hashTarget.length();
-    hashTarget.resize( hashTarget.length() + sizeof( uint32_t ) );
-    char* hashTargetPtr = &( hashTarget[ textureIdIndex ] );
-
-    for( size_t byteIter = 0; byteIter < sizeof( uint32_t ); ++byteIter )
-    {
-      *hashTargetPtr++ = frameIndex & 0xff;
-      frameIndex >>= 8u;
-    }
-  }
-
   if( maskTextureId != INVALID_TEXTURE_ID )
   {
     auto textureIdIndex = hashTarget.length();
@@ -1261,9 +1244,7 @@ int TextureManager::FindCachedTexture(
   const Dali::SamplingMode::Type    samplingMode,
   const bool                        useAtlas,
   TextureId                         maskTextureId,
-  TextureManager::MultiplyOnLoad    preMultiplyOnLoad,
-  bool                              isAnimatedImage,
-  uint32_t                          frameIndex )
+  TextureManager::MultiplyOnLoad    preMultiplyOnLoad)
 {
   // Default to an invalid ID, in case we do not find a match.
   int cacheIndex = INVALID_CACHE_INDEX;
@@ -1283,9 +1264,7 @@ int TextureManager::FindCachedTexture(
           ( size == textureInfo.desiredSize ) &&
           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
             ( fittingMode == textureInfo.fittingMode &&
-              samplingMode == textureInfo.samplingMode ) ) &&
-          ( isAnimatedImage == ( ( textureInfo.animatedImageLoading ) ? true : false ) ) &&
-          ( frameIndex == textureInfo.frameIndex ) )
+              samplingMode == textureInfo.samplingMode ) ) )
       {
         // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
         // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
@@ -1402,11 +1381,6 @@ void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl)
   mBrokenImageUrl = brokenImageUrl;
 }
 
-const std::string TextureManager::GetBrokenImageUrl()
-{
-  return mBrokenImageUrl;
-}
-
 Geometry TextureManager::GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements )
 {
   return RenderingAddOn::Get().IsValid() ?
index a3da3fc..89a8ed0 100644 (file)
@@ -428,12 +428,6 @@ public:
   void SetBrokenImageUrl(const std::string& brokenImageUrl);
 
   /**
-   * @brief Get an image to be used when a visual has failed to correctly render
-   * @return Returns The broken image url.
-   */
-  const std::string GetBrokenImageUrl();
-
-  /**
    * @brief Returns the geometry associated with texture.
    * @param[in] textureId Id of the texture
    * @param[out] frontElements number of front elements
@@ -752,14 +746,12 @@ private:
    * @param[in] samplingMode     The SamplingMode to use
    * @param[in] useAtlas         True if atlased
    * @param[in] maskTextureId    The masking texture id (or INVALID_TEXTURE_ID)
-   * @param[in] isAnimatedImage  The boolean value to know whether the request is for animated image or not
-   * @param[in] frameIndex       The frame index of a frame to be loaded frame
    * @return                     A hash of the provided data for caching.
    */
   TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
                             const FittingMode::Type fittingMode,
                             const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
-                            TextureId maskTextureId, bool isAnimatedImage, uint32_t frameIndex );
+                            TextureId maskTextureId );
 
   /**
    * @brief Looks up a cached texture by its hash.
@@ -772,8 +764,6 @@ private:
    * @param[in] useAtlas          True if atlased
    * @param[in] maskTextureId     Optional texture ID to use to mask this image
    * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
-   * @param[in] isAnimatedImage   The boolean value to know whether the request is for animated image or not
-   * @param[in] frameIndex        The frame index of a frame to be loaded frame
    * @return                      A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
    */
   TextureManager::TextureId FindCachedTexture(
@@ -784,9 +774,7 @@ private:
     const Dali::SamplingMode::Type samplingMode,
     const bool useAtlas,
     TextureId maskTextureId,
-    MultiplyOnLoad preMultiplyOnLoad,
-    bool isAnimatedImage,
-    uint32_t frameIndex );
+    MultiplyOnLoad preMultiplyOnLoad);
 
 private:
 
index c7b8a14..fc962bd 100644 (file)
@@ -115,21 +115,22 @@ bool GetPolicyFromValue( const Property::Value& value, Vector2& policy )
 
 } // unnamed namespace
 
-Internal::Visual::Base::Impl::Impl( FittingMode fittingMode, Toolkit::Visual::Type type )
-: mCustomShader( NULL ),
-  mEventObserver( NULL ),
+Internal::Visual::Base::Impl::Impl(FittingMode fittingMode, Toolkit::Visual::Type type)
+: mCustomShader(NULL),
+  mEventObserver(NULL),
   mTransform(),
-  mMixColor( Color::WHITE ),
-  mControlSize( Vector2::ZERO ),
-  mCornerRadius( 0.0f ),
-  mCornerRadiusPolicy( 1.0f ),
-  mDepthIndex( 0.0f ),
-  mMixColorIndex( Property::INVALID_INDEX ),
-  mCornerRadiusIndex( Property::INVALID_INDEX ),
-  mFittingMode( fittingMode ),
-  mFlags( 0 ),
-  mResourceStatus( Toolkit::Visual::ResourceStatus::PREPARING ),
-  mType( type )
+  mMixColor(Color::WHITE),
+  mControlSize(Vector2::ZERO),
+  mCornerRadius(0.0f),
+  mCornerRadiusPolicy(1.0f),
+  mDepthIndex(0.0f),
+  mMixColorIndex(Property::INVALID_INDEX),
+  mCornerRadiusIndex(Property::INVALID_INDEX),
+  mFittingMode(fittingMode),
+  mFlags(0),
+  mResourceStatus(Toolkit::Visual::ResourceStatus::PREPARING),
+  mType(type),
+  mNeedCornerRadius(false)
 {
 }
 
index ae7a93e..a1056ac 100644 (file)
@@ -134,6 +134,7 @@ struct Base::Impl
   int             mFlags;
   Toolkit::Visual::ResourceStatus  mResourceStatus;
   const Toolkit::Visual::Type      mType;
+  bool                             mNeedCornerRadius;
 };
 
 } // namespace Visual
index aca317e..6e04f0a 100755 (executable)
@@ -323,7 +323,7 @@ void Visual::Base::SetOnScene( Actor& actor )
 
       if( IsRoundedCornerRequired() )
       {
-        mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty( CORNER_RADIUS, mImpl->mCornerRadius );
+        mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
         mImpl->mRenderer.RegisterProperty( CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy );
 
         mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
@@ -340,6 +340,17 @@ void Visual::Base::SetOffScene( Actor& actor )
 {
   if( IsOnScene() )
   {
+    if(mImpl->mRenderer)
+    {
+      // Update values from Renderer
+      mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(mImpl->mMixColorIndex);
+      mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
+      if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
+      {
+        mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<float>(mImpl->mCornerRadiusIndex);
+      }
+    }
+
     DoSetOffScene( actor );
     mImpl->mMixColorIndex = Property::INVALID_INDEX;
     mImpl->mCornerRadiusIndex = Property::INVALID_INDEX;
@@ -349,11 +360,22 @@ void Visual::Base::SetOffScene( Actor& actor )
 
 void Visual::Base::CreatePropertyMap( Property::Map& map ) const
 {
-  DoCreatePropertyMap( map );
+  if(mImpl->mRenderer)
+  {
+    // Update values from Renderer
+    mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(mImpl->mMixColorIndex);
+    mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
+    if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
+    {
+      mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<float>(mImpl->mCornerRadiusIndex);
+    }
+  }
 
-  if( mImpl->mCustomShader )
+  DoCreatePropertyMap(map);
+
+  if(mImpl->mCustomShader)
   {
-    mImpl->mCustomShader->CreatePropertyMap( map );
+    mImpl->mCustomShader->CreatePropertyMap(map);
   }
 
   Property::Map transform;
@@ -426,7 +448,12 @@ bool Visual::Base::IsOnScene() const
 
 bool Visual::Base::IsRoundedCornerRequired() const
 {
-  return !EqualsZero( mImpl->mCornerRadius );
+  if(mImpl->mRenderer && mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
+  {
+    // Update values from Renderer
+    mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<float>(mImpl->mCornerRadiusIndex);
+  }
+  return !EqualsZero(mImpl->mCornerRadius) || mImpl->mNeedCornerRadius;
 }
 
 void Visual::Base::OnDoAction( const Property::Index actionId, const Property::Value& attributes )
@@ -479,11 +506,6 @@ void Visual::Base::SetMixColor( const Vector3& color )
   }
 }
 
-const Vector4& Visual::Base::GetMixColor() const
-{
-  return mImpl->mMixColor;
-}
-
 void Visual::Base::AddEventObserver( Visual::EventObserver& observer)
 {
   mImpl->mEventObserver = &observer;
@@ -624,21 +646,12 @@ void Visual::Base::AnimateProperty(
   }
 #endif
 
-  Property::Map map;
-  DoCreatePropertyMap( map );
-  Property::Value* valuePtr = map.Find( Toolkit::Visual::Property::TYPE );
-  int visualType = -1;
-  if( valuePtr )
-  {
-    valuePtr->Get( visualType );
-  }
-
-  if( animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
-      animator.propertyKey == MIX_COLOR ||
-      ( visualType == Toolkit::Visual::COLOR &&
-        animator.propertyKey == ColorVisual::Property::MIX_COLOR ) ||
-      ( visualType == Toolkit::Visual::PRIMITIVE &&
-        animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR ) )
+  if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
+     animator.propertyKey == MIX_COLOR ||
+     (mImpl->mType == Toolkit::Visual::COLOR &&
+      animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
+     (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
+      animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
   {
     AnimateMixColorProperty( transition, animator );
   }
@@ -745,6 +758,65 @@ void Visual::Base::AnimateMixColorProperty(
   }
 }
 
+Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
+{
+  if(!mImpl->mRenderer)
+  {
+    Handle handle;
+    return Dali::Property(handle, Property::INVALID_INDEX);
+  }
+
+  // Mix color or opacity cases
+  if(key.type == Property::Key::INDEX)
+  {
+    if(key.indexKey == Toolkit::Visual::Property::MIX_COLOR || (mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) || (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR))
+    {
+      return Dali::Property(mImpl->mRenderer, mImpl->mMixColorIndex);
+    }
+    else if(key.indexKey == Toolkit::Visual::Property::OPACITY)
+    {
+      return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
+    }
+  }
+  else
+  {
+    if(key.stringKey == MIX_COLOR)
+    {
+      return Dali::Property(mImpl->mRenderer, mImpl->mMixColorIndex);
+    }
+    else if(key.stringKey == OPACITY)
+    {
+      return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
+    }
+  }
+
+  // Other cases
+  Property::Index index = GetPropertyIndex(key);
+  if(index == Property::INVALID_INDEX)
+  {
+    if((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::CORNER_RADIUS) || (key.type == Property::Key::STRING && key.stringKey == CORNER_RADIUS))
+    {
+      // Register CORNER_RADIUS property
+      mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
+      mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
+      index = mImpl->mCornerRadiusIndex;
+
+      mImpl->mNeedCornerRadius = true;
+
+      // Change shader
+      UpdateShader();
+    }
+    else
+    {
+      // We can't find the property in the base class.
+      // Request to child class
+      return OnGetPropertyObject(key);
+    }
+  }
+
+  return Dali::Property(mImpl->mRenderer, index);
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index b925e46..8b0132e 100644 (file)
@@ -203,12 +203,6 @@ public:
   void SetMixColor( const Vector3& color );
 
   /**
-   * Gets the mix color of the visual.
-   * @return The mix color
-   */
-  const Vector4& GetMixColor() const;
-
-  /**
    * Animate the property if it exists in the visual or renderer.
    *
    * If it's a visual property such as mix color or a transform property,
@@ -279,8 +273,15 @@ public:
    */
   Toolkit::Visual::Type GetType() const;
 
- protected:
+  /**
+   * @brief Retrieve the property object associated with the property key.
+   *
+   * @param[in] key The Property key of the visual.
+   * @return The Property object
+   */
+  Dali::Property GetPropertyObject(Dali::Property::Key key);
 
+protected:
   /**
    * @brief Constructor.
    *
@@ -350,6 +351,25 @@ protected:
    */
   virtual void OnDoAction( const Property::Index actionId, const Property::Value& attributes );
 
+  /**
+   * @brief Update the shader when some properties are changed.
+   */
+  virtual void UpdateShader()
+  {
+  }
+
+  /**
+   * @brief Called by GetPropertyObject() allowing sub classes to respond to the GetPropertyObject event
+   * @note The derived class is required to register the given property.
+   * @param[in] key The key of the visual's property.
+   * @return The Property object
+   */
+  virtual Dali::Property OnGetPropertyObject(Dali::Property::Key key)
+  {
+    Handle handle;
+    return Dali::Property(handle, Property::INVALID_INDEX);
+  }
+
 protected:
 
   /**
index fa1b1fd..b9b78d0 100644 (file)
@@ -120,6 +120,7 @@ const char * const IMAGE_SAMPLING_MODE( "samplingMode" );
 const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" );
 const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
 const char * const ALPHA_MASK_URL("alphaMaskUrl");
+const char * const REDRAW_IN_SCALING_DOWN_NAME("redrawInScalingDown");
 
 // Text visual
 const char * const TEXT_PROPERTY( "text" );
index f972f37..c30033a 100644 (file)
@@ -104,6 +104,7 @@ extern const char * const IMAGE_SAMPLING_MODE;
 extern const char * const IMAGE_DESIRED_WIDTH;
 extern const char * const IMAGE_DESIRED_HEIGHT;
 extern const char * const ALPHA_MASK_URL;
+extern const char * const REDRAW_IN_SCALING_DOWN_NAME;
 
 // Text visual
 extern const char * const TEXT_PROPERTY;
index 9fa27ae..44ba50a 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 3;
+const unsigned int TOOLKIT_MICRO_VERSION = 5;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index be3aead..bea661d 100644 (file)
@@ -107,15 +107,16 @@ enum
 
   /**
    * @brief Mix color is a blend color for any visual.
-   * @details Name "mixColor", type Property::VECTOR3 or Property::VECTOR4.
+   * @details Name "mixColor", type Property::VECTOR3 or Property::VECTOR4, animatable
    * @SINCE_1_2.60
    * @note Optional
+   * @note To animate an opacity, OPACITY property should be used.
    */
   MIX_COLOR,
 
   /**
    * @brief Opacity is the alpha component of the mixColor, above.
-   * @details Name "opacity", type Property::FLOAT.
+   * @details Name "opacity", type Property::FLOAT, animatable
    * @SINCE_1_2.60
    * @note Optional
    */
index fd08b0b..2e17d85 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.0.3
+Version:    2.0.5
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT