for( unsigned int i = 0u; i < 10; ++i )
{
- Actor child = view.GetChildAt( i );
+ Actor child = view.GetItem( i );
Actor newActor = Actor::New();
newActor.SetName("Replaced");
- replaceList.push_back( Item( view.GetItemId(child), newActor ) );
+ replaceList.push_back( Item( i, newActor ) );
}
if( !replaceList.empty() )
namespace
{
+typedef NinePatchImage::StretchRanges StretchRanges;
+
const char* TEST_IMAGE_FILE_NAME = "gallery_image_01.jpg";
const char* TEST_NPATCH_FILE_NAME = "gallery_image_01.9.jpg";
}
}
-void AddStretchRegionsToImage( Integration::Bitmap* image, unsigned int imageWidth, unsigned int imageHeight, const Vector4& requiredStretchBorder, Pixel::Format pixelFormat )
+void AddStretchRegionsToImage( Integration::Bitmap* image, unsigned int imageWidth, unsigned int imageHeight, const StretchRanges& stretchRangesX, const StretchRanges& stretchRangesY, Pixel::Format pixelFormat )
{
PixelBuffer* pixbuffer = image->GetBuffer();
unsigned int bytesPerPixel = GetBytesPerPixel( pixelFormat );
- for( unsigned int column = requiredStretchBorder.x; column < imageWidth - requiredStretchBorder.z; ++column )
+ for(StretchRanges::ConstIterator it = stretchRangesX.Begin(); it != stretchRangesX.End(); ++it)
{
- unsigned int pixelOffset = column * bytesPerPixel;
- pixbuffer[ pixelOffset ] = 0x00;
- pixbuffer[ pixelOffset + 1 ] = 0x00;
- pixbuffer[ pixelOffset + 2 ] = 0x00;
- pixbuffer[ pixelOffset + 3 ] = 0xFF;
+ const Uint16Pair& range = *it;
+ //since the stretch range is in the cropped image space, we need to offset by 1 to get it to the uncropped image space
+ for( unsigned int column = range.GetX() + 1u; column < range.GetY() + 1u; ++column )
+ {
+ unsigned int pixelOffset = column * bytesPerPixel;
+ pixbuffer[ pixelOffset ] = 0x00;
+ pixbuffer[ pixelOffset + 1 ] = 0x00;
+ pixbuffer[ pixelOffset + 2 ] = 0x00;
+ pixbuffer[ pixelOffset + 3 ] = 0xFF;
+ }
}
- for( unsigned int row = requiredStretchBorder.y; row < imageHeight - requiredStretchBorder.w; ++row )
+
+ for(StretchRanges::ConstIterator it = stretchRangesY.Begin(); it != stretchRangesY.End(); ++it)
{
- unsigned int pixelOffset = row * imageWidth * bytesPerPixel;
- pixbuffer[ pixelOffset ] = 0x00;
- pixbuffer[ pixelOffset + 1 ] = 0x00;
- pixbuffer[ pixelOffset + 2 ] = 0x00;
- pixbuffer[ pixelOffset + 3 ] = 0xFF;
+ const Uint16Pair& range = *it;
+ //since the stretch range is in the cropped image space, we need to offset by 1 to get it to the uncropped image space
+ for( unsigned int row = range.GetX() + 1u; row < range.GetY() + 1u; ++row )
+ {
+ unsigned int pixelOffset = row * imageWidth * bytesPerPixel;
+ pixbuffer[ pixelOffset ] = 0x00;
+ pixbuffer[ pixelOffset + 1 ] = 0x00;
+ pixbuffer[ pixelOffset + 2 ] = 0x00;
+ pixbuffer[ pixelOffset + 3 ] = 0xFF;
+ }
}
}
Integration::ResourcePointer CustomizeNinePatch( TestApplication& application,
unsigned int ninePatchImageWidth,
unsigned int ninePatchImageHeight,
- const Vector4& requiredStretchBorder,
+ const StretchRanges& stretchRangesX,
+ const StretchRanges& stretchRangesY,
bool addChildRegion = false,
Vector4 requiredChildRegion = Vector4::ZERO )
{
InitialiseRegionsToZeroAlpha( bitmap, ninePatchImageWidth, ninePatchImageHeight, pixelFormat );
tet_infoline("Add Stretch regions to Bitmap");
- AddStretchRegionsToImage( bitmap, ninePatchImageWidth, ninePatchImageHeight, requiredStretchBorder, pixelFormat );
+ AddStretchRegionsToImage( bitmap, ninePatchImageWidth, ninePatchImageHeight, stretchRangesX, stretchRangesY, pixelFormat );
if( addChildRegion )
{
return resourcePtr;
}
+void TestControlRendererRender( ToolkitTestApplication& application, Actor& actor, ControlRenderer& controlRenderer, Integration::ResourcePointer resourcePtr = Integration::ResourcePointer(), std::size_t expectedSamplers = 0)
+{
+ actor.SetSize( 200.f, 200.f );
+ Stage::GetCurrent().Add( actor );
+ controlRenderer.SetSize( Vector2(200.f, 200.f) );
+ controlRenderer.SetOnStage( actor );
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+ DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfTextures() == expectedSamplers );
+
+ application.SendNotification();
+ application.Render();
+
+ if( resourcePtr )
+ {
+ Integration::ResourceRequest* request = application.GetPlatform().GetRequest();
+ if(request)
+ {
+ application.GetPlatform().SetResourceLoaded(request->GetId(), request->GetType()->id, resourcePtr );
+ }
+ }
+
+ application.Render();
+ application.SendNotification();
+
+ if( resourcePtr )
+ {
+ DALI_TEST_CHECK(application.GetPlatform().WasCalled(TestPlatformAbstraction::LoadResourceFunc));
+ }
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+}
+
} // namespace
DALI_TEST_CHECK( controlRenderer );
Actor actor = Actor::New();
- actor.SetSize(200.f, 200.f);
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize(Vector2(200.f, 200.f));
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
- TestGlAbstraction& gl = application.GetGlAbstraction();
-
- application.SendNotification();
- application.Render(0);
+ TestControlRendererRender( application, actor, controlRenderer );
Vector4 actualValue(Vector4::ZERO);
+ TestGlAbstraction& gl = application.GetGlAbstraction();
DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "uBlendColor", actualValue ) );
DALI_TEST_EQUALS( actualValue, testColor, TEST_LOCATION );
DALI_TEST_CHECK( controlRenderer );
Actor actor = Actor::New();
- actor.SetSize(200.f, 200.f);
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize(Vector2(200.f, 200.f));
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
- TestGlAbstraction& gl = application.GetGlAbstraction();
-
- application.SendNotification();
- application.Render(0);
+ TestControlRendererRender( application, actor, controlRenderer );
Vector4 actualValue(Vector4::ZERO);
+ TestGlAbstraction& gl = application.GetGlAbstraction();
DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "uBlendColor", actualValue ) );
DALI_TEST_EQUALS( actualValue, testColor, TEST_LOCATION );
ControlRenderer controlRenderer = factory.GetControlRenderer(propertyMap);
DALI_TEST_CHECK( controlRenderer );
- Actor actor = Actor::New();
- Vector2 size(200.f, 200.f);
- actor.SetSize(size);
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetOnStage( actor );
- controlRenderer.SetSize(size);
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
// A lookup texture is generated and pass to shader as sampler
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
-
- application.SendNotification();
- application.Render(0);
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, Integration::ResourcePointer(), 1u );
controlRenderer.SetOffStage( actor );
DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
ControlRenderer controlRenderer = factory.GetControlRenderer(propertyMap);
DALI_TEST_CHECK( controlRenderer );
- Actor actor = Actor::New();
- Vector2 size(200.f, 200.f);
- actor.SetSize(size);
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize(size);
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
// A lookup texture is generated and pass to shader as sampler
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
-
- TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render(0);
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, Integration::ResourcePointer(), 1u );
Matrix3 alignMatrix( radius, 0.f, 0.f, 0.f, radius, 0.f, center.x, center.y, 1.f );
alignMatrix.Invert();
Matrix3 actualValue( Matrix3::IDENTITY );
+ TestGlAbstraction& gl = application.GetGlAbstraction();
DALI_TEST_CHECK( gl.GetUniformValue<Matrix3>( "uAlignmentMatrix", actualValue ) );
DALI_TEST_EQUALS( actualValue, alignMatrix, Math::MACHINE_EPSILON_100, TEST_LOCATION );
DALI_TEST_CHECK( controlRenderer );
Actor actor = Actor::New();
- actor.SetSize( 200.f, 200.f );
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize( Vector2(200.f, 200.f) );
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
+ TestControlRendererRender( application, actor, controlRenderer, Integration::ResourcePointer(Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD)), 1u );
TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render();
-
- Integration::ResourceRequest* request = application.GetPlatform().GetRequest();
- if(request)
- {
- application.GetPlatform().SetResourceLoaded(request->GetId(), request->GetType()->id, Integration::ResourcePointer(Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD)));
- }
-
- application.Render();
- application.SendNotification();
-
- DALI_TEST_CHECK(application.GetPlatform().WasCalled(TestPlatformAbstraction::LoadResourceFunc));
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
int textureUnit = -1;
DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
ControlRenderer controlRenderer = factory.GetControlRenderer( image );
Actor actor = Actor::New();
- actor.SetSize( 200.f, 200.f );
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize( Vector2(200.f, 200.f) );
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
+ TestControlRendererRender( application, actor, controlRenderer, Integration::ResourcePointer(Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD)), 1u );
TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render();
-
- Integration::ResourceRequest* request = application.GetPlatform().GetRequest();
- if(request)
- {
- application.GetPlatform().SetResourceLoaded(request->GetId(), request->GetType()->id, Integration::ResourcePointer(Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD)));
- }
-
- application.Render();
- application.SendNotification();
-
- DALI_TEST_CHECK(application.GetPlatform().WasCalled(TestPlatformAbstraction::LoadResourceFunc));
-
int textureUnit = -1;
DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
int UtcDaliRendererFactoryGetNPatchRenderer1(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliRendererFactoryGetNPatchRenderer1: Request n-patch renderer with a Property::Map" );
+ tet_infoline( "UtcDaliRendererFactoryGetNPatchRenderer1: Request 9-patch renderer with a Property::Map" );
RendererFactory factory = RendererFactory::Get();
DALI_TEST_CHECK( factory );
const unsigned int ninePatchImageHeight = 18;
const unsigned int ninePatchImageWidth = 28;
- const Vector4 requiredStretchBorder( 3, 4, 5, 6 );
- Integration::ResourcePointer ninePatchResource = CustomizeNinePatch( application, ninePatchImageWidth, ninePatchImageHeight, requiredStretchBorder );
+ StretchRanges stretchRangesX;
+ stretchRangesX.PushBack( Uint16Pair( 2, 3 ) );
+ StretchRanges stretchRangesY;
+ stretchRangesY.PushBack( Uint16Pair( 4, 5 ) );
+ Integration::ResourcePointer ninePatchResource = CustomizeNinePatch( application, ninePatchImageWidth, ninePatchImageHeight, stretchRangesX, stretchRangesY );
Property::Map propertyMap;
propertyMap.Insert( "renderer-type", "n-patch-renderer" );
propertyMap.Insert( "image-url", TEST_NPATCH_FILE_NAME );
+ {
+ tet_infoline( "whole grid" );
+ ControlRenderer controlRenderer = factory.GetControlRenderer( propertyMap );
+ DALI_TEST_CHECK( controlRenderer );
- ControlRenderer controlRenderer = factory.GetControlRenderer( propertyMap );
- DALI_TEST_CHECK( controlRenderer );
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, ninePatchResource, 1u );
- Actor actor = Actor::New();
- actor.SetSize( 200.f, 200.f );
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize( Vector2(200.f, 200.f) );
- controlRenderer.SetOnStage( actor );
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ int textureUnit = -1;
+ DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
+ DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
+ }
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
+ propertyMap.Insert( "border-only", true );
+ {
+ tet_infoline( "border only" );
+ ControlRenderer controlRenderer = factory.GetControlRenderer( propertyMap );
+ DALI_TEST_CHECK( controlRenderer );
- TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render();
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, ninePatchResource, 1u );
- Integration::ResourceRequest* request = application.GetPlatform().GetRequest();
- if(request)
- {
- application.GetPlatform().SetResourceLoaded(request->GetId(), request->GetType()->id, ninePatchResource );
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ int textureUnit = -1;
+ DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
+ DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
}
- application.Render();
- application.SendNotification();
+ END_TEST;
+}
- DALI_TEST_CHECK(application.GetPlatform().WasCalled(TestPlatformAbstraction::LoadResourceFunc));
+int UtcDaliRendererFactoryGetNPatchRenderer2(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliRendererFactoryGetNPatchRenderer2: Request n-patch renderer with a Property::Map" );
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+ RendererFactory factory = RendererFactory::Get();
+ DALI_TEST_CHECK( factory );
- int textureUnit = -1;
- DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
- DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
+ const unsigned int ninePatchImageWidth = 18;
+ const unsigned int ninePatchImageHeight = 28;
+ StretchRanges stretchRangesX;
+ stretchRangesX.PushBack( Uint16Pair( 2, 3 ) );
+ stretchRangesX.PushBack( Uint16Pair( 5, 7 ) );
+ stretchRangesX.PushBack( Uint16Pair( 12, 15 ) );
+ StretchRanges stretchRangesY;
+ stretchRangesY.PushBack( Uint16Pair( 4, 5 ) );
+ stretchRangesY.PushBack( Uint16Pair( 8, 12 ) );
+ stretchRangesY.PushBack( Uint16Pair( 15, 16 ) );
+ stretchRangesY.PushBack( Uint16Pair( 25, 27 ) );
+ Integration::ResourcePointer ninePatchResource = CustomizeNinePatch( application, ninePatchImageWidth, ninePatchImageHeight, stretchRangesX, stretchRangesY );
- controlRenderer.SetOffStage( actor );
- DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+ Property::Map propertyMap;
+ propertyMap.Insert( "renderer-type", "n-patch-renderer" );
+ propertyMap.Insert( "image-url", TEST_NPATCH_FILE_NAME );
+ {
+ ControlRenderer controlRenderer = factory.GetControlRenderer( propertyMap );
+ DALI_TEST_CHECK( controlRenderer );
+
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, ninePatchResource, 1u );
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ int textureUnit = -1;
+ DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
+ DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
+
+ controlRenderer.SetOffStage( actor );
+ DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+ }
+
+ propertyMap.Insert( "border-only", true );
+ {
+ tet_infoline( "border only" );
+ ControlRenderer controlRenderer = factory.GetControlRenderer( propertyMap );
+ DALI_TEST_CHECK( controlRenderer );
+
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, ninePatchResource, 1u );
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ int textureUnit = -1;
+ DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
+ DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
+
+ controlRenderer.SetOffStage( actor );
+ DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+ }
END_TEST;
}
-int UtcDaliRendererFactoryGetNPatchRenderer2(void)
+int UtcDaliRendererFactoryGetNPatchRenderer3(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliRendererFactoryGetNPatchRenderer2: Request n-patch renderer with an image url" );
+ tet_infoline( "UtcDaliRendererFactoryGetNPatchRenderer3: Request 9-patch renderer with an image url" );
RendererFactory factory = RendererFactory::Get();
DALI_TEST_CHECK( factory );
const unsigned int ninePatchImageHeight = 18;
const unsigned int ninePatchImageWidth = 28;
- const Vector4 requiredStretchBorder( 3, 4, 5, 6 );
- Integration::ResourcePointer ninePatchResource = CustomizeNinePatch( application, ninePatchImageWidth, ninePatchImageHeight, requiredStretchBorder );
+ StretchRanges stretchRangesX;
+ stretchRangesX.PushBack( Uint16Pair( 2, 3 ) );
+ StretchRanges stretchRangesY;
+ stretchRangesY.PushBack( Uint16Pair( 4, 5 ) );
+ Integration::ResourcePointer ninePatchResource = CustomizeNinePatch( application, ninePatchImageWidth, ninePatchImageHeight, stretchRangesX, stretchRangesY );
ControlRenderer controlRenderer = factory.GetControlRenderer( TEST_NPATCH_FILE_NAME );
DALI_TEST_CHECK( controlRenderer );
Actor actor = Actor::New();
- actor.SetSize( 200.f, 200.f );
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize( Vector2(200.f, 200.f) );
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
+ TestControlRendererRender( application, actor, controlRenderer, ninePatchResource, 1u );
TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render();
+ int textureUnit = -1;
+ DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
+ DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
- Integration::ResourceRequest* request = application.GetPlatform().GetRequest();
- if(request)
- {
- application.GetPlatform().SetResourceLoaded(request->GetId(), request->GetType()->id, ninePatchResource );
- }
+ END_TEST;
+}
- application.Render();
- application.SendNotification();
+int UtcDaliRendererFactoryGetNPatchRenderer4(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliRendererFactoryGetNPatchRenderer4: Request n-patch renderer with an image url" );
+
+ RendererFactory factory = RendererFactory::Get();
+ DALI_TEST_CHECK( factory );
+
+ const unsigned int ninePatchImageHeight = 18;
+ const unsigned int ninePatchImageWidth = 28;
+ StretchRanges stretchRangesX;
+ stretchRangesX.PushBack( Uint16Pair( 2, 3 ) );
+ stretchRangesX.PushBack( Uint16Pair( 5, 7 ) );
+ stretchRangesX.PushBack( Uint16Pair( 12, 15 ) );
+ StretchRanges stretchRangesY;
+ stretchRangesY.PushBack( Uint16Pair( 4, 5 ) );
+ stretchRangesY.PushBack( Uint16Pair( 8, 12 ) );
+ stretchRangesY.PushBack( Uint16Pair( 15, 16 ) );
+ stretchRangesY.PushBack( Uint16Pair( 25, 27 ) );
+ Integration::ResourcePointer ninePatchResource = CustomizeNinePatch( application, ninePatchImageWidth, ninePatchImageHeight, stretchRangesX, stretchRangesY );
+
+ ControlRenderer controlRenderer = factory.GetControlRenderer( TEST_NPATCH_FILE_NAME );
+ DALI_TEST_CHECK( controlRenderer );
- DALI_TEST_CHECK(application.GetPlatform().WasCalled(TestPlatformAbstraction::LoadResourceFunc));
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer, ninePatchResource, 1u );
+ TestGlAbstraction& gl = application.GetGlAbstraction();
int textureUnit = -1;
DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
DALI_TEST_CHECK( controlRenderer );
Actor actor = Actor::New();
- actor.SetSize( 200.f, 200.f );
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize( Vector2(200.f, 200.f) );
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
+ //The testkit still has to load a bitmap for the broken renderer image
+ TestControlRendererRender( application, actor, controlRenderer, Integration::ResourcePointer(Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD)), 1u );
TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render();
-
int textureUnit = -1;
DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
DALI_TEST_CHECK( controlRenderer );
Actor actor = Actor::New();
- actor.SetSize( 200.f, 200.f );
- Stage::GetCurrent().Add( actor );
- controlRenderer.SetSize( Vector2(200.f, 200.f) );
- controlRenderer.SetOnStage( actor );
-
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfSamplers() == 1u );
+ //The testkit still has to load a bitmap for the broken renderer image
+ TestControlRendererRender( application, actor, controlRenderer, Integration::ResourcePointer(Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD)), 1u );
TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render();
-
int textureUnit = -1;
DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
ToolkitTestApplication application;
tet_infoline( "UtcDaliRendererFactoryResetRenderer1" );
- Actor actor = Actor::New();
- actor.SetSize(200.f, 200.f);
- Stage::GetCurrent().Add( actor );
-
RendererFactory factory = RendererFactory::Get();
DALI_TEST_CHECK( factory );
ControlRenderer controlRenderer = factory.GetControlRenderer( Color::RED );
DALI_TEST_CHECK( controlRenderer );
- controlRenderer.SetSize(Vector2(200.f, 200.f));
- controlRenderer.SetOnStage( actor );
- DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
- TestGlAbstraction& gl = application.GetGlAbstraction();
- application.SendNotification();
- application.Render(0);
+ Actor actor = Actor::New();
+ TestControlRendererRender( application, actor, controlRenderer );
+
Vector4 actualValue(Vector4::ZERO);
+ TestGlAbstraction& gl = application.GetGlAbstraction();
DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "uBlendColor", actualValue ) );
DALI_TEST_EQUALS( actualValue, Color::RED, TEST_LOCATION );
controlRenderer.SetOnStage( actor2 );
application.SendNotification();
application.Render(0);
- Image samplerImage = actor2.GetRendererAt(0u).GetMaterial().GetSamplerAt(0u).GetImage();
- DALI_TEST_CHECK( BufferImage::DownCast( samplerImage ) );
END_TEST;
}
application.SendNotification();
application.Render(0);
- Image samplerImage = actor.GetRendererAt(0u).GetMaterial().GetSamplerAt(0u).GetImage();
- DALI_TEST_CHECK( ResourceImage::DownCast( samplerImage ) );
Image bufferImage = CreateBufferImage( 100, 200, Vector4( 1.f, 1.f, 1.f, 1.f ) );
bool isNewRenderer = factory.ResetRenderer( controlRenderer, bufferImage );
DALI_TEST_CHECK( !isNewRenderer );
application.SendNotification();
application.Render(0);
- samplerImage = actor.GetRendererAt(0u).GetMaterial().GetSamplerAt(0u).GetImage();
- DALI_TEST_CHECK( BufferImage::DownCast( samplerImage ) );
isNewRenderer = factory.ResetRenderer( controlRenderer, Color::RED );
DALI_TEST_CHECK( isNewRenderer );
// Set up a scrollView...
ScrollView scrollView = ScrollView::New();
+ scrollView.SetOvershootEnabled(true);
Stage::GetCurrent().Add( scrollView );
Vector2 stageSize = Stage::GetCurrent().GetSize();
scrollView.SetSize(stageSize);
// Set up a scrollView...
ScrollView scrollView = ScrollView::New();
+ scrollView.SetOvershootEnabled(true);
Stage::GetCurrent().Add( scrollView );
Vector2 stageSize = Stage::GetCurrent().GetSize();
scrollView.SetSize(stageSize);
END_TEST;
}
+
+// Non-API test (so no P or N variant).
+int UtcDaliToolkitScrollViewGesturePageLimit(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( " UtcDaliToolkitScrollViewGesturePageLimit" );
+
+ // Set up a scrollView.
+ ScrollView scrollView = ScrollView::New();
+
+ // Do not rely on stage size for UTC tests.
+ Vector2 pageSize( 720.0f, 1280.0f );
+ scrollView.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
+ scrollView.SetSize( pageSize );
+ scrollView.SetParentOrigin( ParentOrigin::CENTER );
+ scrollView.SetAnchorPoint( AnchorPoint::CENTER );
+ scrollView.SetPosition( 0.0f, 0.0f, 0.0f );
+
+ // Position rulers.
+ // We set the X ruler to fixed to give us pages to snap to.
+ Dali::Toolkit::FixedRuler* rulerX = new Dali::Toolkit::FixedRuler( pageSize.width );
+ // Note: The 3x page width is arbitary, but we need enough to show that we are
+ // capping page movement by the page limiter, and not the domain.
+ rulerX->SetDomain( Dali::Toolkit::RulerDomain( 0.0f, pageSize.width * 3.0f, false ) );
+ Dali::Toolkit::RulerPtr rulerY = new Dali::Toolkit::DefaultRuler();
+ rulerY->Disable();
+ scrollView.SetRulerX( rulerX );
+ scrollView.SetRulerY( rulerY );
+
+ scrollView.SetWrapMode( false );
+ scrollView.SetScrollSensitive( true );
+
+ Stage::GetCurrent().Add( scrollView );
+
+ // Set up a gesture to perform.
+ Vector2 startPos( 50.0f, 0.0f );
+ Vector2 direction( -5.0f, 0.0f );
+ int frames = 200;
+
+ // Force starting position.
+ scrollView.ScrollTo( startPos, 0.0f );
+ Wait( application );
+
+ // Deliberately skip the "Finished" part of the gesture, so we can read the coordinates before the snap begins.
+ Vector2 currentPos( PerformGestureDiagonalSwipe( application, startPos, direction, frames - 1, false ) );
+
+ // Confirm the final X coord has not moved more than one page from the start X position.
+ DALI_TEST_GREATER( ( startPos.x + pageSize.width ), scrollView.GetCurrentScrollPosition().x, TEST_LOCATION );
+
+ // Finish the gesture and wait for the snap.
+ currentPos += direction;
+ SendPan( application, Gesture::Finished, currentPos );
+ // We add RENDER_FRAME_INTERVAL on to wait for an extra frame (for the last "finished" gesture to complete first.
+ Wait( application, RENDER_DELAY_SCROLL + RENDER_FRAME_INTERVAL );
+
+ // Confirm the final X coord has snapped to exactly one page ahead of the start page.
+ DALI_TEST_EQUALS( pageSize.width, scrollView.GetCurrentScrollPosition().x, Math::MACHINE_EPSILON_0, TEST_LOCATION );
+
+ END_TEST;
+}
END_TEST;
}
-int UtcDaliCreateDistanceFieldEffect(void)
+int UtcDaliCreateDissolveEffect(void)
{
ToolkitTestApplication application;
- ShaderEffect effect = Toolkit::CreateDistanceFieldEffect();
- DALI_TEST_CHECK( effect );
+ Property::Map effect = Toolkit::CreateDistanceFieldEffect();
+ DALI_TEST_CHECK( !effect.Empty() );
+
+ Property::Value* customShaderValue = effect.Find( "shader" );
+ DALI_TEST_CHECK( customShaderValue );
+
+ Property::Map customShader;
+ DALI_TEST_CHECK( customShaderValue->Get( customShader ) );
+
+ Property::Value* vertexShaderValue = customShader.Find( "vertex-shader" );
+ DALI_TEST_CHECK( !vertexShaderValue );
+
+ Property::Value* fragmentShaderValue = customShader.Find( "fragment-shader" );
+ DALI_TEST_CHECK( fragmentShaderValue );
+
+ std::string fragmentShader;
+ DALI_TEST_CHECK( fragmentShaderValue->Get( fragmentShader ) );
+ DALI_TEST_CHECK( !fragmentShader.empty() );
+
+ Property::Value* gridXValue = customShader.Find( "subdivide-grid-x" );
+ DALI_TEST_CHECK( !gridXValue );
+
+ Property::Value* hintsValue = customShader.Find( "hints" );
+ DALI_TEST_CHECK( hintsValue );
+
+ std::string hints;
+ DALI_TEST_CHECK( hintsValue->Get( hints ) );
+ DALI_TEST_CHECK( hints == "output-is-transparent" );
END_TEST;
}
{
ToolkitTestApplication application;
- unsigned int sampleCount(4);
- ShaderEffect effect = Toolkit::CreateMotionBlurEffect(sampleCount);
- DALI_TEST_CHECK( effect );
+ Property::Map effect = Toolkit::CreateMotionBlurEffect();
+ DALI_TEST_CHECK( !effect.Empty() );
+
+ Property::Value* customShaderValue = effect.Find( "shader" );
+ DALI_TEST_CHECK( customShaderValue );
+
+ Property::Map customShader;
+ DALI_TEST_CHECK( customShaderValue->Get( customShader ) );
- Property::Value value = effect.GetProperty( effect.GetPropertyIndex("uNumSamples"));
- DALI_TEST_EQUALS( value.Get<float>(), (float)sampleCount, TEST_LOCATION );
+ Property::Value* vertexShaderValue = customShader.Find( "vertex-shader" );
+ DALI_TEST_CHECK( vertexShaderValue );
+
+ std::string vertexShader;
+ DALI_TEST_CHECK( vertexShaderValue->Get( vertexShader ) );
+ DALI_TEST_CHECK( !vertexShader.empty() );
+
+ Property::Value* fragmentShaderValue = customShader.Find( "fragment-shader" );
+ DALI_TEST_CHECK( fragmentShaderValue );
+
+ std::string fragmentShader;
+ DALI_TEST_CHECK( fragmentShaderValue->Get( fragmentShader ) );
+ DALI_TEST_CHECK( !fragmentShader.empty() );
+
+ Property::Value* gridXValue = customShader.Find( "subdivide-grid-x" );
+ DALI_TEST_CHECK( gridXValue );
+
+ int gridX = 0;
+ DALI_TEST_CHECK( gridXValue->Get( gridX ) );
+ DALI_TEST_CHECK( gridX > 1 );
+
+ Property::Value* gridYValue = customShader.Find( "subdivide-grid-y" );
+ DALI_TEST_CHECK( gridYValue );
+
+ int gridY = 0;
+ DALI_TEST_CHECK( gridYValue->Get( gridY ) );
+ DALI_TEST_CHECK( gridY > 1 );
+
+ Property::Value* hintsValue = customShader.Find( "hints" );
+ DALI_TEST_CHECK( hintsValue );
+
+ std::string hints;
+ DALI_TEST_CHECK( hintsValue->Get( hints ) );
+ DALI_TEST_CHECK( hints == "output-is-transparent" );
+
+ unsigned int sampleCount( 4 );
+ Actor actor = Actor::New();
+ Toolkit::SetMotionBlurProperties( actor, sampleCount );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uBlurTexCoordScale" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uGeometryStretchFactor" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uSpeedScalingFactor" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uObjectFadeStart" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uObjectFadeEnd" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uAlphaScale" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uNumSamples" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uModelLastFrame" ) != Property::INVALID_INDEX );
END_TEST;
}
{
ToolkitTestApplication application;
- ShaderEffect effect = Toolkit::CreateMotionStretchEffect();
- DALI_TEST_CHECK( effect );
+ Property::Map effect = Toolkit::CreateMotionStretchEffect();
+ DALI_TEST_CHECK( !effect.Empty() );
+
+ Property::Value* customShaderValue = effect.Find( "shader" );
+ DALI_TEST_CHECK( customShaderValue );
+
+ Property::Map customShader;
+ DALI_TEST_CHECK( customShaderValue->Get( customShader ) );
+
+ Property::Value* vertexShaderValue = customShader.Find( "vertex-shader" );
+ DALI_TEST_CHECK( vertexShaderValue );
+
+ std::string vertexShader;
+ DALI_TEST_CHECK( vertexShaderValue->Get( vertexShader ) );
+ DALI_TEST_CHECK( !vertexShader.empty() );
+
+ Property::Value* fragmentShaderValue = customShader.Find( "fragment-shader" );
+ DALI_TEST_CHECK( fragmentShaderValue );
+
+ std::string fragmentShader;
+ DALI_TEST_CHECK( fragmentShaderValue->Get( fragmentShader ) );
+ DALI_TEST_CHECK( !fragmentShader.empty() );
+
+ Property::Value* gridXValue = customShader.Find( "subdivide-grid-x" );
+ DALI_TEST_CHECK( gridXValue );
+
+ int gridX = 0;
+ DALI_TEST_CHECK( gridXValue->Get( gridX ) );
+ DALI_TEST_CHECK( gridX > 1 );
+
+ Property::Value* gridYValue = customShader.Find( "subdivide-grid-y" );
+ DALI_TEST_CHECK( gridYValue );
+
+ int gridY = 0;
+ DALI_TEST_CHECK( gridYValue->Get( gridY ) );
+ DALI_TEST_CHECK( gridY > 1 );
+
+ Property::Value* hintsValue = customShader.Find( "hints" );
+ DALI_TEST_CHECK( hintsValue );
+
+ std::string hints;
+ DALI_TEST_CHECK( hintsValue->Get( hints ) );
+ DALI_TEST_CHECK( hints == "output-is-transparent" );
+
+ Actor actor = Actor::New();
+ Toolkit::SetMotionStretchProperties( actor );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uGeometryStretchFactor" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uSpeedScalingFactor" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uObjectFadeStart" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uObjectFadeEnd" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uAlphaScale" ) != Property::INVALID_INDEX );
+ DALI_TEST_CHECK( actor.GetPropertyIndex( "uModelLastFrame" ) != Property::INVALID_INDEX );
END_TEST;
}
# Build the Dali Toolkit library
-toolkit_images_dir = ../../../dali-toolkit/images-common
+toolkit_images_dir = ../../../dali-toolkit/styles/images-common
toolkit_sounds_dir = ../../../dali-toolkit/sounds
toolkit_src_dir = ../../../dali-toolkit/internal
public_api_src_dir = ../../../dali-toolkit/public-api
toolkit_styles_dir = $(STYLE_DIR)
toolkit_style_images_dir = $(STYLE_DIR)/images
-include ../../../dali-toolkit/images-common/file.list
include ../../../dali-toolkit/sounds/file.list
include ../../../dali-toolkit/styles/file.list
+include ../../../dali-toolkit/styles/images-common/file.list
include ../../../dali-toolkit/internal/file.list
include ../../../dali-toolkit/public-api/file.list
include ../../../dali-toolkit/devel-api/file.list
* "uPercentage" - This value is proportional to the distortion applied; a value of zero means no distortion.
*
* @param[in] useHighPrecision True if using high precision in fragment shader for fully random noise, false otherwise
- * @return A handle to a newly allocated ShaderEffect
+ * @return The newly created Property::Map with the dissolve effect
*/
inline Property::Map CreateDissolveEffect( bool useHighPrecision = true )
*/
// EXTERNAL INCLUDES
+#include <string.h>
#include <dali/public-api/shader-effects/shader-effect.h>
namespace Dali
*
* DistanceFieldEffect is a custom shader effect to achieve distance field on Image actors
*
- * Animatable/Constrainable uniforms:
+ * Animatable/Constrainable uniforms - These will need to be registered to the actor as a custom property to take into effect:
*
- * "uSmoothing" - Soft edge smoothing. Specify the distance field value for the center of the edge.
-
* "uDoGlow" - The glow state. If true, glow is enabled
* "uGlowBoundary" - The glow boundary factor
* "uGlowColor" - The glow color multiplier
* First value [0-1] Specifies the distance field value for the center of the outline.
* Second value [0-1] Specifies the softness/width/anti-aliasing of the outlines inner edge.
*
- * @return A handle to a newly allocated ShaderEffect
+ * @return The newly created Property::Map with the distance field effect
*/
-inline ShaderEffect CreateDistanceFieldEffect()
+inline Dali::Property::Map CreateDistanceFieldEffect()
{
- std::string fragmentShaderPrefix(
- "#extension GL_OES_standard_derivatives : enable\n"
- "\n"
- );
-
- std::string fragmentShader(
- "uniform mediump float uSmoothing;\n"
- "uniform mediump float uGlowBoundary;\n"
- "uniform mediump vec2 uOutlineParams;\n"
- "uniform lowp vec4 uOutlineColor;\n"
- "uniform lowp vec4 uShadowColor;\n"
- "uniform mediump vec2 uShadowOffset;\n"
- "uniform lowp vec4 uGlowColor;\n"
- "uniform lowp float uDoOutline;\n"
- "uniform lowp float uDoShadow;\n"
- "uniform lowp float uDoGlow;\n"
- "\n"
- "void main()\n"
- "{\n"
- " // sample distance field\n"
- " mediump float distance = texture2D(sTexture, vTexCoord).a;\n"
- " mediump float smoothWidth = fwidth(distance);\n"
- " mediump float alphaFactor = smoothstep(uSmoothing - smoothWidth, uSmoothing + smoothWidth, distance);\n"
- " lowp vec4 color;\n"
- " if (uDoShadow == 0.0)\n"
- " {\n"
- " mediump float alpha = uColor.a * alphaFactor;\n"
- " lowp vec4 rgb = uColor;\n"
- "\n"
- " if (uDoOutline > 0.0)\n"
- " {\n"
- " mediump float outlineWidth = uOutlineParams[1] + smoothWidth;\n"
- " mediump float outlineBlend = smoothstep(uOutlineParams[0] - outlineWidth, uOutlineParams[0] + outlineWidth, distance);\n"
- " alpha = smoothstep(uSmoothing - smoothWidth, uSmoothing + smoothWidth, distance);\n"
- " rgb = mix(uOutlineColor, uColor, outlineBlend);\n"
- " }\n"
- "\n"
- " if (uDoGlow > 0.0)\n"
- " {\n"
- " rgb = mix(uGlowColor, rgb, alphaFactor);\n"
- " alpha = smoothstep(uGlowBoundary, uSmoothing, distance);\n"
- " }\n"
- "\n"
- " // set fragment color\n"
- " color = vec4(rgb.rgb, alpha);\n"
- " }\n"
- "\n"
- " else // (uDoShadow > 0.0)\n"
- " {\n"
- " mediump float shadowDistance = texture2D(sTexture, vTexCoord - uShadowOffset).a;\n"
- " mediump float inText = alphaFactor;\n"
- " mediump float inShadow = smoothstep(uSmoothing - smoothWidth, uSmoothing + smoothWidth, shadowDistance);\n"
- "\n"
- " // inside object, outside shadow\n"
- " if (inText == 1.0)\n"
- " {\n"
- " color = uColor;\n"
- " }\n"
- " // inside object, outside shadow\n"
- " else if ((inText != 0.0) && (inShadow == 0.0))\n"
- " {\n"
- " color = uColor;\n"
- " color.a *= inText;\n"
- " }\n"
- " // outside object, completely inside shadow\n"
- " else if ((inText == 0.0) && (inShadow == 1.0))\n"
- " {\n"
- " color = uShadowColor;\n"
- " }\n"
- " // inside object, completely inside shadow\n"
- " else if ((inText != 0.0) && (inShadow == 1.0))\n"
- " {\n"
- " color = mix(uShadowColor, uColor, inText);\n"
- " color.a = uShadowColor.a;\n"
- " }\n"
- " // inside object, inside shadow's border\n"
- " else if ((inText != 0.0) && (inShadow != 0.0))\n"
- " {\n"
- " color = mix(uShadowColor, uColor, inText);\n"
- " color.a *= max(inText, inShadow);\n"
- " }\n"
- " // inside shadow's border\n"
- " else if (inShadow != 0.0)\n"
- " {\n"
- " color = uShadowColor;\n"
- " color.a *= inShadow;\n"
- " }\n"
- " // outside shadow and object\n"
- " else \n"
- " {\n"
- " color.a = 0.0;\n"
- " }\n"
- "\n"
- " }\n"
- "\n"
- " gl_FragColor = color;\n"
- "\n"
- "}\n"
+ const char* fragmentShaderPrefix( "#extension GL_OES_standard_derivatives : enable\n" );
+
+ const char* fragmentShader( DALI_COMPOSE_SHADER(
+ varying mediump vec2 vTexCoord;\n
+ \n
+ uniform mediump float uGlowBoundary;\n
+ uniform mediump vec2 uOutlineParams;\n
+ uniform lowp vec4 uOutlineColor;\n
+ uniform lowp vec4 uShadowColor;\n
+ uniform mediump vec2 uShadowOffset;\n
+ uniform lowp vec4 uGlowColor;\n
+ uniform lowp float uDoOutline;\n
+ uniform lowp float uDoShadow;\n
+ uniform lowp float uDoGlow;\n
+ \n
+ uniform sampler2D sTexture;\n
+ uniform lowp vec4 uColor;\n
+ \n
+ void main()\n
+ {\n
+ // sample distance field\n
+ mediump float smoothing = 0.5;\n
+
+ mediump float distance = texture2D(sTexture, vTexCoord).a;\n
+ mediump float smoothWidth = fwidth(distance);\n
+ mediump float alphaFactor = smoothstep(smoothing - smoothWidth, smoothing + smoothWidth, distance);\n
+ lowp vec4 color;\n
+ if (uDoShadow == 0.0)\n
+ {\n
+ mediump float alpha = uColor.a * alphaFactor;\n
+ lowp vec4 rgb = uColor;\n
+ \n
+ if (uDoOutline > 0.0)\n
+ {\n
+ mediump float outlineWidth = uOutlineParams[1] + smoothWidth;\n
+ mediump float outlineBlend = smoothstep(uOutlineParams[0] - outlineWidth, uOutlineParams[0] + outlineWidth, distance);\n
+ alpha = smoothstep(smoothing - smoothWidth, smoothing + smoothWidth, distance);\n
+ rgb = mix(uOutlineColor, uColor, outlineBlend);\n
+ }\n
+ \n
+ if (uDoGlow > 0.0)\n
+ {\n
+ rgb = mix(uGlowColor, rgb, alphaFactor);\n
+ alpha = smoothstep(uGlowBoundary, smoothing, distance);\n
+ }\n
+ \n
+ // set fragment color\n
+ color = vec4(rgb.rgb, alpha);\n
+ }\n
+ \n
+ else // (uDoShadow > 0.0)\n
+ {\n
+ mediump float shadowDistance = texture2D(sTexture, vTexCoord - uShadowOffset).a;\n
+ mediump float inText = alphaFactor;\n
+ mediump float inShadow = smoothstep(smoothing - smoothWidth, smoothing + smoothWidth, shadowDistance);\n
+ \n
+ // inside object, outside shadow\n
+ if (inText == 1.0)\n
+ {\n
+ color = uColor;\n
+ }\n
+ // inside object, outside shadow\n
+ else if ((inText != 0.0) && (inShadow == 0.0))\n
+ {\n
+ color = uColor;\n
+ color.a *= inText;\n
+ }\n
+ // outside object, completely inside shadow\n
+ else if ((inText == 0.0) && (inShadow == 1.0))\n
+ {\n
+ color = uShadowColor;\n
+ }\n
+ // inside object, completely inside shadow\n
+ else if ((inText != 0.0) && (inShadow == 1.0))\n
+ {\n
+ color = mix(uShadowColor, uColor, inText);\n
+ color.a = uShadowColor.a;\n
+ }\n
+ // inside object, inside shadow's border\n
+ else if ((inText != 0.0) && (inShadow != 0.0))\n
+ {\n
+ color = mix(uShadowColor, uColor, inText);\n
+ color.a *= max(inText, inShadow);\n
+ }\n
+ // inside shadow's border\n
+ else if (inShadow != 0.0)\n
+ {\n
+ color = uShadowColor;\n
+ color.a *= inShadow;\n
+ }\n
+ // outside shadow and object\n
+ else \n
+ {\n
+ color.a = 0.0;\n
+ }\n
+ \n
+ }\n
+ \n
+ gl_FragColor = color;\n
+ \n
+ } )
);
- // Create the implementation, temporarily owned on stack
- Dali::ShaderEffect shaderEffect = Dali::ShaderEffect::NewWithPrefix(
- "", "",
- fragmentShaderPrefix, fragmentShader,
- ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING));
+ Property::Map map;
- shaderEffect.SetUniform("uSmoothing",0.5f);
- shaderEffect.SetUniform("uOutlineColor",Color::BLACK);
- shaderEffect.SetUniform("uOutlineParams",Vector2(0.51f, 0.0f));
- shaderEffect.SetUniform("uGlowBoundary",0.4f);
- shaderEffect.SetUniform("uGlowColor",Color::GREEN);
- shaderEffect.SetUniform("uShadowColor",Vector4(0.0f, 0.0f, 0.0f, 0.4f));
+ Property::Map customShader;
- // TODO: find a way to set the shadow offset in texel coordinates instead of UVs.
- shaderEffect.SetUniform("uShadowOffset",Vector2(0.05f, 0.05f));
+ std::string fragmentShaderString;
+ fragmentShaderString.reserve( strlen( fragmentShaderPrefix ) + strlen( fragmentShader ) );
+ fragmentShaderString.append( fragmentShaderPrefix );
+ fragmentShaderString.append( fragmentShader );
- // Default:
- shaderEffect.SetUniform("uDoOutline",false);
- shaderEffect.SetUniform("uDoGlow",false);
- shaderEffect.SetUniform("uDoShadow",false);
+ customShader[ "fragment-shader" ] = fragmentShaderString;
+ customShader[ "hints" ] = "output-is-transparent";
- return shaderEffect;
+ map[ "shader" ] = customShader;
+ return map;
}
{
/**
+ * @brief Set the properties for the motion blur
+ *
+ * @param numBlurSamples Number of samples used by the shader
+ */
+inline void SetMotionBlurProperties( Actor& actor, unsigned int numBlurSamples = 8 )
+{
+ actor.RegisterProperty( "uBlurTexCoordScale", 0.125f );
+ actor.RegisterProperty( "uGeometryStretchFactor", 0.05f );
+ actor.RegisterProperty( "uSpeedScalingFactor", 0.5f );
+ actor.RegisterProperty( "uObjectFadeStart", Vector2( 0.25f, 0.25f ) );
+ actor.RegisterProperty( "uObjectFadeEnd", Vector2( 0.5f, 0.5f ) );
+ actor.RegisterProperty( "uAlphaScale", 0.75f );
+ actor.RegisterProperty( "uNumSamples", static_cast<float>( numBlurSamples ) );
+ actor.RegisterProperty( "uRecipNumSamples", 1.0f / static_cast<float>( numBlurSamples ) );
+ actor.RegisterProperty( "uRecipNumSamplesMinusOne", 1.0f / static_cast<float>( numBlurSamples - 1.0f ) );
+ Property::Index uModelProperty = actor.RegisterProperty( "uModelLastFrame", Matrix::IDENTITY );
+
+ Constraint constraint = Constraint::New<Matrix>( actor, uModelProperty, EqualToConstraint() );
+ constraint.AddSource( Source( actor , Actor::Property::WORLD_MATRIX ) );
+ constraint.Apply();
+}
+
+/**
* @brief Create a new MotionBlurEffect
*
* Motion blur shader works on a per object basis. Objects will
- * blur when they move, or if the camera moves. Can be applied to ImageActor or
- * TextActor only.
- *
- * Usage example:-
- *
- * // Create shader used for doing motion blur\n
- * ShaderEffect MotionBlurEffect = CreateMotionBlurEffect();
- *
- * // set actor shader to the blur one\n
- * Actor actor = Actor::New( ... );\n
- * actor.SetShaderEffect( MotionBlurEffect );
- *
- * // Constrain "uModelLastFrame" to be the same as the actor's world matrix\n
- * Dali::Property::Index uModelProperty = MotionBlurEffect.GetPropertyIndex( "uModelLastFrame" );
- * Constraint constraint = Constraint::New<Matrix>( MotionBlurEffect, uModelProperty, EqualToConstraint() );\n
- * constraint.AddSource( Source( actor , Actor::Property::WORLD_MATRIX ) );\n
- * constraint.Apply();\n
- *
+ * blur when they move, or if the camera moves.
*
* Animatable/Constrainable uniforms:
* "uBlurTexCoordScale" - This scales the offset for texture samples along the motion velocity vector.
* at the cost of performance.
* "uModelLastFrame" - The model to world space transformation matrix of the actor in the previous frame.
*
- * @param numBlurSamples Number of samples used by the shader
- * @return A handle to a newly allocated ShaderEffect
+ * @return The newly created Property::Map with the motion blur effect
*/
-inline ShaderEffect CreateMotionBlurEffect( unsigned int numBlurSamples = 8 )
+inline Property::Map CreateMotionBlurEffect()
{
- // Dali vertexSource prefix for reference:
- // precision highp float;
- // attribute vec3 aPosition;
- // attribute vec2 aTexCoord;
- // uniform mat4 uMvpMatrix;
- // uniform mat4 uModelView;
- // uniform mat3 uNormalMatrix;
- // uniform mat4 uProjection;
- // uniform vec4 uColor;
- // varying vec2 vTexCoord;
std::string vertexSource;
vertexSource =
"precision mediump float;\n"
+
+ "attribute vec2 aPosition;\n"
+
+ "uniform mat4 uMvpMatrix;\n"
+ "uniform mat4 uModelView;\n"
+ "uniform mat4 uViewMatrix;\n"
+ "uniform mat4 uProjection;\n"
+ "uniform vec3 uSize;\n"
+
"uniform mat4 uModelLastFrame;\n"
- "uniform float uTimeDelta;\n"
+ "float timeDelta = 0.0167;\n"
"uniform float uGeometryStretchFactor;\n"
"uniform float uSpeedScalingFactor;\n"
"varying vec2 vModelSpaceCenterToPos;\n"
"varying vec2 vScreenSpaceVelocityVector;\n"
"varying float vSpeed;\n"
+ "varying vec2 vTexCoord;\n"
"void main()\n"
"{\n"
// get view space position of vertex this frame and last frame
- " vec4 vertex = vec4(aPosition, 1.0);\n"
- " vec4 viewSpaceVertex = uModelView * vertex;\n"
- " vec4 viewSpaceVertexLastFrame = (uViewMatrix * uModelLastFrame) * vertex;\n"
- " float reciprocalTimeDelta = 1.0 / ((uTimeDelta > 0.0) ? uTimeDelta : 0.01);\n"
+ " vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n"
+ " vertexPosition.xyz *= uSize;\n"
+
+ " vec4 viewSpaceVertex = uModelView * vertexPosition;\n"
+ " vec4 viewSpaceVertexLastFrame = (uViewMatrix * uModelLastFrame) * vertexPosition;\n"
+ " float reciprocalTimeDelta = 1.0 / timeDelta;\n"
// work out vertex's last movement in view space
" vec3 viewSpacePosDelta = viewSpaceVertex.xyz - viewSpaceVertexLastFrame.xyz;\n"
// get clip space position of vertex this frame and last frame
- " vec4 clipSpaceVertex = uMvpMatrix * vertex;\n"
+ " vec4 clipSpaceVertex = uMvpMatrix * vertexPosition;\n"
" vec4 clipSpaceVertexLastFrame = uProjection * viewSpaceVertexLastFrame;\n"
// decide how much this vertex is 'trailing', i.e. at the back of the object relative to its direction of motion. We do this
" float posDeltaLength = length(viewSpacePosDelta);\n"
" if(posDeltaLength > 0.001)\n" // avoid div by 0 if object has barely moved
" {\n"
- " vec4 viewSpaceCenterToPos = uModelView * vec4(aPosition, 0.0);\n"
+ " vec4 viewSpaceCenterToPos = uModelView * vec4(vertexPosition.xy, 0.0, 0.0);\n"
" float centerToVertexDist = length(viewSpaceCenterToPos);\n"
" if(centerToVertexDist > 0.001)\n" // avoid div by 0 if object has vertex at model space origin
" {\n"
" vSpeed = clamp(vSpeed, 0.0, 1.0);\n"
// provide fragment shader with vector from center of object to pixel (assumes the objects model space origin is at its center and verts have same z)
- " vModelSpaceCenterToPos = aPosition.xy;\n"
+ " vModelSpaceCenterToPos = viewSpaceVertex.xy;\n"
- " vTexCoord = aTexCoord;\n"
+ " vec2 texCoord = aPosition + vec2(0.5);"
+ " vTexCoord = texCoord;\n"
"}\n";
- // Dali fragmentSource prefix for reference:
- // precision highp float;
- // uniform sampler2D sTexture;
- // uniform sampler2D sEffect;
- // uniform vec4 uColor;
- // varying vec2 vTexCoord;
std::string fragmentSource;
fragmentSource =
"precision mediump float;\n"
+
+ "uniform sampler2D sTexture;\n"
+ "uniform vec4 uColor;\n"
+
"uniform vec2 uObjectFadeStart;\n"
"uniform vec2 uObjectFadeEnd;\n"
"uniform float uAlphaScale;\n"
"varying vec2 vModelSpaceCenterToPos;\n"
"varying vec2 vScreenSpaceVelocityVector;\n"
"varying float vSpeed;\n"
+ "varying vec2 vTexCoord;\n"
"void main()\n"
"{\n"
" col += texture2D(sTexture, vTexCoord + (velocity * t)) * uRecipNumSamples;\n"
" }\n"
" gl_FragColor = mix(colActor, col, vSpeed);\n" // lerp blurred and non-blurred actor based on speed of motion
- " gl_FragColor.a = colActor.a * fadeToEdgesScale;\n" // fade blurred actor to its edges based on speed of motion
+ " gl_FragColor.a = fadeToEdgesScale;//colActor.a * fadeToEdgesScale;\n" // fade blurred actor to its edges based on speed of motion
" gl_FragColor *= uColor;\n"
"}\n";
+
+ Property::Map map;
+
+ Property::Map customShader;
+ customShader[ "vertex-shader" ] = vertexSource;
+ customShader[ "fragment-shader" ] = fragmentSource;
+
+ customShader[ "subdivide-grid-x" ] = 10;
+ customShader[ "subdivide-grid-y" ] = 10;
+
// NOTE: we must turn on alpha blending for the actor (HINT_BLENDING)
- ShaderEffect shader = ShaderEffect::New( vertexSource, fragmentSource,
- ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID) );
-
- //////////////////////////////////////
- // Register uniform properties
- //
- //
- shader.SetUniform( "uBlurTexCoordScale", 0.125f );
- shader.SetUniform( "uGeometryStretchFactor", 0.05f );
- shader.SetUniform( "uSpeedScalingFactor", 0.5f );
- shader.SetUniform( "uObjectFadeStart", Vector2( 0.25f, 0.25f ) );
- shader.SetUniform( "uObjectFadeEnd", Vector2( 0.5f, 0.5f ) );
- shader.SetUniform( "uAlphaScale", 0.75f );
- shader.SetUniform( "uNumSamples", static_cast<float>( numBlurSamples ) );
- shader.SetUniform( "uRecipNumSamples", 1.0f / static_cast<float>( numBlurSamples ) );
- shader.SetUniform( "uRecipNumSamplesMinusOne", 1.0f / static_cast<float>( numBlurSamples - 1.0f ) );
- shader.SetUniform( "uModelLastFrame", Matrix::IDENTITY );
-
- return shader;
+ customShader[ "hints" ] = "output-is-transparent";
+
+ map[ "shader" ] = customShader;
+ return map;
}
}
{
/**
+ * @brief Set the properties for the motion stretch
+ */
+inline void SetMotionStretchProperties( Actor& actor )
+{
+ actor.RegisterProperty( "uGeometryStretchFactor", 0.5f );
+ actor.RegisterProperty( "uSpeedScalingFactor", 0.5f );
+ actor.RegisterProperty( "uObjectFadeStart", Vector2( 0.25f, 0.25f ) );
+ actor.RegisterProperty( "uObjectFadeEnd", Vector2( 0.5f, 0.5f ) );
+ actor.RegisterProperty( "uAlphaScale", 0.75f );
+ Property::Index uModelProperty = actor.RegisterProperty( "uModelLastFrame", Matrix::IDENTITY );
+
+ Constraint constraint = Constraint::New<Matrix>( actor, uModelProperty, EqualToConstraint() );
+ constraint.AddSource( Source( actor , Actor::Property::WORLD_MATRIX ) );
+ constraint.Apply();
+}
+
+/**
* @brief Creates a new MotionStretchEffect
*
- * Motion stretch shader works on a per object basis. Objects will stretch in the direction of motion when they move, or if the camera moves. Can be applied
- * to ImageActor or TextActor only.
- *
- * Usage example:-
- *
- * // Create shader used for doing motion stretch\n
- * ShaderEffect MotionStretchEffect = CreateMotionStretchEffect();
- *
- * // set actor shader to the stretch one\n
- * Actor actor = Actor::New( ... );\n
- * actor.SetShaderEffect( MotionStretchEffect );
- *
- * // Constrain "uModelLastFrame" to be the same as the actor's world matrix\n
- * Dali::Property::Index uModelProperty = MotionBlurEffect.GetPropertyIndex( "uModelLastFrame" );
- * Constraint constraint = Constraint::New<Matrix>( MotionBlurEffect, uModelProperty, EqualToConstraint() );\n
- * constraint.AddSource( Source( actor , Actor::Property::WORLD_MATRIX ) );\n
- * constraint.Apply();\n
+ * Motion stretch shader works on a per object basis. Objects will stretch in the direction of motion when they move, or if the camera moves.
*
* Animatable/Constrainable uniforms:
* "uGeometryStretchFactor" - This scales the amount the geometry stretches along the motion velocity vector.
* applied. Default 0.75.
* "uModelLastFrame" - The model to world space transformation matrix of the actor in the previous frame.
*
- * @return A handle to a newly allocated ShaderEffect
+ * @return The newly created Property::Map with the motion stretch effect
*/
-inline ShaderEffect CreateMotionStretchEffect()
+inline Property::Map CreateMotionStretchEffect()
{
- // Dali vertexSource prefix for reference:
- // precision highp float;
- // attribute vec3 aPosition;
- // attribute vec2 aTexCoord;
- // uniform mat4 uMvpMatrix;
- // uniform mat4 uModelView;
- // uniform mat3 uNormalMatrix;
- // uniform mat4 uProjection;
- // uniform vec4 uColor;
- // varying vec2 vTexCoord;
std::string vertexSource;
vertexSource =
"precision mediump float;\n"
+
+ "attribute vec2 aPosition;\n"
+
+ "uniform mat4 uMvpMatrix;\n"
+ "uniform mat4 uModelView;\n"
+ "uniform mat4 uViewMatrix;\n"
+ "uniform mat4 uProjection;\n"
+ "uniform vec3 uSize;\n"
+
"uniform mat4 uModelLastFrame;\n"
- "uniform float uTimeDelta;\n"
+ "float timeDelta = 0.0167;\n"
"uniform float uGeometryStretchFactor;\n"
"uniform float uSpeedScalingFactor;\n"
"varying vec2 vModelSpaceCenterToPos;\n"
"varying vec2 vScreenSpaceVelocityVector;\n"
"varying float vSpeed;\n"
+ "varying vec2 vTexCoord;\n"
"void main()\n"
"{\n"
// get view space position of vertex this frame and last frame
- " vec4 vertex = vec4(aPosition, 1.0);\n"
- " vec4 viewSpaceVertex = uModelView * vertex;\n"
- " vec4 viewSpaceVertexLastFrame = uViewMatrix * uModelLastFrame * vertex;\n"
+ " vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n"
+ " vertexPosition.xyz *= uSize;\n"
+
+ " vec4 viewSpaceVertex = uModelView * vertexPosition;\n"
+ " vec4 viewSpaceVertexLastFrame = uViewMatrix * uModelLastFrame * vertexPosition;\n"
// work out vertex's last movement in view space
" vec3 viewSpacePosDelta = viewSpaceVertex.xyz - viewSpaceVertexLastFrame.xyz;\n"
- " float reciprocalTimeDelta = 1.0 / ((uTimeDelta > 0.0) ? uTimeDelta : 0.01);\n"
+ " float reciprocalTimeDelta = 1.0 / timeDelta;\n"
// get clip space position of vertex this frame and last frame
- " vec4 clipSpaceVertex = uMvpMatrix * vertex;\n"
+ " vec4 clipSpaceVertex = uMvpMatrix * vertexPosition;\n"
" vec4 clipSpaceVertexLastFrame = uProjection * viewSpaceVertexLastFrame;\n"
// decide how much this vertex is 'trailing', i.e. at the back of the object relative to its direction of motion. We do this
" float posDeltaLength = length(viewSpacePosDelta);\n"
" if(posDeltaLength > 0.001)\n" // avoid div by 0 if object has barely moved
" {\n"
- " vec4 viewSpaceCenterToPos = uModelView * vec4(aPosition, 0.0);\n"
+ " vec4 viewSpaceCenterToPos = uModelView * vec4(aPosition, 0.0, 0.0);\n"
" float centerToVertexDist = length(viewSpaceCenterToPos);\n"
" if(centerToVertexDist > 0.001)\n" // avoid div by 0 if object has vertex at model space origin
" {\n"
" vSpeed = clamp(vSpeed, 0.0, 1.0);\n"
// provide fragment shader with vector from center of object to pixel (assumes the objects model space origin is at its center and verts have same z)
- " vModelSpaceCenterToPos = aPosition.xy;\n"
+ " vModelSpaceCenterToPos = viewSpaceVertex.xy;\n"
- " vTexCoord = aTexCoord;\n"
+ " vec2 texCoord = aPosition + vec2(0.5);"
+ " vTexCoord = texCoord;\n"
"}\n";
-
- // Dali fragmentSource prefix for reference:
- // precision highp float;
- // uniform sampler2D sTexture;
- // uniform sampler2D sEffect;
- // uniform vec4 uColor;
- // varying vec2 vTexCoord;
std::string fragmentSource;
fragmentSource =
"precision mediump float;\n"
+ "uniform sampler2D sTexture;\n"
+ "uniform vec4 uColor;\n"
+
"uniform vec2 uObjectFadeStart;\n"
"uniform vec2 uObjectFadeEnd;\n"
"uniform float uAlphaScale;\n"
"varying vec2 vModelSpaceCenterToPos;\n"
"varying vec2 vScreenSpaceVelocityVector;\n"
"varying float vSpeed;\n"
+ "varying vec2 vTexCoord;\n"
"void main()\n"
"{\n"
" gl_FragColor *= uColor;\n"
"}";
+ Property::Map map;
+
+ Property::Map customShader;
+ customShader[ "vertex-shader" ] = vertexSource;
+ customShader[ "fragment-shader" ] = fragmentSource;
+
+ customShader[ "subdivide-grid-x" ] = 10;
+ customShader[ "subdivide-grid-y" ] = 10;
+
// NOTE: we must turn on alpha blending for the actor (HINT_BLENDING)
- ShaderEffect shaderEffect = ShaderEffect::New(
- vertexSource, fragmentSource,
- ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID ) );
-
-
- //////////////////////////////////////
- // Register uniform properties
- //
- //
- shaderEffect.SetUniform( "uGeometryStretchFactor", 0.5f );
- shaderEffect.SetUniform( "uSpeedScalingFactor", 0.5f );
- shaderEffect.SetUniform( "uObjectFadeStart", Vector2( 0.25f, 0.25f ) );
- shaderEffect.SetUniform( "uObjectFadeEnd", Vector2( 0.5f, 0.5f ) );
- shaderEffect.SetUniform( "uAlphaScale", 0.75f );
- shaderEffect.SetUniform( "uModelLastFrame", Matrix::IDENTITY );
-
- return shaderEffect;
+ customShader[ "hints" ] = "output-is-transparent";
+
+ map[ "shader" ] = customShader;
+ return map;
}
}
/**
* A property value type can be forced when its unknown by a disambiguation convention in the json
* ie "myarray": [1,2,3,4] ; would be a vector but
- * "myarray": {'type-cast':'array', 'value':[1,2,3,4]} would be an array
+ * "myarray": {"type-cast":"array", "value":[1,2,3,4]} would be an array
* @param child The node whos string to search for a disambiguated type
* @param value The value to set
* @param overrideMap The user overriding constant map
// Prepare the frame buffer to store the color adjusted background image
mEffectImage = FrameBufferImage::New( mMovementArea.width/4.f, mMovementArea.height/4.f, Pixel::RGBA8888, Dali::Image::UNUSED );
- // Generate the samplers and geometry, which is used by all bubbleActors
- mSamplerBackground = Sampler::New( mEffectImage, "sBackground" );
- mSamplerBubbleShape = Sampler::New( mShapeImage, "sBubbleShape" );
+ // Generate the geometry, which is used by all bubbleActors
mMeshGeometry = CreateGeometry( mNumBubblePerActor*mDensity );
Shader bubbleShader = CreateBubbleShader (mNumBubblePerActor );
mMaterial = Material::New( bubbleShader );
- mMaterial.AddSampler( mSamplerBackground );
- mMaterial.AddSampler( mSamplerBubbleShape );
+ mMaterial.AddTexture( mEffectImage, "sBackground" );
+ mMaterial.AddTexture( mShapeImage, "sBubbleShape" );
mBubbleActors.resize( mNumActor );
void BubbleEmitter::SetShapeImage( Image shapeImage )
{
- mSamplerBubbleShape.SetImage( shapeImage );
+ mMaterial.SetTextureImage( 1, shapeImage );
}
void BubbleEmitter::SetBubbleScale( float scale )
FrameBufferImage mEffectImage; ///< The image stores the adjusted color of the background image.The bubbles pick color from this image.
CameraActor mCameraActor; ///< The render task views the scene from the perspective of this actor.
- Sampler mSamplerBackground; ///< The sampler which provides the background image to material
- Sampler mSamplerBubbleShape; ///< The sampler which provides the bubble shape image to material
Geometry mMeshGeometry; ///< The mesh geometry which contains the vertices and indices data
Material mMaterial; ///< The material which controls the bubble display
std::vector<BubbleActorPtr> mBubbleActors; ///< The meshActor vector, its size is mNumShader.
}
-/**
- * ZrelativeToYconstraint
- *
- * f(current, property, scale) = Vector3(current.x, current.y, property.y * scale)
- */
-struct ZrelativeToYconstraint
-{
- ZrelativeToYconstraint( float scale )
- : mScale( scale )
- {}
-
- Vector3 operator()(const Vector3& current,
- const PropertyInput& property)
- {
- Vector3 v;
-
- v.x = current.x;
- v.y = current.y;
- v.z = property.GetVector3().y * mScale;
-
- return v;
- }
-
- float mScale;
-};
-
void GaussianBlurView::OnSizeSet(const Vector3& targetSize)
{
mTargetSize = Vector2(targetSize);
void ImageView::OnStageConnection( int depth )
{
+ Control::OnStageConnection( depth );
+
if( mRenderer )
{
CustomActor self = Self();
CustomActor self = Self();
mRenderer.SetOffStage( self );
}
+
+ Control::OnStageDisconnection();
}
void Model3dView::OnStageConnection( int depth )
{
+ Control::OnStageConnection( depth );
+
CustomActor self = Self();
self.AddRenderer( mRenderer );
Image tex0 = ResourceImage::New( imgUrl );
if( tex0 )
{
- Sampler sampler = Sampler::New( tex0, "sDiffuse" );
- sampler.SetWrapMode(Sampler::REPEAT,Sampler::REPEAT);
- sampler.SetAffectsTransparency(false);
-
- mMaterial.AddSampler( sampler );
+ size_t index = mMaterial.AddTexture( tex0, "sDiffuse" );
+ mMaterial.SetTextureAffectsTransparency(index, false );
}
}
Image tex1 = ResourceImage::New( imgUrl );
if (tex1)
{
- Sampler sampler = Sampler::New( tex1, "sNormal" );
- sampler.SetWrapMode(Sampler::REPEAT,Sampler::REPEAT);
- sampler.SetAffectsTransparency(false);
- sampler.SetFilterMode(Sampler::LINEAR,Sampler::LINEAR);
-
- mMaterial.AddSampler( sampler );
+ size_t index = mMaterial.AddTexture( tex1, "sNormal" );
+ mMaterial.SetTextureAffectsTransparency(index, false );
}
}
Image tex2 = ResourceImage::New( imgUrl );
if( tex2 )
{
- Sampler sampler = Sampler::New( tex2, "sGloss" );
- sampler.SetWrapMode(Sampler::REPEAT,Sampler::REPEAT);
- sampler.SetAffectsTransparency(false);
- sampler.SetFilterMode(Sampler::LINEAR,Sampler::LINEAR);
-
- mMaterial.AddSampler( sampler );
+ size_t index = mMaterial.AddTexture( tex2, "sGloss" );
+ mMaterial.SetTextureAffectsTransparency(index, false );
}
}
}
void PageTurnView::OnStageConnection( int depth )
{
+ Control::OnStageConnection( depth );
+
SetupShadowView();
mTurningPageLayer.Raise();
}
SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
}
+
+ Control::OnStageDisconnection();
}
void PageTurnView::SetPageSize( const Vector2& pageSize )
};
std::string mCachedRendererKey; ///< The key to use for caching of the renderer. If it is empty then no caching will occur
- RendererFactoryCache::CachedRendererPtr mCachedRenderer; ///< The current cached renderer from the Factory Cache. mCachedRenderer == null whilst this control render is offstage
Renderer mRenderer;
CustomShader* mCustomShader;
}
else
{
- //remove the cached renderer from the cache if we and the cache are the only things that hold a reference to it
- if( mImpl->mCachedRenderer && mImpl->mCachedRenderer->ReferenceCount() == 2 )
- {
- mFactoryCache.RemoveRenderer( mImpl->mCachedRenderer->mKey );
- }
- mImpl->mCachedRenderer.Reset();
+ //clean the renderer from the cache since it may no longer be in use
+ mFactoryCache.CleanRendererCache( mImpl->mCachedRendererKey );
//add the new renderer
mImpl->mCachedRendererKey = cachedRendererKey;
if( !mImpl->mCachedRendererKey.empty() && !mImpl->mCustomShader )
{
DALI_ASSERT_DEBUG( mImpl->mRenderer && "The control render is on stage but it doesn't have a valid renderer.");
- mImpl->mCachedRenderer = mFactoryCache.SaveRenderer( mImpl->mCachedRendererKey, mImpl->mRenderer );
+ mFactoryCache.SaveRenderer( mImpl->mCachedRendererKey, mImpl->mRenderer );
}
}
}
{
if( !mImpl->mCachedRendererKey.empty() && !mImpl->mCustomShader )
{
- mImpl->mCachedRenderer = mFactoryCache.GetRenderer( mImpl->mCachedRendererKey );
- if( !mImpl->mCachedRenderer || !mImpl->mCachedRenderer->mRenderer )
+ mImpl->mRenderer = mFactoryCache.GetRenderer( mImpl->mCachedRendererKey );
+ if( !mImpl->mRenderer )
{
InitializeRenderer( mImpl->mRenderer );
- mImpl->mCachedRenderer = mFactoryCache.SaveRenderer( mImpl->mCachedRendererKey, mImpl->mRenderer );
- }
-
- if( mImpl->mCachedRenderer && mImpl->mCachedRenderer->mRenderer )
- {
- mImpl->mRenderer = mImpl->mCachedRenderer->mRenderer;
+ mFactoryCache.SaveRenderer( mImpl->mCachedRendererKey, mImpl->mRenderer );
}
}
if( mImpl->mIsOnStage )
{
DoSetOffStage( actor );
-
- //remove the cached renderer from the cache if we and the cache are the only things that hold a reference to it
- if( mImpl->mCachedRenderer && mImpl->mCachedRenderer->ReferenceCount() == 2 )
- {
- mFactoryCache.RemoveRenderer( mImpl->mCachedRenderer->mKey );
- }
- mImpl->mCachedRenderer.Reset();
-
actor.RemoveRenderer( mImpl->mRenderer );
mImpl->mRenderer.Reset();
+ //clean the renderer from the cache since it may no longer be in use
+ mFactoryCache.CleanRendererCache( mImpl->mCachedRendererKey );
+
mImpl->mIsOnStage = false;
}
}
)
};
-Sampler::WrapMode GetWrapMode( Gradient::SpreadMethod spread )
+Dali::WrapMode::Type GetWrapMode( Gradient::SpreadMethod spread )
{
switch(spread)
{
case Gradient::REPEAT:
{
- return Sampler::REPEAT;
+ return Dali::WrapMode::REPEAT;
}
case Gradient::REFLECT:
{
- return Sampler::MIRRORED_REPEAT;
+ return Dali::WrapMode::MIRRORED_REPEAT;
}
case Gradient::PAD:
default:
{
- return Sampler::CLAMP_TO_EDGE;
+ return Dali::WrapMode::CLAMP_TO_EDGE;
}
}
}
}
Dali::BufferImage lookupTexture = mGradient->GenerateLookupTexture();
- Sampler sampler = Sampler::New( lookupTexture, UNIFORM_TEXTULRE_NAME );
- Sampler::WrapMode wrap = GetWrapMode( mGradient->GetSpreadMethod() );
+ Sampler sampler = Sampler::New();
+ Dali::WrapMode::Type wrap = GetWrapMode( mGradient->GetSpreadMethod() );
sampler.SetWrapMode( wrap, wrap );
- material.AddSampler( sampler );
+ material.AddTexture( lookupTexture, UNIFORM_TEXTULRE_NAME, sampler );
renderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
}
{
if( !mImageUrl.empty() && !mImage )
{
- mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ Dali::ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+
+ mImage = resourceImage;
}
ApplyImageToSampler();
if( !mImageUrl.empty() && mImpl->mIsOnStage )
{
- mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ Dali::ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+ mImage = resourceImage;
+
ApplyImageToSampler();
}
else
Material material = mImpl->mRenderer.GetMaterial();
if( material )
{
- for( std::size_t i = 0; i < material.GetNumberOfSamplers(); ++i )
+ int index = material.GetTextureIndex(TEXTURE_UNIFORM_NAME);
+ if( index != -1 )
{
- Sampler sampler = material.GetSamplerAt( i );
- if( sampler.GetUniformName() == TEXTURE_UNIFORM_NAME )
- {
- sampler.SetImage( mImage );
- return;
- }
+ material.SetTextureImage( index, mImage );
+ return;
}
- Sampler sampler = Sampler::New( mImage, TEXTURE_UNIFORM_NAME );
- material.AddSampler( sampler );
+ material.AddTexture( mImage,TEXTURE_UNIFORM_NAME );
+ }
+ }
+}
+
+void ImageRenderer::OnImageLoaded( ResourceImage image )
+{
+ if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
+ {
+ mImage = RendererFactory::GetBrokenRendererImage();
+ if( mImpl->mIsOnStage )
+ {
+ ApplyImageToSampler();
}
}
}
// EXTERNAL INCLUDES
#include <dali/public-api/images/image.h>
#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/images/resource-image.h>
namespace Dali
{
* "default"
*
*/
-class ImageRenderer: public ControlRenderer
+class ImageRenderer: public ControlRenderer, public ConnectionTracker
{
public:
*/
void ApplyImageToSampler();
+ /**
+ * Callback function of image resource loading succeed
+ * @param[in] image The Image content that we attempted to load from mImageUrl
+ */
+ void OnImageLoaded( ResourceImage image );
+
private:
Image mImage;
std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
+ attribute mediump vec2 aPosition;\n
+ varying mediump vec2 vTexCoord;\n
+ uniform mediump mat4 uMvpMatrix;\n
+ uniform mediump vec3 uSize;\n
+ uniform mediump vec2 uNinePatchFactorsX[ FACTOR_SIZE_X ];\n
+ uniform mediump vec2 uNinePatchFactorsY[ FACTOR_SIZE_Y ];\n
+ \n
+ void main()\n
+ {\n
+ mediump vec2 fixedFactor = vec2( uNinePatchFactorsX[ int( ( aPosition.x + 1.0 ) * 0.5 ) ].x, uNinePatchFactorsY[ int( ( aPosition.y + 1.0 ) * 0.5 ) ].x );\n
+ mediump vec2 stretch = vec2( uNinePatchFactorsX[ int( ( aPosition.x ) * 0.5 ) ].y, uNinePatchFactorsY[ int( ( aPosition.y ) * 0.5 ) ].y );\n
+ \n
+ mediump vec2 fixedTotal = vec2( uNinePatchFactorsX[ FACTOR_SIZE_X - 1 ].x, uNinePatchFactorsY[ FACTOR_SIZE_Y - 1 ].x );\n
+ mediump vec2 stretchTotal = vec2( uNinePatchFactorsX[ FACTOR_SIZE_X - 1 ].y, uNinePatchFactorsY[ FACTOR_SIZE_Y - 1 ].y );\n
+ \n
+ mediump vec4 vertexPosition = vec4( ( fixedFactor + ( uSize.xy - fixedTotal ) * stretch / stretchTotal ), 0.0, 1.0 );\n
+ vertexPosition.xy -= uSize.xy * vec2( 0.5, 0.5 );\n
+ vertexPosition = uMvpMatrix * vertexPosition;\n
+ \n
+ vTexCoord = ( fixedFactor + stretch ) / ( fixedTotal + stretchTotal );\n
+ \n
+ gl_Position = vertexPosition;\n
+ }\n
+);
+
const char* VERTEX_SHADER_3X3 = DALI_COMPOSE_SHADER(
attribute mediump vec2 aPosition;\n
varying mediump vec2 vTexCoord;\n
vertices.PushBack( Vector2( x, y ) );
}
+void RegisterStretchProperties( Material& material, const char * uniformName, const NinePatchImage::StretchRanges& stretchPixels, uint16_t imageExtent)
+{
+ uint16_t prevEnd = 0;
+ uint16_t prevFix = 0;
+ uint16_t prevStretch = 0;
+ unsigned int i = 1;
+ for( NinePatchImage::StretchRanges::ConstIterator it = stretchPixels.Begin(); it != stretchPixels.End(); ++it, ++i )
+ {
+ uint16_t start = it->GetX();
+ uint16_t end = it->GetY();
+
+ uint16_t fix = prevFix + start - prevEnd;
+ uint16_t stretch = prevStretch + end - start;
+
+ std::stringstream uniform;
+ uniform << uniformName << "[" << i << "]";
+ material.RegisterProperty( uniform.str(), Vector2( fix, stretch ) );
+
+ prevEnd = end;
+ prevFix = fix;
+ prevStretch = stretch;
+ }
+
+ {
+ prevFix += imageExtent - prevEnd;
+ std::stringstream uniform;
+ uniform << uniformName << "[" << i << "]";
+ material.RegisterProperty( uniform.str(), Vector2( prevFix, prevStretch ) );
+ }
+}
+
} //unnamed namespace
/////////////////NPatchRenderer////////////////
if( imageURLValue->Get( mImageUrl ) )
{
NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
- InitialiseFromImage( nPatch );
+ InitializeFromImage( nPatch );
}
else
{
- CreateErrorImage();
+ InitializeFromBrokenImage();
DALI_LOG_ERROR( "The property '%s' is not a string\n", IMAGE_URL_NAME );
}
}
{
naturalSize.x = mImage.GetWidth();
naturalSize.y = mImage.GetHeight();
- return;
}
else if( !mImageUrl.empty() )
{
ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
naturalSize.x = dimentions.GetWidth();
naturalSize.y = dimentions.GetHeight();
- return;
}
-
- naturalSize = Vector2::ZERO;
+ else
+ {
+ naturalSize = Vector2::ZERO;
+ }
}
void NPatchRenderer::SetClipRect( const Rect<int>& clipRect )
void NPatchRenderer::InitializeRenderer( Renderer& renderer )
{
Geometry geometry;
- if( !mBorderOnly )
+ Shader shader;
+ if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 )
{
- geometry = mFactoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY );
- if( !geometry )
+ if( !mBorderOnly )
{
- geometry = CreateGeometry( Uint16Pair( 3, 3 ) );
- mFactoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY, geometry );
+ geometry = mFactoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY );
+ if( !geometry )
+ {
+ geometry = CreateGeometry( Uint16Pair( 3, 3 ) );
+ mFactoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY, geometry );
+ }
}
- }
- else
- {
- geometry = mFactoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY );
- if( !geometry )
+ else
+ {
+ geometry = mFactoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY );
+ if( !geometry )
+ {
+ geometry = CreateGeometryBorder( Uint16Pair( 3, 3 ) );
+ mFactoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY, geometry );
+ }
+ }
+
+ shader = mFactoryCache.GetShader( RendererFactoryCache::NINE_PATCH_SHADER );
+ if( !shader )
{
- geometry = CreateGeometryBorder( Uint16Pair( 3, 3 ) );
- mFactoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY, geometry );
+ shader = Shader::New( VERTEX_SHADER_3X3, FRAGMENT_SHADER );
+ mFactoryCache.SaveShader( RendererFactoryCache::NINE_PATCH_SHADER, shader );
}
}
+ else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0)
+ {
+ Uint16Pair gridSize( 2 * mStretchPixelsX.Size() + 1, 2 * mStretchPixelsY.Size() + 1 );
+ geometry = !mBorderOnly ? CreateGeometry( gridSize ) : CreateGeometryBorder( gridSize );
+
+ std::stringstream vertexShader;
+ vertexShader << "#define FACTOR_SIZE_X " << mStretchPixelsX.Size() + 2 << "\n"
+ << "#define FACTOR_SIZE_Y " << mStretchPixelsY.Size() + 2 << "\n"
+ << VERTEX_SHADER;
- Shader shader = mFactoryCache.GetShader( RendererFactoryCache::NINE_PATCH_SHADER );
- if( !shader )
+ shader = Shader::New( vertexShader.str(), FRAGMENT_SHADER );
+ }
+ else
{
- shader = Shader::New( VERTEX_SHADER_3X3, FRAGMENT_SHADER );
- mFactoryCache.SaveShader( RendererFactoryCache::NINE_PATCH_SHADER, shader );
+ DALI_LOG_ERROR("The 9 patch image '%s' doesn't have any valid stretch borders and so is not a valid 9 patch image\n", mImageUrl.c_str() );
+ InitializeFromBrokenImage();
}
if( !renderer )
if( !mImageUrl.empty() )
{
NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
- InitialiseFromImage( nPatch );
+ InitializeFromImage( nPatch );
}
else if( mImage )
{
- InitialiseFromImage( mImage );
+ InitializeFromImage( mImage );
}
+
+ InitializeRenderer( mImpl->mRenderer );
}
if( mCroppedImage )
mImageUrl = imageUrl;
NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
- InitialiseFromImage( nPatch );
+ InitializeFromImage( nPatch );
if( mCroppedImage && mImpl->mIsOnStage )
{
}
mImage = image;
- InitialiseFromImage( mImage );
+ InitializeFromImage( mImage );
if( mCroppedImage && mImpl->mIsOnStage )
{
}
}
-void NPatchRenderer::InitialiseFromImage( NinePatchImage nPatch )
+void NPatchRenderer::InitializeFromImage( NinePatchImage nPatch )
{
mCroppedImage = nPatch.CreateCroppedBufferImage();
if( !mCroppedImage )
{
DALI_LOG_ERROR("'%s' specify a valid 9 patch image\n", mImageUrl.c_str() );
- CreateErrorImage();
+ InitializeFromBrokenImage();
return;
}
mStretchPixelsY = nPatch.GetStretchPixelsY();
}
-void NPatchRenderer::CreateErrorImage()
+void NPatchRenderer::InitializeFromBrokenImage()
{
- mImageSize = ImageDimensions( 1, 1 );
-
- BufferImage bufferImage = BufferImage::New( mImageSize.GetWidth(), mImageSize.GetHeight(), Pixel::RGBA8888 );
- mCroppedImage = bufferImage;
- PixelBuffer* pixbuf = bufferImage.GetBuffer();
-
- for( size_t i = 0; i < mImageSize.GetWidth() * mImageSize.GetHeight() * 4u; )
- {
- pixbuf[ i++ ] = 0;
- pixbuf[ i++ ] = 0;
- pixbuf[ i++ ] = 0;
- pixbuf[ i++ ] = 255;
- }
+ mCroppedImage = RendererFactory::GetBrokenRendererImage();
+ mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() );
mStretchPixelsX.Clear();
mStretchPixelsX.PushBack( Uint16Pair( 0, mImageSize.GetWidth() ) );
Material material = mImpl->mRenderer.GetMaterial();
if( material )
{
- Sampler sampler;
- for( std::size_t i = 0; i < material.GetNumberOfSamplers(); ++i )
+ int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
+ if( index > -1 )
{
- sampler = material.GetSamplerAt( i );
- if( sampler.GetUniformName() == TEXTURE_UNIFORM_NAME )
- {
- sampler.SetImage( mCroppedImage );
- break;
- }
+ material.SetTextureImage( index, mCroppedImage );
}
- if( !sampler )
+ else
{
- sampler = Sampler::New( mCroppedImage, TEXTURE_UNIFORM_NAME );
- material.AddSampler( sampler );
+ material.AddTexture( mCroppedImage, TEXTURE_UNIFORM_NAME );
}
- if( mStretchPixelsX.Size() > 0 && mStretchPixelsY.Size() > 0 )
+ if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 )
{
- //only 9 patch supported for now
+ //special case for 9 patch
Uint16Pair stretchX = mStretchPixelsX[ 0 ];
Uint16Pair stretchY = mStretchPixelsY[ 0 ];
uint16_t stretchWidth = stretchX.GetY() - stretchX.GetX();
uint16_t stretchHeight = stretchY.GetY() - stretchY.GetX();
- sampler.RegisterProperty( "uFixed[0]", Vector2::ZERO );
- sampler.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
- sampler.RegisterProperty( "uFixed[2]", Vector2( mImageSize.GetWidth() - stretchWidth, mImageSize.GetHeight() - stretchHeight ) );
- sampler.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
+ material.RegisterProperty( "uFixed[0]", Vector2::ZERO );
+ material.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
+ material.RegisterProperty( "uFixed[2]", Vector2( mImageSize.GetWidth() - stretchWidth, mImageSize.GetHeight() - stretchHeight ) );
+ material.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
+ }
+ else
+ {
+ material.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO );
+ material.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO );
+
+ RegisterStretchProperties( material, "uNinePatchFactorsX", mStretchPixelsX, mImageSize.GetWidth() );
+ RegisterStretchProperties( material, "uNinePatchFactorsY", mStretchPixelsY, mImageSize.GetHeight() );
}
}
}
*
* @param nPatchImage The NinePatchImage to base our cropped images and stretch borders from
*/
- void InitialiseFromImage( NinePatchImage nPatchImage );
+ void InitializeFromImage( NinePatchImage nPatchImage );
/**
- * @brief Creates a black Image to indicate that there was an error in either the image url or the parsing of the image
+ * @brief Creates an error Image to indicate that there was an error in either the image url or the parsing of the image
*
*/
- void CreateErrorImage();
+ void InitializeFromBrokenImage();
/**
* @brief Applies this renderer's image to the sampler to the material used for this renderer
NinePatchImage mImage; ///< The image to render if the renderer was set from an NinePatchImage, empty otherwise
Image mCroppedImage;
- Geometry mNinePatchGeometry;
- Geometry mNinePatchBorderGeometry;
std::string mImageUrl; ///< The url to the image resource to render if the renderer was set from an image resource url, empty otherwise
NinePatchImage::StretchRanges mStretchPixelsX;
if( it != mRendererHashes.End() )
{
int index = it - mRendererHashes.Begin();
- const CachedRendererPtr& cachedRenderer = mRenderers[ index ];
+ const CachedRenderer* cachedRenderer = mRenderers[ index ];
if( cachedRenderer && cachedRenderer->mKey == key )
{
return -1;
}
-RendererFactoryCache::CachedRendererPtr RendererFactoryCache::GetRenderer( const std::string& key ) const
+Renderer RendererFactoryCache::GetRenderer( const std::string& key ) const
{
int index = FindRenderer( key );
if( index != -1 )
{
- return mRenderers[ index ];
+ return mRenderers[ index ]->mRenderer.GetHandle();
}
else
{
- return CachedRendererPtr();
+ return Renderer();
}
}
-RendererFactoryCache::CachedRendererPtr RendererFactoryCache::SaveRenderer( const std::string& key, Renderer& renderer )
+void RendererFactoryCache::SaveRenderer( const std::string& key, Renderer& renderer )
{
int hash = Dali::CalculateHash( key );
- CachedRendererPtr newCachedRenderer = new CachedRenderer( key, renderer );
+ const CachedRenderer* cachedRenderer = new CachedRenderer( key, renderer );
- CachedRenderers::iterator it = std::find(mRenderers.begin(), mRenderers.end(), CachedRendererPtr() );
- if( it != mRenderers.end() )
+ CachedRenderers::Iterator it = std::find( mRenderers.Begin(), mRenderers.End(), static_cast< CachedRenderer* >( NULL ) );
+ if( it != mRenderers.End() )
{
- *it = newCachedRenderer;
- int index = it - mRenderers.begin();
+ *it = cachedRenderer;
+ int index = it - mRenderers.Begin();
mRendererHashes[ index ] = hash;
}
else
{
mRendererHashes.PushBack( hash );
- mRenderers.push_back( newCachedRenderer );
+ mRenderers.PushBack( cachedRenderer );
}
-
- return newCachedRenderer;
}
-void RendererFactoryCache::RemoveRenderer( const std::string& key )
+void RendererFactoryCache::CleanRendererCache( const std::string& key )
{
int index = FindRenderer( key );
if( index != -1 )
{
- mRendererHashes[ index ] = Dali::CalculateHash( "" );
- mRenderers[ index ].Reset();
+ const CachedRenderer*& cachedRenderer = mRenderers[ index ];
+ if( !cachedRenderer->mRenderer.GetHandle() )
+ {
+ mRendererHashes[ index ] = Dali::INITIAL_HASH_VALUE;
+
+ delete cachedRenderer;
+ cachedRenderer = NULL;
+ }
}
}
#include <dali/devel-api/rendering/geometry.h>
#include <dali/devel-api/rendering/shader.h>
#include <dali/devel-api/rendering/renderer.h>
+#include <dali/devel-api/common/owner-container.h>
+#include <dali/devel-api/object/weak-handle.h>
+
namespace Dali
{
static Geometry CreateQuadGeometry();
public:
- struct CachedRenderer : RefObject
- {
- std::string mKey;
- Renderer mRenderer;
-
- CachedRenderer( const std::string& key, const Renderer& renderer )
- : mKey( key ),
- mRenderer( renderer )
- {}
- };
-
- typedef IntrusivePtr< CachedRenderer > CachedRendererPtr;
/**
* @brief Request renderer from the url
*
- * @return The cached renderer if exist in the cache. Otherwise null is returned.
+ * @return The cached renderer if exist in the cache. Otherwise an empty handle is returned.
*/
- CachedRendererPtr GetRenderer( const std::string& key ) const;
+ Renderer GetRenderer( const std::string& key ) const;
/**
* @brief Cache the renderer based on the given key.
*
* @param[in] key The key to use for caching
* @param[in] renderer The Renderer to be cached
- *
- * @return The cached renderer stored in the cache
*/
- CachedRendererPtr SaveRenderer( const std::string& key, Renderer& renderer );
+ void SaveRenderer( const std::string& key, Renderer& renderer );
/**
- * @brief Removes the renderer from the cache based on the given key
+ * @brief Cleans the renderer cache by removing the renderer from the cache based on the given key if there are no longer any references to it
*
* @param[in] key The key used for caching
*/
- void RemoveRenderer( const std::string& key );
+ void CleanRendererCache( const std::string& key );
protected:
RendererFactoryCache& operator=(const RendererFactoryCache& rhs);
private:
+ struct CachedRenderer
+ {
+ std::string mKey;
+ WeakHandle< Renderer > mRenderer;
+
+ CachedRenderer( const std::string& key, Renderer& renderer )
+ : mKey( key ),
+ mRenderer( renderer)
+ {}
+ };
+
typedef Dali::Vector< std::size_t > HashVector;
- typedef std::vector< CachedRendererPtr > CachedRenderers;
+ typedef Dali::OwnerContainer< const CachedRenderer* > CachedRenderers;
/**
* @brief Finds the first index into the cached renderers from the url
const char * const GRADIENT_RENDERER("gradient-renderer");
const char * const IMAGE_RENDERER("image-renderer");
const char * const N_PATCH_RENDERER("n-patch-renderer");
+
+const char * const BROKEN_RENDERER_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
}
namespace Dali
return false;
}
+Image RendererFactory::GetBrokenRendererImage()
+{
+ return ResourceImage::New( BROKEN_RENDERER_IMAGE_URL );
+}
+
} // namespace Internal
} // namespace Toolkit
*/
bool ResetRenderer( Toolkit::ControlRenderer& renderer, const std::string& image );
+public:
+ /**
+ * @brief Returns an image to be used when a renderer has failed to correctly render
+ */
+ static Image GetBrokenRendererImage();
+
protected:
/**
ScrollBar::ScrollBar(Toolkit::ScrollBar::Direction direction)
: Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
+ mIndicatorShowAlpha(1.0f),
mDirection(direction),
mScrollableObject(WeakHandleBase()),
mPropertyScrollPosition(Property::INVALID_INDEX),
mIndicatorShowDuration(DEFAULT_INDICATOR_SHOW_DURATION),
mIndicatorHideDuration(DEFAULT_INDICATOR_HIDE_DURATION),
mScrollStart(0.0f),
- mIsPanning(false),
mCurrentScrollPosition(0.0f),
mIndicatorHeightPolicy(Toolkit::ScrollBar::Variable),
- mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT)
+ mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT),
+ mIsPanning(false),
+ mIndicatorFirstShow(true)
{
}
if( indicator )
{
mIndicator = indicator;
+ mIndicatorFirstShow = true;
Self().Add(mIndicator);
EnableGestureDetection(Gesture::Type(Gesture::Pan));
mAnimation.Reset();
}
+ if( mIndicatorFirstShow )
+ {
+ // Preserve the alpha value from the stylesheet
+ mIndicatorShowAlpha = Self().GetCurrentColor().a;
+ mIndicatorFirstShow = false;
+ }
+
if(mIndicatorShowDuration > 0.0f)
{
mAnimation = Animation::New( mIndicatorShowDuration );
- mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunction::EASE_IN );
+ mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), mIndicatorShowAlpha, AlphaFunction::EASE_IN );
mAnimation.Play();
}
else
{
- mIndicator.SetOpacity(1.0f);
+ mIndicator.SetOpacity(mIndicatorShowAlpha);
}
}
private:
Actor mIndicator; ///< Image of scroll indicator.
+ float mIndicatorShowAlpha; ///< The alpha value when the indicator is fully shown
Animation mAnimation; ///< Scroll indicator Show/Hide Animation.
Toolkit::ScrollBar::Direction mDirection; ///< The direction of scroll bar (vertical or horizontal)
float mScrollStart; ///< Scroll Start position (start of drag)
Vector3 mGestureDisplacement; ///< Gesture Displacement.
- bool mIsPanning; ///< Whether the scroll bar is being panned.
float mCurrentScrollPosition; ///< The current scroll position updated by the pan gesture
Toolkit::ScrollBar::IndicatorHeightPolicy mIndicatorHeightPolicy; ///< The height policy of scroll indicator (variable or fixed)
Constraint mIndicatorPositionConstraint;
Constraint mIndicatorSizeConstraint;
Constraint mScrollPositionInCurrentAxisConstraint;
+
+ bool mIsPanning : 1; ///< Whether the scroll bar is being panned.
+ bool mIndicatorFirstShow : 1; ///< True if the indicator has never been shown
};
} // namespace Internal
namespace
{
// Bouncing effect is presented by stacked three layers with same color and opacity
-const Vector3 LAYER_HEIGHTS( 1.f, 27.f/42.f, 13.f/42.f);
+const float LAYER_HEIGHTS[5] =
+{
+ 1.f,
+ 26.f * 4.f/ 130.f,
+ 26.f * 3.f / 130.f,
+ 26.f * 2.f / 130.f,
+ 26.f / 130.f
+};
#define MAKE_SHADER(A)#A
Vector3 position2;
};
// 4 vertices 2 triangles per layer. The depth interval between each layer is 0.01
- VertexPosition vertexData[12] = {
- //bottom layer
+ VertexPosition vertexData[20] = {
+ // bottom layer
{ Vector3( -0.5f, -0.5f, 0.f ), Vector3( -0.5f, -0.5f, 0.f ) },
{ Vector3( 0.5f, -0.5f, 0.f ), Vector3( 0.5f, -0.5f, 0.f ) },
{ Vector3( -0.5f, -0.5f, 0.f ), Vector3( -0.5f, -0.5f + LAYER_HEIGHTS[0], 0.f ) },
{ Vector3( 0.5f, -0.5f, 0.f ), Vector3( 0.5f, -0.5f+ LAYER_HEIGHTS[0], 0.f ) },
- // middle layer
+ // mid-bottom layer
{ Vector3( -0.5f, -0.5f, 0.01f ), Vector3( -0.5f, -0.5f, 0.01f ) },
{ Vector3( 0.5f, -0.5f, 0.01f ), Vector3( 0.5f, -0.5f, 0.01f ) },
{ Vector3( -0.5f, -0.5f, 0.01f ), Vector3( -0.5f, -0.5f + LAYER_HEIGHTS[1], 0.01f ) },
{ Vector3( 0.5f, -0.5f, 0.01f ), Vector3( 0.5f, -0.5f+ LAYER_HEIGHTS[1], 0.01f ) },
- // top layer
+ // middle layer
{ Vector3( -0.5f, -0.5f, 0.02f ), Vector3( -0.5f, -0.5f, 0.02f ) },
{ Vector3( 0.5f, -0.5f, 0.02f ), Vector3( 0.5f, -0.5f, 0.02f ) },
{ Vector3( -0.5f, -0.5f, 0.02f ), Vector3( -0.5f, -0.5f + LAYER_HEIGHTS[2], 0.02f ) },
- { Vector3( 0.5f, -0.5f, 0.02f ), Vector3( 0.5f, -0.5f+ LAYER_HEIGHTS[2], 0.02f ) }
+ { Vector3( 0.5f, -0.5f, 0.02f ), Vector3( 0.5f, -0.5f+ LAYER_HEIGHTS[2], 0.02f ) },
+ // mid-top layer
+ { Vector3( -0.5f, -0.5f, 0.03f ), Vector3( -0.5f, -0.5f, 0.03f ) },
+ { Vector3( 0.5f, -0.5f, 0.03f ), Vector3( 0.5f, -0.5f, 0.03f ) },
+ { Vector3( -0.5f, -0.5f, 0.03f ), Vector3( -0.5f, -0.5f + LAYER_HEIGHTS[3], 0.03f ) },
+ { Vector3( 0.5f, -0.5f, 0.03f ), Vector3( 0.5f, -0.5f+ LAYER_HEIGHTS[3], 0.03f ) },
+ // top layer
+ { Vector3( -0.5f, -0.5f, 0.04f ), Vector3( -0.5f, -0.5f, 0.04f ) },
+ { Vector3( 0.5f, -0.5f, 0.04f ), Vector3( 0.5f, -0.5f, 0.04f ) },
+ { Vector3( -0.5f, -0.5f, 0.04f ), Vector3( -0.5f, -0.5f + LAYER_HEIGHTS[4], 0.04f ) },
+ { Vector3( 0.5f, -0.5f, 0.04f ), Vector3( 0.5f, -0.5f+ LAYER_HEIGHTS[4], 0.04f ) }
};
Property::Map vertexFormat;
vertexFormat["aPosition1"] = Property::VECTOR3;
vertexFormat["aPosition2"] = Property::VECTOR3;
- PropertyBuffer vertices = PropertyBuffer::New( vertexFormat, 12u );
+ PropertyBuffer vertices = PropertyBuffer::New( vertexFormat, 20u );
vertices.SetData( vertexData );
- unsigned int indexData[18] = { 0,3,1,0,2,3,4,7,5,4,6,7,8,11,9,8,10,11 };
+ unsigned int indexData[30] = { 0,3,1,0,2,3,4,7,5,4,6,7,8,11,9,8,10,11,12,15,13,12,14,15,16,19,17,16,18,19};
Property::Map indexFormat;
indexFormat["indices"] = Property::INTEGER;
- PropertyBuffer indices = PropertyBuffer::New( indexFormat, 18u );
+ PropertyBuffer indices = PropertyBuffer::New( indexFormat, 30u );
indices.SetData( indexData );
Geometry meshGeometry = Geometry::New();
const float MILLISECONDS_PER_SECONDS = 1000.0f;
-const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
}
// Overshoot overlay constraints
-void OvershootOverlaySizeConstraint( Vector3& current, const PropertyInputContainer& inputs )
+struct OvershootOverlaySizeConstraint
{
- const Vector2& parentScrollDirection = inputs[0]->GetVector2();
- const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
- const Vector3& parentSize = inputs[2]->GetVector3();
-
- if(Toolkit::IsVertical(layoutOrientation))
+ OvershootOverlaySizeConstraint( float height )
+ : mOvershootHeight( height )
{
- current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
}
- else
+
+ void operator()( Vector3& current, const PropertyInputContainer& inputs )
{
- current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
+ const Vector2& parentScrollDirection = inputs[0]->GetVector2();
+ const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
+ const Vector3& parentSize = inputs[2]->GetVector3();
+
+ if(Toolkit::IsVertical(layoutOrientation))
+ {
+ current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
+ }
+ else
+ {
+ current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
+ }
+
+ current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? mOvershootHeight : mOvershootHeight*0.5f;
}
- current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height*0.5f;
-}
+ float mOvershootHeight;
+};
void OvershootOverlayRotationConstraint( Quaternion& current, const PropertyInputContainer& inputs )
{
{
Actor self = Self();
- SetOvershootEnabled(true);
-
Vector2 stageSize = Stage::GetCurrent().GetSize();
mWheelScrollDistanceStep = stageSize.y * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
- if( (firstItemScrollPosition >= 0.0f && currentOvershoot < 1.0f) || (firstItemScrollPosition >= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) && currentOvershoot > -1.0f) )
+ if( ( firstItemScrollPosition >= 0.0f &&
+ currentOvershoot < 1.0f ) ||
+ ( firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) &&
+ currentOvershoot > -1.0f ) )
{
mTotalPanDisplacement += gesture.displacement;
}
mOvershootOverlay.SetDrawMode( DrawMode::OVERLAY_2D );
self.Add(mOvershootOverlay);
- Constraint constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint );
+ Constraint constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint(mOvershootSize.height) );
constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
constraint.Apply();
- mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
+ mOvershootOverlay.SetSize(mOvershootSize.width, mOvershootSize.height);
constraint = Constraint::New<Quaternion>( mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint );
constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
namespace
{
-const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
+
const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
// local helper function to resize the height of the bounce actor
-float GetBounceActorHeight( float width )
+float GetBounceActorHeight( float width, float defaultHeight )
{
- return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height * 0.5f;
+ return (width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? defaultHeight : defaultHeight * 0.5f;
}
const float MAX_OVERSHOOT_NOTIFY_AMOUNT = 0.99f; // maximum amount to set notification for increased overshoot, beyond this we just wait for it to reduce again
mOvershootProperty(Property::INVALID_INDEX),
mEffectOvershootProperty(Property::INVALID_INDEX),
mOvershoot(0.0f),
+ mOvershootSize( scrollable.GetOvershootSize() ),
mAnimationStateFlags(0)
{
mOvershootOverlay = CreateBouncingEffectActor(mEffectOvershootProperty);
mOvershootProperty = IsVertical() ? Toolkit::ScrollView::Property::OVERSHOOT_Y : Toolkit::ScrollView::Property::OVERSHOOT_X;
// make sure height is set, since we only create a constraint for image width
- mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
+ mOvershootOverlay.SetSize( mOvershootSize );
mAttachedScrollView.AddOverlay(mOvershootOverlay);
if(IsVertical())
{
mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.0f ), Vector3::ZAXIS ) );
- mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
+ mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth);
}
else
{
mOvershootOverlay.SetOrientation( Quaternion( Radian( 1.5f * Math::PI ), Vector3::ZAXIS ) );
- mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
+ mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth);
relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
}
mOvershootOverlay.SetPosition(relativeOffset * parentSize);
if(IsVertical())
{
mOvershootOverlay.SetOrientation( Quaternion( Radian( Math::PI ), Vector3::ZAXIS ) );
- mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width), size.depth);
+ mOvershootOverlay.SetSize(parentSize.width, GetBounceActorHeight(parentSize.width, mOvershootSize.height), size.depth);
relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
}
else
{
mOvershootOverlay.SetOrientation( Quaternion( Radian( 0.5f * Math::PI ), Vector3::ZAXIS ) );
- mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height), size.depth);
+ mOvershootOverlay.SetSize(parentSize.height, GetBounceActorHeight(parentSize.height, mOvershootSize.height), size.depth);
relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
}
mOvershootOverlay.SetPosition(relativeOffset * parentSize);
PropertyNotification mOvershootDecreaseNotification;///< notification used to inform as overshoot decreases
Property::Index mOvershootProperty; ///< index of the overshoot property in the scrollable actor
Property::Index mEffectOvershootProperty; ///< index of the effect's overshoot property
- float mOvershoot; ///< last overshoot value as detected by notifications
- unsigned short mAnimationStateFlags; ///< contains flags indicating the current state of the overshoot animation
+ float mOvershoot; ///< last overshoot value as detected by notifications
+ Vector2 mOvershootSize; ///< The size of the overshoot effect
+ unsigned short mAnimationStateFlags; ///< contains flags indicating the current state of the overshoot animation
};
} // namespace Internal
*/
struct InternalPrePositionConstraint
{
- InternalPrePositionConstraint(const Vector2& initialPanPosition,
- const Vector2& initialPanMask,
- bool axisAutoLock,
- float axisAutoLockGradient,
- ScrollView::LockAxis initialLockAxis,
- const Vector2& maxOvershoot,
- const RulerDomain& domainX, const RulerDomain& domainY)
- : mLocalStart(initialPanPosition),
- mInitialPanMask(initialPanMask),
- mDomainMin( -domainX.min, -domainY.min ),
- mDomainMax( -domainX.max, -domainY.max ),
- mMaxOvershoot(maxOvershoot),
- mAxisAutoLockGradient(axisAutoLockGradient),
- mLockAxis(initialLockAxis),
- mAxisAutoLock(axisAutoLock),
- mWasPanning(false),
- mClampX( domainX.enabled ),
- mClampY( domainY.enabled )
- {
+ InternalPrePositionConstraint( const Vector2& initialPanPosition,
+ const Vector2& initialPanMask,
+ bool axisAutoLock,
+ float axisAutoLockGradient,
+ ScrollView::LockAxis initialLockAxis,
+ const Vector2& maxOvershoot,
+ const RulerPtr& rulerX, const RulerPtr& rulerY )
+ : mLocalStart( initialPanPosition ),
+ mInitialPanMask( initialPanMask ),
+ mMaxOvershoot( maxOvershoot ),
+ mAxisAutoLockGradient( axisAutoLockGradient ),
+ mLockAxis( initialLockAxis ),
+ mAxisAutoLock( axisAutoLock ),
+ mWasPanning( false )
+ {
+ const RulerDomain& rulerDomainX = rulerX->GetDomain();
+ const RulerDomain& rulerDomainY = rulerY->GetDomain();
+ mDomainMin = Vector2( rulerDomainX.min, -rulerDomainY.min );
+ mDomainMax = Vector2( -rulerDomainX.max, -rulerDomainY.max );
+ mClampX = rulerDomainX.enabled;
+ mClampY = rulerDomainY.enabled;
+ mFixedRulerX = rulerX->GetType() == Ruler::Fixed;
+ mFixedRulerY = rulerY->GetType() == Ruler::Fixed;
}
void operator()( Vector2& scrollPostPosition, const PropertyInputContainer& inputs )
{
const Vector2& panPosition = inputs[0]->GetVector2();
+ const bool& inGesture = inputs[1]->GetBoolean();
- if(!mWasPanning)
+ // First check if we are within a gesture.
+ // The ScrollView may have received a start gesture from ::OnPan()
+ // while the finish gesture is received now in this constraint.
+ // This gesture must then be rejected as the value will be "old".
+ // Typically the last value from the end of the last gesture.
+ // If we are rejecting the gesture, we simply don't modify the constraint target.
+ if( inGesture )
{
- mPrePosition = scrollPostPosition;
- mCurrentPanMask = mInitialPanMask;
- mWasPanning = true;
- }
-
- // Calculate Deltas...
- const Vector2& currentPosition = panPosition;
- Vector2 panDelta( currentPosition - mLocalStart );
-
- // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
- // appears mostly horizontal or mostly vertical respectively...
- if( mAxisAutoLock )
- {
- mLockAxis = GetLockAxis(panDelta, mLockAxis, mAxisAutoLockGradient);
- if( mLockAxis == ScrollView::LockVertical )
+ if( !mWasPanning )
{
- mCurrentPanMask.y = 0.0f;
+ mPrePosition = scrollPostPosition;
+ mStartPosition = mPrePosition;
+ mCurrentPanMask = mInitialPanMask;
+ mWasPanning = true;
}
- else if( mLockAxis == ScrollView::LockHorizontal )
+
+ // Calculate Deltas...
+ const Vector2& currentPosition = panPosition;
+ Vector2 panDelta( currentPosition - mLocalStart );
+
+ // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan
+ // appears mostly horizontal or mostly vertical respectively...
+ if( mAxisAutoLock )
{
- mCurrentPanMask.x = 0.0f;
+ mLockAxis = GetLockAxis( panDelta, mLockAxis, mAxisAutoLockGradient );
+ if( mLockAxis == ScrollView::LockVertical )
+ {
+ mCurrentPanMask.y = 0.0f;
+ }
+ else if( mLockAxis == ScrollView::LockHorizontal )
+ {
+ mCurrentPanMask.x = 0.0f;
+ }
}
- }
- // Restrict deltas based on ruler enable/disable and axis-lock state...
- panDelta *= mCurrentPanMask;
+ // Restrict deltas based on ruler enable/disable and axis-lock state...
+ panDelta *= mCurrentPanMask;
- // Perform Position transform based on input deltas...
- scrollPostPosition = mPrePosition;
- scrollPostPosition += panDelta;
+ // Perform Position transform based on input deltas...
+ scrollPostPosition = mPrePosition;
+ scrollPostPosition += panDelta;
- // if no wrapping then clamp preposition to maximum overshoot amount
- const Vector3& size = inputs[1]->GetVector3();
- if( mClampX )
- {
- float newXPosition = Clamp(scrollPostPosition.x, (mDomainMax.x + size.x) - mMaxOvershoot.x, mDomainMin.x + mMaxOvershoot.x );
- if( (newXPosition < scrollPostPosition.x - Math::MACHINE_EPSILON_1)
- || (newXPosition > scrollPostPosition.x + Math::MACHINE_EPSILON_1) )
+ // if no wrapping then clamp preposition to maximum overshoot amount
+ const Vector3& size = inputs[2]->GetVector3();
+ if( mClampX )
{
- mPrePosition.x = newXPosition;
- mLocalStart.x = panPosition.x;
+ float newXPosition = Clamp( scrollPostPosition.x, ( mDomainMax.x + size.x ) - mMaxOvershoot.x, mDomainMin.x + mMaxOvershoot.x );
+ if( (newXPosition < scrollPostPosition.x - Math::MACHINE_EPSILON_1)
+ || (newXPosition > scrollPostPosition.x + Math::MACHINE_EPSILON_1) )
+ {
+ mPrePosition.x = newXPosition;
+ mLocalStart.x = panPosition.x;
+ }
+ scrollPostPosition.x = newXPosition;
}
- scrollPostPosition.x = newXPosition;
- }
- if( mClampY )
- {
- float newYPosition = Clamp(scrollPostPosition.y, (mDomainMax.y + size.y) - mMaxOvershoot.y, mDomainMin.y + mMaxOvershoot.y );
- if( (newYPosition < scrollPostPosition.y - Math::MACHINE_EPSILON_1)
- || (newYPosition > scrollPostPosition.y + Math::MACHINE_EPSILON_1) )
+ if( mClampY )
{
- mPrePosition.y = newYPosition;
- mLocalStart.y = panPosition.y;
+ float newYPosition = Clamp( scrollPostPosition.y, ( mDomainMax.y + size.y ) - mMaxOvershoot.y, mDomainMin.y + mMaxOvershoot.y );
+ if( ( newYPosition < scrollPostPosition.y - Math::MACHINE_EPSILON_1 )
+ || ( newYPosition > scrollPostPosition.y + Math::MACHINE_EPSILON_1 ) )
+ {
+ mPrePosition.y = newYPosition;
+ mLocalStart.y = panPosition.y;
+ }
+ scrollPostPosition.y = newYPosition;
+ }
+
+ // If we are using a fixed ruler in a particular axis, limit the maximum pages scrolled on that axis.
+ if( mFixedRulerX || mFixedRulerY )
+ {
+ // Here we limit the maximum amount that can be moved from the starting position of the gesture to one page.
+ // We do this only if we have a fixed ruler (on that axis) and the mode is enabled.
+ // Note: 1.0f is subtracted to keep the value within one page size (otherwise we stray on to the page after).
+ // Note: A further 1.0f is subtracted to handle a compensation that happens later within the flick handling code in SnapWithVelocity().
+ // When a flick is completed, an adjustment of 1.0f is sometimes made to allow for the scenario where:
+ // A flick finishes before the update thread has advanced the scroll position past the previous snap point.
+ Vector2 pageSizeLimit( size.x - ( 1.0f + 1.0f ), size.y - ( 1.0f - 1.0f ) );
+ Vector2 minPosition( mStartPosition.x - pageSizeLimit.x, mStartPosition.y - pageSizeLimit.y );
+ Vector2 maxPosition( mStartPosition.x + pageSizeLimit.x, mStartPosition.y + pageSizeLimit.y );
+
+ if( mFixedRulerX )
+ {
+ scrollPostPosition.x = Clamp( scrollPostPosition.x, minPosition.x, maxPosition.x );
+ }
+ if( mFixedRulerY )
+ {
+ scrollPostPosition.y = Clamp( scrollPostPosition.y, minPosition.y, maxPosition.y );
+ }
}
- scrollPostPosition.y = newYPosition;
}
}
Vector2 mPrePosition;
Vector2 mLocalStart;
- Vector2 mInitialPanMask; ///< Initial pan mask (based on ruler settings)
+ Vector2 mStartPosition; ///< The start position of the gesture - used to limit scroll amount (not modified by clamping).
+ Vector2 mInitialPanMask; ///< Initial pan mask (based on ruler settings).
Vector2 mCurrentPanMask; ///< Current pan mask that can be altered by axis lock mode.
Vector2 mDomainMin;
Vector2 mDomainMax;
float mAxisAutoLockGradient; ///< Set by ScrollView
ScrollView::LockAxis mLockAxis;
- bool mAxisAutoLock:1; ///< Set by ScrollView
+ bool mAxisAutoLock:1; ///< Set by ScrollView
bool mWasPanning:1;
bool mClampX:1;
bool mClampY:1;
+ bool mFixedRulerX:1;
+ bool mFixedRulerY:1;
};
/**
mMaxFlickSpeed(DEFAULT_MAX_FLICK_SPEED),
mWheelScrollDistanceStep(Vector2::ZERO),
mInAccessibilityPan(false),
- mInitialized(false),
mScrolling(false),
mScrollInterrupted(false),
mPanning(false),
mWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
- mInitialized = true;
-
mGestureStackDepth = 0;
EnableGestureDetection( Gesture::Type( Gesture::Pan ) );
mRulerX = ruler;
mRulerY = ruler;
- SetOvershootEnabled(true);
-
self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, mCanScrollVertical);
self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, mCanScrollHorizontal);
void ScrollView::OnStageConnection( int depth )
{
+ ScrollBase::OnStageConnection( depth );
+
DALI_LOG_SCROLL_STATE("[0x%X]", this);
if ( mSensitive )
DALI_LOG_SCROLL_STATE("[0x%X]", this);
StopAnimation();
+
+ ScrollBase::OnStageDisconnection();
}
ScrollView::~ScrollView()
void ScrollView::EnableScrollOvershoot(bool enable)
{
- if(enable && !mOvershootIndicator)
- {
- mOvershootIndicator = ScrollOvershootIndicator::New();
- }
- if( enable )
+ if (enable)
{
+ if (!mOvershootIndicator)
+ {
+ mOvershootIndicator = ScrollOvershootIndicator::New();
+ }
+
mOvershootIndicator->AttachToScrollable(*this);
}
else
{
mMaxOvershoot = mUserMaxOvershoot;
- mOvershootIndicator->DetachFromScrollable(*this);
+
+ if (mOvershootIndicator)
+ {
+ mOvershootIndicator->DetachFromScrollable(*this);
+ }
}
+
UpdateMainInternalConstraint();
}
mAxisAutoLockGradient,
mLockAxis,
mMaxOvershoot,
- mRulerX->GetDomain(),
- mRulerY->GetDomain() ) );
+ mRulerX,
+ mRulerY ) );
mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::LOCAL_POSITION ) );
+ mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::PANNING ) );
mScrollMainInternalPrePositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) );
mScrollMainInternalPrePositionConstraint.Apply();
}
Toolkit::ScrollView::SnapStartedSignalType mSnapStartedSignal;
- bool mInAccessibilityPan : 1; ///< With AccessibilityPan its easier to move between snap positions
- bool mInitialized:1;
+ bool mInAccessibilityPan:1; ///< With AccessibilityPan its easier to move between snap positions
bool mScrolling:1; ///< Flag indicating whether the scroll view is being scrolled (by user or animation)
bool mScrollInterrupted:1; ///< Flag set for when a down event interrupts a scroll
bool mPanning:1; ///< Whether scroll view is currently panning or not
DALI_PROPERTY_REGISTRATION( Toolkit, Scrollable, "overshoot-effect-color", VECTOR4, OVERSHOOT_EFFECT_COLOR )
DALI_PROPERTY_REGISTRATION( Toolkit, Scrollable, "overshoot-animation-speed", FLOAT, OVERSHOOT_ANIMATION_SPEED )
+const int OVERSHOOT_SIZE = Dali::Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED + 1; // OVERSHOOT_SIZE is not public yet
+Dali::PropertyRegistration p1( typeRegistration, "overshoot-size", OVERSHOOT_SIZE, Property::VECTOR2, Dali::Toolkit::Internal::Scrollable::SetProperty, Dali::Toolkit::Internal::Scrollable::GetProperty );
DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, Scrollable, "scroll-relative-position", VECTOR2, SCROLL_RELATIVE_POSITION)
DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, Scrollable, "scroll-position-min", VECTOR2, SCROLL_POSITION_MIN)
const Vector4 DEFAULT_OVERSHOOT_COLOUR(0.0f, 0.64f, 0.85f, 0.25f);
const float DEFAULT_OVERSHOOT_ANIMATION_SPEED(120.0f); // 120 pixels per second
+const Vector2 OVERSHOOT_DEFAULT_SIZE( 720.0f, 42.0f );
}
: Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS | DISABLE_SIZE_NEGOTIATION ) ),
mOvershootEffectColor( DEFAULT_OVERSHOOT_COLOUR ),
mOvershootAnimationSpeed ( DEFAULT_OVERSHOOT_ANIMATION_SPEED ),
- mOvershootEnabled(false)
+ mOvershootSize( OVERSHOOT_DEFAULT_SIZE ),
+ mOvershootEnabled(true)
{
}
: Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS | behaviourFlags ) ),
mOvershootEffectColor( DEFAULT_OVERSHOOT_COLOUR ),
mOvershootAnimationSpeed ( DEFAULT_OVERSHOOT_ANIMATION_SPEED ),
- mOvershootEnabled(false)
+ mOvershootSize( OVERSHOOT_DEFAULT_SIZE ),
+ mOvershootEnabled(true)
{
}
return mOvershootAnimationSpeed;
};
+const Vector2& Scrollable::GetOvershootSize() const
+{
+ return mOvershootSize;
+}
+
Toolkit::Scrollable::ScrollStartedSignalType& Scrollable::ScrollStartedSignal()
{
return mScrollStartedSignal;
scrollableImpl.SetOvershootAnimationSpeed( value.Get<float>() );
break;
}
+ case OVERSHOOT_SIZE: // OVERSHOOT_SIZE is not public yet
+ {
+ Vector2 input;
+ if( value.Get( input ) )
+ {
+ scrollableImpl.mOvershootSize = input;
+ }
+ scrollableImpl.EnableScrollOvershoot( scrollableImpl.IsOvershootEnabled() );
+ break;
+ }
}
}
}
value = scrollableImpl.GetOvershootAnimationSpeed();
break;
}
+ case OVERSHOOT_SIZE: // OVERSHOOT_SIZE is not public yet
+ {
+ value = scrollableImpl.mOvershootSize;
+ break;
+ }
}
}
*/
float GetOvershootAnimationSpeed() const;
+ /**
+ * @copydoc Dali::Toolkit::Scrollable::GetOvershootSize()
+ */
+ const Vector2& GetOvershootSize() const;
+
private:
/**
Vector4 mOvershootEffectColor; ///<The color of the overshoot bouncing effect
float mOvershootAnimationSpeed; ///<The speed of the overshoot animation (pixels per second)
+ Vector2 mOvershootSize; ///<The size of the overshoot effect
Toolkit::Scrollable::ScrollStartedSignalType mScrollStartedSignal;
Toolkit::Scrollable::ScrollUpdatedSignalType mScrollUpdatedSignal;
# Add local source files here
toolkit_src_files = \
- $(toolkit_src_dir)/atlas-manager/atlas-manager.cpp \
- $(toolkit_src_dir)/atlas-manager/atlas-manager-impl.cpp \
$(toolkit_src_dir)/builder/builder-actor.cpp \
$(toolkit_src_dir)/builder/builder-animations.cpp \
$(toolkit_src_dir)/builder/builder-impl.cpp \
$(toolkit_src_dir)/text/rendering/atlas/text-atlas-renderer.cpp \
$(toolkit_src_dir)/text/rendering/atlas/atlas-glyph-manager.cpp \
$(toolkit_src_dir)/text/rendering/atlas/atlas-glyph-manager-impl.cpp \
+ $(toolkit_src_dir)/text/rendering/atlas/atlas-manager.cpp \
+ $(toolkit_src_dir)/text/rendering/atlas/atlas-manager-impl.cpp \
+ $(toolkit_src_dir)/text/rendering/atlas/atlas-mesh-factory.cpp \
$(toolkit_src_dir)/text/rendering/text-backend-impl.cpp \
$(toolkit_src_dir)/transition-effects/cube-transition-effect-impl.cpp \
$(toolkit_src_dir)/transition-effects/cube-transition-cross-effect-impl.cpp \
// Local Data
namespace
{
-const int DEFAULT_POPUP_OFFSET( -100.0f ); // Vertical offset of Popup from cursor or handles position.
-
const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
struct PopupImpl
{
PopupImpl()
- : position(),
- offset( DEFAULT_POPUP_OFFSET )
+ : position()
{
}
TextSelectionPopup actor;
Vector3 position;
- int offset;
};
Impl( ControllerInterface& controller,
*/
void Relayout( const Vector2& size )
{
+ mControlSize = size;
+
// TODO - Remove this if nothing is active
CreateActiveLayer();
if( mPrimaryCursor )
{
const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
- mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= size.width ) && ( cursor.position.x >= 0.f );
+ mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
if( mPrimaryCursorVisible )
{
mPrimaryCursor.SetPosition( cursor.position.x,
if( mSecondaryCursor )
{
const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
- mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= size.width ) && ( cursor.position.x >= 0.f );
+ mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
if( mSecondaryCursorVisible )
{
mSecondaryCursor.SetPosition( cursor.position.x,
// Show or hide the grab handle
HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ bool newGrabHandlePosition = false;
if( grabHandle.active )
{
- const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= size.width ) && ( grabHandle.position.x >= 0.f );
+ const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) && ( grabHandle.position.x >= 0.f );
if( isVisible )
{
// Sets the grab handle image according if it's pressed, flipped, etc.
SetHandleImage( GRAB_HANDLE );
+
+ newGrabHandlePosition = true;
}
if( grabHandle.actor )
// Show or hide the selection handles/highlight
HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
+ bool newPrimaryHandlePosition = false;
+ bool newSecondaryHandlePosition = false;
if( primary.active || secondary.active )
{
- const bool isPrimaryVisible = ( primary.position.x <= size.width ) && ( primary.position.x >= 0.f );
- const bool isSecondaryVisible = ( secondary.position.x <= size.width ) && ( secondary.position.x >= 0.f );
+ const bool isPrimaryVisible = ( primary.position.x <= mControlSize.width ) && ( primary.position.x >= 0.f );
+ const bool isSecondaryVisible = ( secondary.position.x <= mControlSize.width ) && ( secondary.position.x >= 0.f );
if( isPrimaryVisible || isSecondaryVisible )
{
SetHandleImage( LEFT_SELECTION_HANDLE );
SetSelectionHandleMarkerSize( primary );
+
+ newPrimaryHandlePosition = true;
}
if( isSecondaryVisible )
SetHandleImage( RIGHT_SELECTION_HANDLE );
SetSelectionHandleMarkerSize( secondary );
+
+ newSecondaryHandlePosition = true;
}
}
}
}
+ if( newGrabHandlePosition ||
+ newPrimaryHandlePosition ||
+ newSecondaryHandlePosition )
+ {
+ // Setup property notifications to find whether the handles leave the boundaries of the current display.
+ SetupActiveLayerPropertyNotifications();
+ }
+
if( mActiveCopyPastePopup )
{
ShowPopup();
void DeterminePositionPopup()
{
- if ( !mActiveCopyPastePopup )
+ if( !mActiveCopyPastePopup )
{
return;
}
+ // Retrieves the popup's size after relayout.
+ const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
+
if( mPopupSetNewPosition )
{
- if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
- {
- float minHandleXPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
- float maxHandleXPosition = std::max ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
+ const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
+ const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
+ const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
- float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
+ if( primaryHandle.active || secondaryHandle.active )
+ {
+ // Calculates the popup's position if selection handles are active.
+ const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
+ const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
+ const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
- mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
- mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
+ mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
+ mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
}
else
{
- mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
+ // Calculates the popup's position if the grab handle is active.
+ mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
}
}
- Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
-
- GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
+ // Checks if there is enough space above the text control. If not it places the popup under it.
+ GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
SetUpPopupPositionNotifications();
void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
{
- // Get the world position of the active layer
+ // Gets the world position of the active layer. The active layer is where the handles are added.
const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
- // Get the size of the UI control.
- Vector2 targetSize;
- mController.GetTargetSize( targetSize );
-
// The grab handle position in world coords.
- position.x = parentWorldPosition.x - 0.5f * targetSize.width + handle.position.x;
- position.y = parentWorldPosition.y - 0.5f * targetSize.height + handle.position.y + handle.lineHeight;
+ // The active layer's world position is the center of the active layer. The origin of the
+ // coord system of the handles is the top left of the active layer.
+ position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x;
+ position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y;
}
void SetGrabHandlePosition()
// Reference to the grab handle.
HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
- // The grab handle position in world coords.
+ // Transforms the handle position into world coordinates.
+ // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
+ // as it's transforming the handle's position set by the text-controller and not
+ // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
+ // retrieves the position of the center of the actor but the handle's position set
+ // by the text controller is not the center of the actor.
Vector2 grabHandleWorldPosition;
CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
// At the moment only the height is checked for the grab handle.
grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
- ( ( grabHandleWorldPosition.y - grabHandle.lineHeight - grabHandle.size.height ) > mBoundingBox.y ) ) ||
- ( grabHandleWorldPosition.y + grabHandle.size.height > mBoundingBox.w );
+ ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
+ ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
// The grab handle 'y' position in local coords.
// If the grab handle exceeds the bottom of the decoration box,
// Reference to the selection handle.
HandleImpl& handle = mHandle[type];
- // Get the world coordinates of the handle position.
+ // Transforms the handle position into world coordinates.
+ // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
+ // as it's transforming the handle's position set by the text-controller and not
+ // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
+ // retrieves the position of the center of the actor but the handle's position set
+ // by the text controller is not the center of the actor.
Vector2 handleWorldPosition;
CalculateHandleWorldCoordinates( handle, handleWorldPosition );
// Check if the selection handle exceeds the boundaries of the decoration box.
const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
-
const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
// Does not flip if both conditions are true (double flip)
// Whether to flip the handle vertically.
handle.verticallyFlipped = ( verticallyFlippedPreferred &&
- ( ( handleWorldPosition.y - handle.lineHeight - handle.size.height ) > mBoundingBox.y ) ) ||
- ( handleWorldPosition.y + handle.size.height > mBoundingBox.w );
+ ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
+ ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
// The primary selection handle 'y' position in local coords.
// If the handle exceeds the bottom of the decoration box,
return true;
}
- // Popup
+ void HandleResetPosition( PropertyNotification& source )
+ {
+ const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
- float AlternatePopUpPositionRelativeToCursor()
+ if( grabHandle.active )
+ {
+ // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
+ SetGrabHandlePosition();
+
+ // Sets the grab handle image according if it's pressed, flipped, etc.
+ SetHandleImage( GRAB_HANDLE );
+ }
+ else
+ {
+ // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
+ SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
+
+ // Sets the primary handle image according if it's pressed, flipped, etc.
+ SetHandleImage( LEFT_SELECTION_HANDLE );
+
+ // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
+ SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
+
+ // Sets the secondary handle image according if it's pressed, flipped, etc.
+ SetHandleImage( RIGHT_SELECTION_HANDLE );
+ }
+ }
+
+ void SetupActiveLayerPropertyNotifications()
{
- const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
- const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
+ if( !mActiveLayer )
+ {
+ return;
+ }
- float alternativePosition=0.0f;;
+ // Vertical notifications.
- if( mPrimaryCursor ) // Secondary cursor not used for paste
+ // Disconnect any previous connected callback.
+ if( mVerticalLessThanNotification )
{
- alternativePosition = mCursor[PRIMARY_CURSOR].position.y + popupHeight;
+ mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
+ }
+
+ if( mVerticalGreaterThanNotification )
+ {
+ mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
}
const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
- const HandleImpl& selectionPrimaryHandle = mHandle[LEFT_SELECTION_HANDLE];
- const HandleImpl& selectionSecondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
+ const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
+ const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
if( grabHandle.active )
{
- // If grab handle enabled then position pop-up below the grab handle.
- alternativePosition = grabHandle.position.y + grabHandle.size.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET;
+ if( grabHandle.verticallyFlipped )
+ {
+ // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
+ mVerticalGreaterThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the top edje of the display.
+ const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
+
+ mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ LessThanCondition( mBoundingBox.y + topHeight ) );
+ // Notifies the change from false to true and from true to false.
+ mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ else
+ {
+ // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
+ mVerticalLessThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the bottom edje of the display.
+ const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
+
+ mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
}
- else if( selectionPrimaryHandle.active || selectionSecondaryHandle.active )
+ else // The selection handles are active
{
- const float maxHeight = std::max( selectionPrimaryHandle.size.height,
- selectionSecondaryHandle.size.height );
- const float maxY = std::max( selectionPrimaryHandle.position.y,
- selectionSecondaryHandle.position.y );
+ if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
+ {
+ // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
+ mVerticalGreaterThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the top edje of the display.
+ const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
+
+ mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ LessThanCondition( mBoundingBox.y + topHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
+ {
+ // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
+ mVerticalLessThanNotification.Reset();
+
+ // The vertical distance from the center of the active layer to the bottom edje of the display.
+ const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
+ secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
+
+ mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ else
+ {
+ // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
+
+ // The vertical distance from the center of the active layer to the top edje of the display.
+ const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
+ -primaryHandle.position.y + primaryHandle.size.height :
+ -secondaryHandle.position.y + secondaryHandle.size.height );
+
+ mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ LessThanCondition( mBoundingBox.y + topHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+
+ // The vertical distance from the center of the active layer to the bottom edje of the display.
+ const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
+ secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
+ primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
+
+ mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
+ GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
+
+ // Notifies the change from false to true and from true to false.
+ mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ }
+
+ // Horizontal notifications.
+
+ // Disconnect any previous connected callback.
+ if( mHorizontalLessThanNotification )
+ {
+ mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
+ }
+
+ if( mHorizontalGreaterThanNotification )
+ {
+ mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
+ mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
+ }
+
+ if( primaryHandle.active || secondaryHandle.active )
+ {
+ // The horizontal distance from the center of the active layer to the left edje of the display.
+ const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
+ -secondaryHandle.position.x + secondaryHandle.size.width );
+
+ mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
+ LessThanCondition( mBoundingBox.x + leftWidth ) );
- alternativePosition = maxY + maxHeight + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET;
+ // Notifies the change from false to true and from true to false.
+ mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+
+ // The horizontal distance from the center of the active layer to the right edje of the display.
+ const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
+ secondaryHandle.position.x + secondaryHandle.size.width );
+
+ mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
+ GreaterThanCondition( mBoundingBox.z - rightWidth ) );
+
+ // Notifies the change from false to true and from true to false.
+ mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
+
+ // Connects the signals with the callbacks.
+ mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
+ }
+ }
+
+ // Popup
+
+ float AlternatePopUpPositionRelativeToCursor()
+ {
+ float alternativePosition = 0.0f;
+
+ const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
+
+ const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
+ const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
+ const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
+ const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
+
+ if( primaryHandle.active || secondaryHandle.active )
+ {
+ const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
+ alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
+ }
+ else
+ {
+ alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
}
return alternativePosition;
void PopUpLeavesVerticalBoundary( PropertyNotification& source )
{
- float alternativeYPosition=0.0f;
+ float alternativeYPosition = 0.0f;
// todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
// if can't be positioned above, then position below row.
alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
mCopyPastePopup.actor.SetY( alternativeYPosition );
}
- void SetUpPopupPositionNotifications( )
+ void SetUpPopupPositionNotifications()
{
// Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
// Exceeding vertical boundary
- float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
+ const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
- OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
- mBoundingBox.w - popupHeight * 0.5f ) );
+ OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
+ mBoundingBox.w - popupHeight * 0.5f ) );
verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
}
- void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, const Vector4& boundingRectangleWorld )
+ void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
{
DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
// Parent must already by added to Stage for these Get calls to work
- Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
- Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
- Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
- Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
+ const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
+ const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
// Calculate distance to move popup (in local space) so fits within the boundary
float xOffSetToKeepWithinBounds = 0.0f;
{
xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
}
- else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
+ else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
{
xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
}
// Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
- if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
+ if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
{
requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
}
requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
// Prevent pixel mis-alignment by rounding down.
- requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
- requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
+ requiredPopupPosition.x = floor( requiredPopupPosition.x );
+ requiredPopupPosition.y = floor( requiredPopupPosition.y );
}
void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
- Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
- ImageActor mPrimaryCursor;
- ImageActor mSecondaryCursor;
+ Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
+ PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
+ PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
+ PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
+ PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
+ ImageActor mPrimaryCursor;
+ ImageActor mSecondaryCursor;
- Actor mHighlightActor; ///< Actor to display highlight
+ Actor mHighlightActor; ///< Actor to display highlight
Renderer mHighlightRenderer;
Material mHighlightMaterial; ///< Material used for highlight
Property::Map mQuadVertexFormat;
Vector4 mBoundingBox; ///< The bounding box in world coords.
Vector4 mHighlightColor; ///< Color of the highlight
Vector2 mHighlightPosition; ///< The position of the highlight actor.
+ Vector2 mControlSize; ///< The control's size. Set by the Relayout.
unsigned int mActiveCursor;
unsigned int mCursorBlinkInterval;
// EXTERNAL INCLUDES
#include <dali/integration-api/debug.h>
-#define MAKE_SHADER(A)#A
-
namespace
{
Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING");
#endif
+#define MAKE_SHADER(A)#A
+
const char* VERTEX_SHADER = MAKE_SHADER(
attribute mediump vec2 aPosition;
attribute mediump vec2 aTexCoord;
+uniform mediump vec2 uOffset;
uniform mediump mat4 uMvpMatrix;
varying mediump vec2 vTexCoord;
void main()
{
- mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
+ mediump vec4 position = vec4( aPosition.xy + uOffset, 0.0, 1.0 );
gl_Position = uMvpMatrix * position;
vTexCoord = aTexCoord;
}
);
-const char* FRAGMENT_SHADER = MAKE_SHADER(
+const char* FRAGMENT_SHADER_L8 = MAKE_SHADER(
+uniform lowp vec4 uColor;
uniform sampler2D sTexture;
varying mediump vec2 vTexCoord;
void main()
{
- gl_FragColor = texture2D( sTexture, vTexCoord );
-}
-);
-
-const char* VERTEX_SHADER_SHADOW = MAKE_SHADER(
-attribute mediump vec2 aPosition;
-attribute mediump vec2 aTexCoord;
-varying mediump vec2 vTexCoord;
-
-void main()
-{
- mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
- gl_Position = position;
- vTexCoord = aTexCoord;
+ mediump vec4 color = texture2D( sTexture, vTexCoord );
+ gl_FragColor = vec4( uColor.rgb, uColor.a * color.r );
}
);
-const char* FRAGMENT_SHADER_SHADOW = MAKE_SHADER(
+const char* FRAGMENT_SHADER_RGBA = MAKE_SHADER(
uniform sampler2D sTexture;
-uniform lowp vec4 uColor;
varying mediump vec2 vTexCoord;
void main()
{
- mediump vec4 color = texture2D( sTexture, vTexCoord );
- gl_FragColor = vec4(uColor.rgb, uColor.a*color.r);
+ gl_FragColor = texture2D( sTexture, vTexCoord );
}
);
AtlasGlyphManager::AtlasGlyphManager()
{
+ mShaderL8 = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_L8 );
+ mShaderRgba = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RGBA );
mAtlasManager = Dali::Toolkit::AtlasManager::New();
- mEffectBufferShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
- mShadowShader = Shader::New( VERTEX_SHADER_SHADOW, FRAGMENT_SHADER_SHADOW, Dali::Shader::HINT_MODIFIES_GEOMETRY );
}
void AtlasGlyphManager::Add( const Text::GlyphInfo& glyph,
{
DALI_LOG_INFO( gLogFilter, Debug::General, "Added glyph, font: %d index: %d\n", glyph.fontId, glyph.index );
- mAtlasManager.Add( bitmap, slot );
+ if ( mAtlasManager.Add( bitmap, slot ) )
+ {
+ // A new atlas was created so set the material details for the atlas
+ Dali::Atlas atlas = mAtlasManager.GetAtlasContainer( slot.mAtlasId );
+ Pixel::Format pixelFormat = mAtlasManager.GetPixelFormat( slot.mAtlasId );
+ Material material = Material::New( pixelFormat == Pixel::L8 ? mShaderL8 : mShaderRgba );
+ material.AddTexture( atlas, "sTexture" );
+ material.SetBlendMode( BlendingMode::ON );
+ mAtlasManager.SetMaterial( slot.mAtlasId, material );
+ }
GlyphRecordEntry record;
record.mIndex = glyph.index;
mAtlasManager.GenerateMeshData( imageId, position, mesh, false );
}
-void AtlasGlyphManager::StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
- const Toolkit::AtlasManager::Mesh2D& second )
-{
- mAtlasManager.StitchMesh( first, second );
-}
-
-bool AtlasGlyphManager::Cached( Text::FontId fontId,
+bool AtlasGlyphManager::IsCached( Text::FontId fontId,
Text::GlyphIndex index,
Dali::Toolkit::AtlasManager::AtlasSlot& slot )
{
return mAtlasManager.GetMaterial( atlasId );
}
-Sampler AtlasGlyphManager::GetSampler( uint32_t atlasId ) const
-{
- return mAtlasManager.GetSampler( atlasId );
-}
-
AtlasGlyphManager::~AtlasGlyphManager()
{
// mAtlasManager handle is automatically released here
Toolkit::AtlasManager::Mesh2D& mesh );
/**
- * @copydoc Toolkit::AtlasGlyphManager::StitchMesh
+ * @copydoc Toolkit::AtlasGlyphManager::IsCached
*/
- void StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
- const Toolkit::AtlasManager::Mesh2D& second );
-
- /**
- * @copydoc Toolkit::AtlasGlyphManager::Cached
- */
- bool Cached( Text::FontId fontId,
- Text::GlyphIndex index,
- Dali::Toolkit::AtlasManager::AtlasSlot& slot );
+ bool IsCached( Text::FontId fontId,
+ Text::GlyphIndex index,
+ Dali::Toolkit::AtlasManager::AtlasSlot& slot );
/**
* @copydoc Toolkit::AtlasGlyphManager::GetAtlasSize
Material GetMaterial( uint32_t atlasId ) const;
/**
- * @copydoc Toolkit::AtlasGlyphManager::GetMaterial
- */
- Sampler GetSampler( uint32_t atlasId ) const;
-
- /**
* @copydoc Toolkit::AtlasGlyphManager::GetMetrics
*/
const Toolkit::AtlasGlyphManager::Metrics& GetMetrics();
- /**
- * @copydoc Toolkit::AtlasGlyphManager::GetEffectBufferShader
- */
- Shader GetEffectBufferShader() const
- {
- return mEffectBufferShader;
- }
-
- /**
- * @copydoc Toolkit::AtlasGlyphManager::GetGlyphShadowShader
- */
- Shader GetGlyphShadowShader() const
- {
- return mShadowShader;
- }
-
protected:
/**
Dali::Toolkit::AtlasManager mAtlasManager; ///> Atlas Manager created by GlyphManager
std::vector< FontGlyphRecord > mFontGlyphRecords;
Toolkit::AtlasGlyphManager::Metrics mMetrics; ///> Metrics to pass back on GlyphManager status
- Shader mEffectBufferShader; ///> Shader used to render drop shadow buffer textures
- Shader mShadowShader; ///> Shader used to render drop shadow into buffer
+
+ Shader mShaderL8;
+ Shader mShaderRgba;
};
} // namespace Internal
mesh );
}
-void AtlasGlyphManager::StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
- const Toolkit::AtlasManager::Mesh2D& second )
+bool AtlasGlyphManager::IsCached( Text::FontId fontId,
+ Text::GlyphIndex index,
+ AtlasManager::AtlasSlot& slot )
{
- GetImplementation(*this).StitchMesh( first, second );
-}
-
-bool AtlasGlyphManager::Cached( Text::FontId fontId,
- Text::GlyphIndex index,
- AtlasManager::AtlasSlot& slot )
-{
- return GetImplementation(*this).Cached( fontId, index, slot );
+ return GetImplementation(*this).IsCached( fontId, index, slot );
}
void AtlasGlyphManager::SetNewAtlasSize( uint32_t width, uint32_t height, uint32_t blockWidth, uint32_t blockHeight )
return GetImplementation(*this).GetMaterial( atlasId );
}
-Sampler AtlasGlyphManager::GetSampler( uint32_t atlasId ) const
-{
- return GetImplementation(*this).GetSampler( atlasId );
-}
-
const Toolkit::AtlasGlyphManager::Metrics& AtlasGlyphManager::GetMetrics()
{
return GetImplementation(*this).GetMetrics();
GetImplementation(*this).AdjustReferenceCount( fontId, index, delta );
}
-Shader AtlasGlyphManager::GetEffectBufferShader() const
-{
- return GetImplementation(*this).GetEffectBufferShader();
-}
-
-Shader AtlasGlyphManager::GetGlyphShadowShader() const
-{
- return GetImplementation(*this).GetGlyphShadowShader();
-}
-
} // namespace Toolkit
} // namespace Dali
*/
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/atlas-manager/atlas-manager.h>
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-manager.h>
#include <dali-toolkit/internal/text/text-definitions.h>
namespace Dali
Toolkit::AtlasManager::Mesh2D& mesh );
/**
- * @brief Stitch Two Meshes together
- *
- * @param[in] first first mesh
- * @param[in] second second mesh
- */
- void StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
- const Toolkit::AtlasManager::Mesh2D& second );
-
- /**
* @brief Check to see if a glyph is being cached
*
* @param[in] fontId The font that this glyph comes from
*
* @return Whether glyph is cached or not ?
*/
- bool Cached( Text::FontId fontId,
- Text::GlyphIndex index,
- AtlasManager::AtlasSlot& slot );
+ bool IsCached( Text::FontId fontId,
+ Text::GlyphIndex index,
+ AtlasManager::AtlasSlot& slot );
/**
* @brief Retrieve the size of an atlas
Material GetMaterial( uint32_t atlasId ) const;
/**
- * @brief Get the sampler used by an atlas
- *
- * @param[in] atlasId Id of an atlas
- *
- * @return The sampler used by the atlas
- */
- Sampler GetSampler( uint32_t atlasId ) const;
-
- /**
* @brief Get Glyph Manager metrics
*
* @return const reference to glyph manager metrics
*/
void AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, int32_t delta );
- /**
- * @brief Get Shader used for rendering glyph effect buffers
- *
- * @return Handle of shader needed
- */
- Shader GetEffectBufferShader() const;
-
- /**
- * @brief Get Shader used rendering Glyph Shadows
- *
- * @return Handle of shader needed
- */
- Shader GetGlyphShadowShader() const;
-
private:
explicit DALI_INTERNAL AtlasGlyphManager(Internal::AtlasGlyphManager *impl);
*/
// CLASS HEADER
-#include <dali-toolkit/internal/atlas-manager/atlas-manager-impl.h>
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-manager-impl.h>
-// EXTERNAL INCLUDE
-#include <iostream>
+// EXTERNAL INCLUDES
#include <string.h>
-#include <dali/devel-api/rendering/sampler.h>
-#include <dali/devel-api/rendering/shader.h>
#include <dali/integration-api/debug.h>
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
+
namespace Dali
{
const uint32_t DOUBLE_PIXEL_PADDING( SINGLE_PIXEL_PADDING << 1 );
const uint32_t FILLED_PIXEL( -1 );
Toolkit::AtlasManager::AtlasSize EMPTY_SIZE;
-
- #define MAKE_SHADER(A)#A
-
- const char* VERTEX_SHADER = MAKE_SHADER(
- attribute mediump vec2 aPosition;
- attribute mediump vec2 aTexCoord;
- uniform mediump mat4 uMvpMatrix;
- varying mediump vec2 vTexCoord;
-
- void main()
- {
- mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
- gl_Position = uMvpMatrix * position;
- vTexCoord = aTexCoord;
- }
- );
-
- const char* FRAGMENT_SHADER_L8 = MAKE_SHADER(
- uniform lowp vec4 uColor;
- uniform sampler2D sTexture;
- varying mediump vec2 vTexCoord;
-
- void main()
- {
- mediump vec4 color = texture2D( sTexture, vTexCoord );
- gl_FragColor = vec4( uColor.rgb, uColor.a * color.r );
- }
- );
-
- const char* FRAGMENT_SHADER_RGBA = MAKE_SHADER(
- uniform sampler2D sTexture;
- varying mediump vec2 vTexCoord;
-
- void main()
- {
- gl_FragColor = texture2D( sTexture, vTexCoord );
- }
- );
-
}
AtlasManager::AtlasManager()
mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
- mShaderL8 = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_L8 );
- mShaderRgba = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RGBA );
}
AtlasManagerPtr AtlasManager::New()
memset( buffer, 0xFF, filledPixelImage.GetBufferSize() );
atlas.Upload( filledPixelImage, 0, 0 );
-
- Sampler sampler = Sampler::New( atlas, "sTexture" );
- sampler.SetProperty( Sampler::Property::AFFECTS_TRANSPARENCY, true );
- atlasDescriptor.mMaterial = Material::New( pixelformat == Pixel::L8 ? mShaderL8 : mShaderRgba );
- atlasDescriptor.mMaterial.AddSampler( sampler );
- atlasDescriptor.mSampler = sampler;
- atlasDescriptor.mMaterial.SetBlendMode( BlendingMode::ON );
mAtlasList.push_back( atlasDescriptor );
return mAtlasList.size();
}
mAddFailPolicy = policy;
}
-void AtlasManager::Add( const BufferImage& image,
+bool AtlasManager::Add( const BufferImage& image,
Toolkit::AtlasManager::AtlasSlot& slot,
Toolkit::AtlasManager::AtlasId atlas )
{
- // See if there's a slot in an atlas that matches the requirements of this image
- // A bitmap must be sliceable into a single atlas
+ bool created = false;
Pixel::Format pixelFormat = image.GetPixelFormat();
SizeType width = image.GetWidth();
SizeType height = image.GetHeight();
- SizeType blockArea = 0;
SizeType foundAtlas = 0;
SizeType index = 0;
slot.mImageId = 0;
// If there is a preferred atlas then check for room in that first
if ( atlas-- )
{
- foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea );
+ foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
}
// Search current atlases to see if there is a good match
while( !foundAtlas && index < mAtlasList.size() )
{
- foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea );
+ foundAtlas = CheckAtlas( index, width, height, pixelFormat );
++index;
}
mNewAtlasSize.mHeight,
mNewAtlasSize.mBlockWidth,
mNewAtlasSize.mBlockHeight );
- return;
+ return created;
}
-
- foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat, blockArea );
+ created = true;
+ foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
}
if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
{
// Haven't found an atlas for this image!!!!!!
DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
- return;
+ return created;
}
}
- // Work out where the blocks are we're going to use
- for ( SizeType i = 0; i < blockArea; ++i )
+ // Work out which the block we're going to use
+ // Is there currently a next free block available ?
+ if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
{
- // Is there currently a next free block available ?
- if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
- {
- // Yes, so select our next block
- desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks-- );
- }
- else
- {
- // Our next block must be from the free list, fetch from the start of the list
- desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ] );
- mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
- }
+ // Yes, so select our next block
+ desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
+ }
+ else
+ {
+ // Our next block must be from the free list, fetch from the start of the list
+ desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
+ mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
}
desc.mImageWidth = width;
// See if there's a previously freed image ID that we can assign to this new image
uint32_t imageId = 0u;
- for ( uint32_t i = 0u; i < mImageList.size(); ++i )
+ for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
{
if ( !mImageList[ i ].mCount )
{
}
if ( !imageId )
{
- mImageList.push_back( desc );
- slot.mImageId = mImageList.size();
+ mImageList.PushBack( desc );
+ slot.mImageId = mImageList.Size();
}
else
{
slot.mImageId = imageId;
}
slot.mAtlasId = foundAtlas + 1u;
+
+ // Upload the buffer image into the atlas
UploadImage( image, desc );
+ return created;
}
AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
SizeType width,
SizeType height,
- Pixel::Format pixelFormat,
- SizeType& blockArea )
+ Pixel::Format pixelFormat )
{
+ AtlasManager::SizeType result = 0u;
if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
{
// Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
&& width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
&& height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
{
- blockArea = 1u;
- return ( atlas + 1u );
+ result = atlas + 1u;
}
}
- return 0u;
-}
-
-void AtlasManager::CreateMesh( SizeType atlas,
- SizeType imageWidth,
- SizeType imageHeight,
- const Vector2& position,
- SizeType widthInBlocks,
- SizeType heightInBlocks,
- Toolkit::AtlasManager::Mesh2D& mesh,
- AtlasSlotDescriptor& desc )
-{
- Toolkit::AtlasManager::Vertex2D vertex;
- uint32_t faceIndex = 0; // TODO change to unsigned short when property type is available
-
- SizeType blockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
- SizeType blockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
-
- float vertexBlockWidth = static_cast< float >( blockWidth );
- float vertexBlockHeight = static_cast< float >( blockHeight );
-
- SizeType width = mAtlasList[ atlas ].mSize.mWidth;
- SizeType height = mAtlasList[ atlas ].mSize.mHeight;
-
- SizeType atlasWidthInBlocks = ( width - 1u ) / blockWidth;
-
- // Get the normalized size of a texel in both directions
- // TODO when texture resizing and passing texture size via uniforms is available,
- // we will encode pixel positions into the vertex data rather than normalized
- // meaning that geometry needn't be changed on an atlas resize
- float texelX = 1.0f / static_cast< float >( width );
- float texelY = 1.0f / static_cast< float >( height );
-
- float oneAndAHalfTexelX = texelX + ( texelX * 0.5f );
- float oneAndAHalfTexelY = texelY + ( texelY * 0.5f );
-
- // Get the normalized size of a block in texels
- float texelBlockWidth = texelX * vertexBlockWidth;
- float texelBlockHeight = texelY * vertexBlockHeight;
-
- // Get partial block space
- float vertexEdgeWidth = static_cast< float >( imageWidth % blockWidth );
- float vertexEdgeHeight = static_cast< float >( imageHeight % blockHeight );
-
- // And in texels
- float texelEdgeWidth = texelX * vertexEdgeWidth;
- float texelEdgeHeight = texelY * vertexEdgeHeight;
-
- // We're going to 'blit' half a pixel more on each edge
- vertexBlockWidth++;
- vertexEdgeWidth++;
- vertexBlockHeight++;
- vertexEdgeHeight++;
-
- // Block by block create the two triangles for the quad
- SizeType blockIndex = 0;
- float ndcWidth;
- float ndcHeight;
- float ndcVWidth;
- float ndcVHeight;
-
- // Move back half a pixel
- Vector2 topLeft = Vector2( position.x - 0.5f, position.y - 0.5f );
-
- for ( SizeType y = 0; y < heightInBlocks; ++y )
- {
-
- float currentX = position.x;
-
- if ( ( heightInBlocks - 1u ) == y && vertexEdgeHeight > 0.0f )
- {
- ndcHeight = texelEdgeHeight + texelY;
- ndcVHeight = vertexEdgeHeight;
- }
- else
- {
- ndcHeight = texelBlockHeight + texelY;
- ndcVHeight = vertexBlockHeight;
- }
-
- for ( SizeType x = 0; x < widthInBlocks; ++x )
- {
- SizeType block = desc.mBlocksList[ blockIndex++ ];
-
- float fBlockX = texelBlockWidth * static_cast< float >( block % atlasWidthInBlocks );
- float fBlockY = texelBlockHeight * static_cast< float >( block / atlasWidthInBlocks );
-
- // Add on texture filtering compensation ( half a texel plus compensation for filled pixel in top left corner )
- fBlockX += oneAndAHalfTexelX;
- fBlockY += oneAndAHalfTexelY;
-
- if ( ( widthInBlocks - 1u ) == x && vertexEdgeWidth > 0.0f )
- {
- ndcWidth = texelEdgeWidth + texelX;
- ndcVWidth = vertexEdgeWidth;
- }
- else
- {
- ndcWidth = texelBlockWidth + texelX;
- ndcVWidth = vertexBlockWidth;
- }
-
- // Top left
- vertex.mPosition.x = topLeft.x;
- vertex.mPosition.y = topLeft.y;
- vertex.mTexCoords.x = fBlockX;
- vertex.mTexCoords.y = fBlockY;
-
- mesh.mVertices.PushBack( vertex );
-
- // Top Right
- vertex.mPosition.x = topLeft.x + ndcVWidth;
- vertex.mPosition.y = topLeft.y;
- vertex.mTexCoords.x = fBlockX + ndcWidth;
- vertex.mTexCoords.y = fBlockY;
-
- mesh.mVertices.PushBack( vertex );
-
- // Bottom Left
- vertex.mPosition.x = topLeft.x;
- vertex.mPosition.y = topLeft.y + ndcVHeight;
- vertex.mTexCoords.x = fBlockX;
- vertex.mTexCoords.y = fBlockY + ndcHeight;
-
- mesh.mVertices.PushBack( vertex );
-
- // Bottom Right
- topLeft.x += ndcVWidth;
- vertex.mPosition.x = topLeft.x;
- vertex.mPosition.y = topLeft.y + ndcVHeight;
- vertex.mTexCoords.x = fBlockX + ndcWidth;
- vertex.mTexCoords.y = fBlockY + ndcHeight;
-
- mesh.mVertices.PushBack( vertex );
-
- // Six indices in counter clockwise winding
- mesh.mIndices.PushBack( faceIndex + 1u );
- mesh.mIndices.PushBack( faceIndex );
- mesh.mIndices.PushBack( faceIndex + 2u );
- mesh.mIndices.PushBack( faceIndex + 2u );
- mesh.mIndices.PushBack( faceIndex + 3u );
- mesh.mIndices.PushBack( faceIndex + 1u );
- faceIndex += 4;
- }
-
- // Move down a row
- topLeft.x = currentX;
- topLeft.y += vertexBlockHeight;
- }
-
- // If there's only one block then skip this next vertex optimisation
- if ( widthInBlocks * heightInBlocks > 1 )
- {
- Toolkit::AtlasManager::Mesh2D optimizedMesh;
- OptimizeMesh( mesh, optimizedMesh );
- }
-}
-
-void AtlasManager::PrintMeshData( const Toolkit::AtlasManager::Mesh2D& mesh )
-{
- uint32_t vertexCount = mesh.mVertices.Size();
- uint32_t indexCount = mesh.mIndices.Size();
- std::cout << "\nMesh Data for Image: VertexCount = " << vertexCount;
- std::cout << ", Triangles = " << indexCount / 3 << std::endl;
-
- for ( SizeType v = 0; v < vertexCount; ++v )
- {
- std::cout << " Vertex(" << v << ") x = " << mesh.mVertices[v].mPosition.x << ", ";
- std::cout << "y = " << mesh.mVertices[v].mPosition.y << ", ";
- std::cout << "u = " << mesh.mVertices[v].mTexCoords.x << ", ";
- std::cout << "v = " << mesh.mVertices[v].mTexCoords.y << std::endl;
- }
-
- std::cout << "\n Indices: ";
- for ( SizeType i = 0; i < indexCount; ++i )
- {
- std::cout << " " << mesh.mIndices[ i ];
- }
- std::cout << std::endl;
-}
-
-void AtlasManager::OptimizeMesh( const Toolkit::AtlasManager::Mesh2D& in,
- Toolkit::AtlasManager::Mesh2D& out )
-{
- unsigned short vertexIndex = 0;
-
- // We could check to see if blocks are next to each other, but it's probably just as quick to compare verts
- for ( SizeType i = 0; i < in.mIndices.Size(); ++i )
- {
- // Fetch a vertex, has it already been assigned?
- bool foundVertex = false;
- Toolkit::AtlasManager::Vertex2D v = in.mVertices[ in.mIndices[ i ] ];
- for ( SizeType j = 0; j < out.mVertices.Size(); ++j )
- {
- if ( ( fabsf( v.mPosition.x - out.mVertices[ j ].mPosition.x ) < Math::MACHINE_EPSILON_1000 ) &&
- ( fabsf( v.mPosition.y - out.mVertices[ j ].mPosition.y ) < Math::MACHINE_EPSILON_1000 ) &&
- ( fabsf( v.mTexCoords.x - out.mVertices[ j ].mTexCoords.x ) < Math::MACHINE_EPSILON_1000 ) &&
- ( fabsf( v.mTexCoords.y - out.mVertices[ j ].mTexCoords.y ) < Math::MACHINE_EPSILON_1000 ) )
- {
- // Yes, so store this down as the vertex to use
- out.mIndices.PushBack( j );
- foundVertex = true;
- break;
- }
- }
-
- // Did we find a vertex ?
- if ( !foundVertex )
- {
- // No so add a new one
- out.mVertices.PushBack( v );
- vertexIndex++;
- }
- }
-}
-
-void AtlasManager::StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
- const Toolkit::AtlasManager::Mesh2D& second,
- bool optimize )
-{
- const uint32_t verticesCount = first.mVertices.Size();
- first.mVertices.Insert( first.mVertices.End(),
- second.mVertices.Begin(),
- second.mVertices.End() );
-
- const uint32_t indicesCount = first.mIndices.Size();
- first.mIndices.Insert( first.mIndices.End(),
- second.mIndices.Begin(),
- second.mIndices.End() );
-
- for( Vector<unsigned int>::Iterator it = first.mIndices.Begin() + indicesCount,
- endIt = first.mIndices.End();
- it != endIt;
- ++it )
- {
- *it += verticesCount;
- }
-
- if ( optimize )
- {
- Toolkit::AtlasManager::Mesh2D optimizedMesh;
- OptimizeMesh( first, optimizedMesh );
- first = optimizedMesh;
- }
+ return result;
}
void AtlasManager::UploadImage( const BufferImage& image,
SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
- SizeType block = desc.mBlocksList[ 0 ];
- SizeType blockX = block % atlasWidthInBlocks;
- SizeType blockY = block / atlasWidthInBlocks;
+ SizeType blockX = desc.mBlock % atlasWidthInBlocks;
+ SizeType blockY = desc.mBlock / atlasWidthInBlocks;
SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
SizeType width = mImageList[ imageId ].mImageWidth;
SizeType height = mImageList[ imageId ].mImageHeight;
- SizeType widthInBlocks = width / mAtlasList[ atlas ].mSize.mBlockWidth;
- if ( width % mAtlasList[ atlas ].mSize.mBlockWidth )
- {
- widthInBlocks++;
- }
- SizeType heightInBlocks = height / mAtlasList[ atlas ].mSize.mBlockHeight;
- if ( height % mAtlasList[ atlas ].mSize.mBlockHeight )
- {
- heightInBlocks++;
- }
-
- CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] );
+ AtlasMeshFactory::CreateQuad( width,
+ height,
+ mImageList[ imageId ].mBlock,
+ mAtlasList[ atlas ].mSize,
+ position,
+ meshData );
// Mesh created so increase the reference count, if we're asked to
if ( addReference )
Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
{
- Dali::Atlas null;
- if ( !atlas || atlas > mAtlasList.size( ) )
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ Dali::Atlas atlasContainer;
+ if ( atlas && atlas-- <= mAtlasList.size() )
{
-
- DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
- return null;
+ atlasContainer = mAtlasList[ atlas ].mAtlas;
}
- return mAtlasList[ atlas -1u ].mAtlas;
+ return atlasContainer;
}
bool AtlasManager::Remove( ImageId id )
SizeType imageId = id - 1u;
bool removed = false;
- if ( id > mImageList.size() )
+ if ( id > mImageList.Size() )
{
DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
return false;
removed = true;
mImageList[ imageId ].mCount = 0;
SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
- for ( uint32_t i = 0; i < mImageList[ imageId ].mBlocksList.Size(); ++i )
- {
- mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlocksList[ i ] );
- }
+ mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
}
return removed;
}
AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
{
- if ( id && id <= mImageList.size() )
- {
- return mImageList[ id - 1u ].mAtlasId;
- }
- else
+ DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
+ AtlasManager::AtlasId atlasId = 0u;
+ if ( id && id-- <= mImageList.Size() )
{
- return 0;
+ atlasId = mImageList[ id ].mAtlasId;
}
+ return atlasId;
}
void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
if ( atlas && atlas-- <= mAtlasList.size() )
{
return mAtlasList[ atlas ].mSize;
AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ AtlasManager::SizeType freeBlocks = 0u;
if ( atlas && atlas-- <= mAtlasList.size() )
{
- return ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() );
- }
- else
- {
- return 0;
+ freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
}
+ return freeBlocks;
}
AtlasManager::SizeType AtlasManager::GetAtlasCount() const
Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
{
- if ( !atlas || atlas > mAtlasList.size( ) )
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ Pixel::Format pixelFormat = Pixel::RGBA8888;
+ if ( atlas && atlas-- <= mAtlasList.size() )
{
-
- DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
- return Pixel::L8;
+ pixelFormat = mAtlasList[ atlas ].mPixelFormat;
}
- return mAtlasList[ --atlas].mPixelFormat;
+ return pixelFormat;
}
void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
Material AtlasManager::GetMaterial( AtlasId atlas ) const
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ Material material;
if ( atlas && atlas-- <= mAtlasList.size() )
{
- return mAtlasList[ atlas ].mMaterial;
+ material = mAtlasList[ atlas ].mMaterial;
}
- Material null;
- return null;
+ return material;
}
-Sampler AtlasManager::GetSampler( AtlasId atlas ) const
+void AtlasManager::SetMaterial( AtlasId atlas, Material& material )
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
if ( atlas && atlas-- <= mAtlasList.size() )
{
- return mAtlasList[ atlas ].mSampler;
+ mAtlasList[ atlas ].mMaterial = material;
}
- Sampler null;
- return null;
}
} // namespace Internal
#include <dali/public-api/object/base-object.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/atlas-manager/atlas-manager.h>
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-manager.h>
namespace Dali
{
BufferImage mHorizontalStrip; // Image used to pad upload
BufferImage mVerticalStrip; // Image used to pad upload
Material mMaterial; // material used for atlas texture
- Sampler mSampler; // sampler used for atlas texture
SizeType mTotalBlocks; // total number of blocks in atlas
SizeType mAvailableBlocks; // number of blocks available in atlas
Dali::Vector< SizeType > mFreeBlocksList; // unless there are any previously freed blocks
SizeType mImageWidth; // Width of image stored
SizeType mImageHeight; // Height of image stored
AtlasId mAtlasId; // Image is stored in this Atlas
- Dali::Vector< SizeType > mBlocksList; // List of blocks within atlas used for image
+ SizeType mBlock; // Block within atlas used for image
};
AtlasManager();
/**
* @copydoc Toolkit::AtlasManager::Add
*/
- void Add( const BufferImage& image,
+ bool Add( const BufferImage& image,
Toolkit::AtlasManager::AtlasSlot& slot,
Toolkit::AtlasManager::AtlasId atlas );
bool addReference );
/**
- * @copydoc Toolkit::AtlasManager::StitchMesh
- */
- void StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
- const Toolkit::AtlasManager::Mesh2D& second,
- bool optimize );
-
- /**
* @copydoc Toolkit::AtlasManager::Remove
*/
bool Remove( ImageId id );
*/
Material GetMaterial( AtlasId atlas ) const;
-/**
- * @copydoc Toolkit::AtlasManager::GetSampler
+ /**
+ * @copydoc Toolkit::AtlasManager::SetMaterial
*/
- Sampler GetSampler( AtlasId atlas ) const;
+ void SetMaterial( AtlasId atlas, Material& material );
private:
std::vector< AtlasDescriptor > mAtlasList; // List of atlases created
- std::vector< AtlasSlotDescriptor > mImageList; // List of bitmaps store in atlases
+ Vector< AtlasSlotDescriptor > mImageList; // List of bitmaps stored in atlases
Toolkit::AtlasManager::AtlasSize mNewAtlasSize; // Atlas size to use in next creation
Toolkit::AtlasManager::AddFailPolicy mAddFailPolicy; // Policy for faling to add an Image
SizeType CheckAtlas( SizeType atlas,
SizeType width,
SizeType height,
- Pixel::Format pixelFormat,
- SizeType& blockArea );
-
- void CreateMesh( SizeType atlas,
- SizeType imageWidth,
- SizeType imageHeight,
- const Vector2& position,
- SizeType widthInBlocks,
- SizeType heightInBlocks,
- Toolkit::AtlasManager::Mesh2D& mesh,
- AtlasSlotDescriptor& desc );
-
- void OptimizeMesh( const Toolkit::AtlasManager::Mesh2D& in,
- Toolkit::AtlasManager::Mesh2D& out );
+ Pixel::Format pixelFormat );
void UploadImage( const BufferImage& image,
const AtlasSlotDescriptor& desc );
- void PrintMeshData( const Toolkit::AtlasManager::Mesh2D& mesh );
-
- Shader mShaderL8;
- Shader mShaderRgba;
-
};
} // namespace Internal
*/
// CLASS HEADER
-#include <dali-toolkit/internal/atlas-manager/atlas-manager.h>
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-manager.h>
- // INTERNAL INCLUDES
-#include <dali-toolkit/internal/atlas-manager/atlas-manager-impl.h>
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-manager-impl.h>
namespace Dali
{
GetImplementation(*this).SetAddPolicy( policy );
}
-void AtlasManager::Add( const BufferImage& image,
+bool AtlasManager::Add( const BufferImage& image,
AtlasManager::AtlasSlot& slot,
AtlasManager::AtlasId atlas )
{
- GetImplementation(*this).Add( image, slot, atlas );
+ return GetImplementation(*this).Add( image, slot, atlas );
}
bool AtlasManager::Remove( ImageId id )
addReference );
}
-void AtlasManager::StitchMesh( Mesh2D& first,
- const Mesh2D& second,
- bool optimize )
-{
- GetImplementation(*this).StitchMesh( first, second, optimize );
-}
-
Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
{
return GetImplementation(*this).GetAtlasContainer( atlas );
return GetImplementation(*this).GetMaterial( atlas );
}
-Sampler AtlasManager::GetSampler( AtlasId atlas ) const
+void AtlasManager::SetMaterial( AtlasId atlas, Material& material )
{
- return GetImplementation(*this).GetSampler( atlas );
+ GetImplementation(*this).SetMaterial( atlas, material );
}
} // namespace Toolkit
} // namespace Internal
-/**
- * AtlasManager
- * ------------
- *
- * Creates and manages additions and removals of images from Texture Atlases
- *
- * The AtlasManager will match pixeltype and optimal block use to determine
- * the appropriate atlas to upload an image to.
- *
- * A policy can be specified to determine the action the AtlasManager will carry
- * out, should it not be able to add an image. This can return an error, or create
- * a new atlas of pre-determined dimensions to accomodate the new image.
- *
- * Images are referenced by an ImageId once they have been successfully uploaded.
- *
- * Once an image has been successfully uploaded, Geometry can be generated by passing
- * the ImageId to the GenerateMeshData method and geometry can be consolidated via
- * the StitchMesh method.
- *
- * Images are reference counted once mesh data has been generated. An image is removed
- * from the Atlas via the Remove( ImageId ) method. This unreferences the image and only
- * physically removes it from the atlas once all references have been freed.
- *
- * If the AddPolicy is set to generate and error if an image can't be uploaded, then it
- * is the applications responsibility to deal with the situation. An error will be indicated
- * with an ImageId of 0.
- *
- * Examples using the AtlasManager
- *
- * Create or obtain the AtlasManager
- * @code
- *
- * AtlasManager manager = AtlasManager::Get();
- *
- * @endcode
- *
- * Set the AtlasManager AddPolicy
- *
- * @code
- *
- * // Tell the atlas manager to create a new atlas, if it needs to
- * manager.SetAddPolicy( FAIL_ON_ADD_CREATES );
- *
- * // Tell the atlas manager to return an error, if it can't add an image
- * manager.SetAddPolicy( FAIL_ON_ADD_FAILS );
- *
- * @endcode
- *
- * Simple add and removal of BufferImage to and from an atlas
- *
- * @code
- *
- * // Structure returned by AtlasManager operations
- * AtlasSlot slot;
- *
- * // Add image to an atlas ( will be created if none suitable exists )
- * manager.Add( bitmapImage, slot );
- *
- * // slot.mImage returns the imageId for the bitmap, slot.mAtlas indicates the atlasId
- * // that the image was added to. The imageId is used to communicate with the AtlasManager
- * uint32_t imageId = slot.mImage;
- * if ( !imageId )
- * {
- * // Addition has failed.....
- * }
- * ...
- * ...
- * // Done with image, so remove from atlas, if not being used elsewhere
- * manager.Remove( imageId );
- *
- * @endcode
- *
- * Create a Specific Atlas for adding BufferImages to
- *
- * @code
- *
- * // Create an RGB888 atlas of 2048x2848, with a blocksize of 128x128
- * uint32_t atlas = manager.CreateAtlas( 2048u, 2048u, 128u, 128u, Pixel::RGB888 );
- *
- * // Add an image to a preferred atlas ( note not specifying atlas may still result
- * // in the bitmap being added to the atlas above )
- * manager.Add( bitmapImage, slot, atlas );
- *
- * @endcode
- *
- * Create Geometry for a previously added image
- *
- * @code
- *
- * // Top left corner of geometry to be generated
- * Vector2 position( 1.0f, 1.0f );
- *
- * // Geometry will end up here!
- * MeshData meshData;
- * manager.GenerateMeshData( imageId, position, meshData );
- *
- * @endcode
- *
- * Generating Geometry from multiple images in the same atlas
- *
- * @code
- *
- * MeshData firstMesh;
- * MeshData secondMesh;
- * manager.GenerateMeshData( imageid_1, position_1, firstMesh );
- * manager.GenerateMeshData( imageid_2, position_2, secondMesh );
- *
- * // Combine the two meshes. Passing MESH_OPTIMIZE as an optional third parameter will remove duplicate vertices
- * manager.StitchMesh( first, second );
- *
- * @endcode
- *
- */
-
class AtlasManager : public BaseHandle
{
public:
typedef uint32_t SizeType;
typedef SizeType AtlasId;
typedef SizeType ImageId;
- static const bool MESH_OPTIMIZE = true;
struct AtlasSize
{
struct Vertex2D
{
- Vector2 mPosition;
- Vector2 mTexCoords;
+ Vector2 mPosition; ///< Vertex posiiton
+ Vector2 mTexCoords; ///< Vertex texture co-ordinates
};
struct Mesh2D
{
- Vector< Vertex2D > mVertices;
- Vector< unsigned int> mIndices;
+ Vector< Vertex2D > mVertices; ///< container of vertices
+ Vector< SizeType > mIndices; ///< container of indices
};
/**
* @param[in] image reference to a bitmapimage
* @param[out] slot result of add operation
* @param[in] atlas optional preferred atlas
+ *
+ * @return true if a new atlas was created
*/
- void Add( const BufferImage& image,
+ bool Add( const BufferImage& image,
AtlasSlot& slot,
AtlasId atlas = 0 );
bool addReference = true );
/**
- * @brief Append second mesh to the first mesh
- *
- * @param[in] first First mesh
- * @param[in] second Second mesh
- * @param[in] optimize should we optimize vertex data
- */
- void StitchMesh( Mesh2D& first,
- const Mesh2D& second,
- bool optimize = false );
-
- /**
* @brief Get the BufferImage containing an atlas
*
* @param[in] atlas AtlasId returned when atlas was created
/**
* @brief Get Material used by atlas
*
- * @param atlas[in] atlas AtlasId
+ * @param[in] atlas AtlasId
*
* @return Material used by atlas
*/
Material GetMaterial( AtlasId atlas ) const;
- /**
- * @brief Get Sampler used by atlas
- *
- * @param atlas[in] atlas AtlasId
+ /**
+ * @brief Set the material used by an atlas
*
- * @return Sampler used by atlas
+ * @param[in] atlas AtlasId
+ * @param[in] material The Material to assign
*/
- Sampler GetSampler( AtlasId atlas ) const;
+ void SetMaterial( AtlasId atlas, Material& material );
+
private:
explicit DALI_INTERNAL AtlasManager(Internal::AtlasManager *impl);
--- /dev/null
+ /*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace AtlasMeshFactory
+{
+
+void CreateQuad( SizeType imageWidth,
+ SizeType imageHeight,
+ SizeType block,
+ const Toolkit::AtlasManager::AtlasSize& atlasSize,
+ const Vector2& position,
+ Toolkit::AtlasManager::Mesh2D& mesh )
+{
+ Toolkit::AtlasManager::Vertex2D vertex;
+
+ SizeType blockWidth = atlasSize.mBlockWidth;
+ SizeType blockHeight = atlasSize.mBlockHeight;
+
+ float vertexBlockWidth = static_cast< float >( blockWidth );
+ float vertexBlockHeight = static_cast< float >( blockHeight );
+
+ SizeType atlasWidth = atlasSize.mWidth;
+ SizeType atlasHeight = atlasSize.mHeight;
+
+ SizeType atlasWidthInBlocks = ( atlasWidth - 1u ) / blockWidth;
+
+ // Get the normalized size of a texel in both directions
+ float texelX = 1.0f / static_cast< float >( atlasWidth );
+ float texelY = 1.0f / static_cast< float >( atlasHeight );
+
+ float oneAndAHalfTexelX = texelX + ( texelX * 0.5f );
+ float oneAndAHalfTexelY = texelY + ( texelY * 0.5f );
+
+ float texelBlockWidth = texelX * vertexBlockWidth;
+ float texelBlockHeight = texelY * vertexBlockHeight;
+
+ uint32_t pixelsX = imageWidth % blockWidth;
+ uint32_t pixelsY = imageHeight % blockHeight;
+
+ if ( !pixelsX )
+ {
+ pixelsX = blockWidth;
+ }
+ if ( !pixelsY )
+ {
+ pixelsY = blockHeight;
+ }
+ float vertexWidth = static_cast< float >( pixelsX );
+ float vertexHeight = static_cast< float >( pixelsY );
+ float texelWidth = texelX * vertexWidth;
+ float texelHeight = texelY * vertexHeight;
+
+ // We're going to 'blit' half a pixel more on each edge
+ vertexWidth++;
+ vertexHeight++;
+
+ // Move back half a pixel
+ Vector2 topLeft = Vector2( position.x - 0.5f, position.y - 0.5f );
+
+ float fBlockX = texelBlockWidth * static_cast< float >( block % atlasWidthInBlocks );
+ float fBlockY = texelBlockHeight * static_cast< float >( block / atlasWidthInBlocks );
+
+ // Add on texture filtering compensation ( half a texel plus compensation for filled pixel in top left corner )
+ fBlockX += oneAndAHalfTexelX;
+ fBlockY += oneAndAHalfTexelY;
+
+ float texelWidthOffset = texelWidth + texelX;
+ float texelHeightOffset = texelHeight + texelY;
+
+ // Top left
+ vertex.mPosition.x = topLeft.x;
+ vertex.mPosition.y = topLeft.y;
+ vertex.mTexCoords.x = fBlockX;
+ vertex.mTexCoords.y = fBlockY;
+
+ mesh.mVertices.Reserve( 4u );
+ mesh.mVertices.PushBack( vertex );
+
+ // Top Right
+ vertex.mPosition.x = topLeft.x + vertexWidth;
+ vertex.mPosition.y = topLeft.y;
+ vertex.mTexCoords.x = fBlockX + texelWidthOffset;
+ vertex.mTexCoords.y = fBlockY;
+
+ mesh.mVertices.PushBack( vertex );
+
+ // Bottom Left
+ vertex.mPosition.x = topLeft.x;
+ vertex.mPosition.y = topLeft.y + vertexHeight;
+ vertex.mTexCoords.x = fBlockX;
+ vertex.mTexCoords.y = fBlockY + texelHeightOffset;
+
+ mesh.mVertices.PushBack( vertex );
+
+ // Bottom Right
+ vertex.mPosition.x = topLeft.x + vertexWidth;
+ vertex.mPosition.y = topLeft.y + vertexHeight;
+ vertex.mTexCoords.x = fBlockX + texelWidthOffset;
+ vertex.mTexCoords.y = fBlockY + texelHeightOffset;
+
+ mesh.mVertices.PushBack( vertex );
+
+ // Six indices in counter clockwise winding
+ mesh.mIndices.Reserve( 6u );
+ mesh.mIndices.PushBack( 1u );
+ mesh.mIndices.PushBack( 0u );
+ mesh.mIndices.PushBack( 2u );
+ mesh.mIndices.PushBack( 2u );
+ mesh.mIndices.PushBack( 3u );
+ mesh.mIndices.PushBack( 1u );
+}
+
+void AppendMesh( Toolkit::AtlasManager::Mesh2D& first,
+ const Toolkit::AtlasManager::Mesh2D& second )
+{
+ const uint32_t verticesCount = first.mVertices.Size();
+ first.mVertices.Insert( first.mVertices.End(),
+ second.mVertices.Begin(),
+ second.mVertices.End() );
+
+ const uint32_t indicesCount = first.mIndices.Size();
+ first.mIndices.Insert( first.mIndices.End(),
+ second.mIndices.Begin(),
+ second.mIndices.End() );
+
+ for( Vector<unsigned int>::Iterator it = first.mIndices.Begin() + indicesCount,
+ endIt = first.mIndices.End();
+ it != endIt;
+ ++it )
+ {
+ *it += verticesCount;
+ }
+}
+
+} // namespace AtlasMeshFactory
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_ATLAS_MESH_FACTORY_H__
+#define __DALI_TOOLKIT_ATLAS_MESH_FACTORY_H__
+
+/*
+ * Copyright (c) 2015 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/rendering/atlas/atlas-manager.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace AtlasMeshFactory
+{
+ typedef uint32_t SizeType;
+
+ /**
+ * @brief Create a Quad that describes an area in an atlas and a position.
+ *
+ * @param[in] width Width of area in pixels.
+ * @param[in] height Height of area in pixels.
+ * @param[in] block Block position in atlas.
+ * @param[in] atlasSize Atlas and block dimensions.
+ * @param[in] position Position to place area in space.
+ * @param[out] mesh Mesh object to hold created quad.
+ */
+ void CreateQuad( SizeType width,
+ SizeType height,
+ SizeType block,
+ const Toolkit::AtlasManager::AtlasSize& atlasSize,
+ const Vector2& position,
+ Toolkit::AtlasManager::Mesh2D& mesh );
+
+ /**
+ * @brief Append one mesh to another.
+ *
+ * @param[in,out] first Mesh to append to.
+ * @param[in] second Mesh to append.
+ */
+ void AppendMesh( Toolkit::AtlasManager::Mesh2D& first,
+ const Toolkit::AtlasManager::Mesh2D& second );
+
+} // namespace AtlasMeshFactory
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // __DALI_TOOLKIT_ATLAS_MESH_FACTORY_H__
// EXTERNAL INCLUDES
#include <dali/integration-api/debug.h>
-#include <dali/public-api/common/stage.h>
-#include <dali/public-api/images/frame-buffer-image.h>
-#include <dali/public-api/render-tasks/render-task.h>
-#include <dali/public-api/render-tasks/render-task-list.h>
#include <dali/devel-api/rendering/renderer.h>
#include <dali/devel-api/rendering/geometry.h>
#include <dali/devel-api/text-abstraction/font-client.h>
#include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
#include <dali-toolkit/internal/text/glyph-run.h>
#include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
#include <dali-toolkit/internal/text/text-view.h>
using namespace Dali;
const float ZERO( 0.0f );
const float HALF( 0.5f );
const float ONE( 1.0f );
-const float TWO( 2.0f );
const uint32_t DEFAULT_ATLAS_WIDTH = 512u;
const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
}
-struct AtlasRenderer::Impl : public ConnectionTracker
+
+struct AtlasRenderer::Impl
{
enum Style
{
bool IsGlyphUnderlined( GlyphIndex index,
const Vector<GlyphRun>& underlineRuns )
{
- // TODO: At the moment it works because we always traverse the glyphs starting from the beginning
- // and there is only one glyph run! If there are more they should be ordered.
-
for( Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
endIt = underlineRuns.End();
it != endIt;
lastUnderlinedFontId = glyph.fontId;
} // underline
- if ( !mGlyphManager.Cached( glyph.fontId, glyph.index, slot ) )
+ if ( !mGlyphManager.IsCached( glyph.fontId, glyph.index, slot ) )
{
// Select correct size for new atlas if needed....?
if ( lastFontId != glyph.fontId )
// Now remove references for the old text
RemoveText();
mTextCache.Swap( newTextCache );
- RemoveAllShadowRenderTasks();
if( thereAreUnderlinedGlyphs )
{
// Create an effect if necessary
if ( style == STYLE_DROP_SHADOW )
{
- Actor shadowActor = GenerateShadow( *mIt, actorSize, shadowOffset, shadowColor );
- shadowActor.Add( actor );
- actor = shadowActor;
+ // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues.
+ Actor containerActor = Actor::New();
+ containerActor.SetParentOrigin( ParentOrigin::CENTER );
+ containerActor.SetSize( actorSize );
+
+ Actor shadowActor = Actor::New();
+#if defined(DEBUG_ENABLED)
+ shadowActor.SetName( "Text Shadow renderable actor" );
+#endif
+ // Offset shadow in x and y
+ shadowActor.RegisterProperty("uOffset", shadowOffset );
+ if ( actor.GetRendererCount() )
+ {
+ Dali::Renderer renderer( actor.GetRendererAt( 0 ) );
+ Geometry geometry = renderer.GetGeometry();
+ Material material = renderer.GetMaterial();
+
+ Dali::Renderer shadowRenderer = Dali::Renderer::New( geometry, material );
+ shadowRenderer.SetDepthIndex( renderer.GetDepthIndex() - 1 );
+ shadowActor.AddRenderer( shadowRenderer );
+ shadowActor.SetParentOrigin( ParentOrigin::CENTER );
+ shadowActor.SetSize( actorSize );
+ shadowActor.SetColor( shadowColor );
+ containerActor.Add( shadowActor );
+ containerActor.Add( actor );
+ actor = containerActor;
+ }
}
if( mActor )
actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
actor.SetSize( actorSize );
actor.SetColor( meshRecord.mColor );
+ actor.RegisterProperty("uOffset", Vector2::ZERO );
return actor;
}
{
if ( slot.mAtlasId == mIt->mAtlasId && color == mIt->mColor )
{
- // Stitch the mesh to the existing mesh and adjust any extents
- mGlyphManager.StitchMesh( mIt->mMesh, newMesh );
+ // Append the mesh to the existing mesh and adjust any extents
+ Toolkit::Internal::AtlasMeshFactory::AppendMesh( mIt->mMesh, newMesh );
if( underlineGlyph )
{
if ( underlineColor == textColor )
{
- mGlyphManager.StitchMesh( meshRecords[ index ].mMesh, newMesh );
+ Toolkit::Internal::AtlasMeshFactory::AppendMesh( meshRecords[ index ].mMesh, newMesh );
}
else
{
}
}
- Actor GenerateShadow( MeshRecord& meshRecord,
- const Vector2& actorSize,
- const Vector2& shadowOffset,
- const Vector4& shadowColor )
- {
- // Scan vertex buffer to determine width and height of effect buffer needed
- const Vector< AtlasManager::Vertex2D >& verts = meshRecord.mMesh.mVertices;
- float tlx = verts[ 0 ].mPosition.x;
- float tly = verts[ 0 ].mPosition.y;
- float brx = ZERO;
- float bry = ZERO;
-
- for ( uint32_t i = 0; i < verts.Size(); ++i )
- {
- if ( verts[ i ].mPosition.x < tlx )
- {
- tlx = verts[ i ].mPosition.x;
- }
- if ( verts[ i ].mPosition.y < tly )
- {
- tly = verts[ i ].mPosition.y;
- }
- if ( verts[ i ].mPosition.x > brx )
- {
- brx = verts[ i ].mPosition.x;
- }
- if ( verts[ i ].mPosition.y > bry )
- {
- bry = verts[ i ].mPosition.y;
- }
- }
-
- float width = brx - tlx;
- float height = bry - tly;
- float divWidth = TWO / width;
- float divHeight = TWO / height;
-
- // Create a buffer to render to
- meshRecord.mBuffer = FrameBufferImage::New( width, height );
-
- // We will render a quad into this buffer
- unsigned int indices[ 6 ] = { 1, 0, 2, 2, 3, 1 };
- PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, 4u );
- PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, sizeof(indices)/sizeof(indices[0]) );
-
- AtlasManager::Vertex2D vertices[ 4 ] = {
- { Vector2( tlx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ZERO, ZERO ) },
- { Vector2( brx + shadowOffset.x, tly + shadowOffset.y ), Vector2( ONE, ZERO ) },
- { Vector2( tlx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ZERO, ONE ) },
- { Vector2( brx + shadowOffset.x, bry + shadowOffset.y ), Vector2( ONE, ONE ) } };
-
- quadVertices.SetData( vertices );
- quadIndices.SetData( indices );
-
- Geometry quadGeometry = Geometry::New();
- quadGeometry.AddVertexBuffer( quadVertices );
- quadGeometry.SetIndexBuffer( quadIndices );
-
- Sampler sampler = Sampler::New( meshRecord.mBuffer, "sTexture" );
- Material material = Material::New( mGlyphManager.GetEffectBufferShader() );
- material.AddSampler( sampler );
-
- Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, material );
-
- // Set depth index to -1.0 to make sure shadow is rendered first in 3D layers
- renderer.SetDepthIndex( -1.0f );
- Actor actor = Actor::New();
- actor.AddRenderer( renderer );
- actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
- actor.SetSize( actorSize );
-
- // Create a sub actor to render the source with normalized vertex positions
- Vector< AtlasManager::Vertex2D > normVertexList;
- for ( uint32_t i = 0; i < verts.Size(); ++i )
- {
- AtlasManager::Vertex2D vertex = verts[ i ];
- vertex.mPosition.x = ( ( vertex.mPosition.x - tlx ) * divWidth ) - ONE;
- vertex.mPosition.y = ( ( vertex.mPosition.y - tly ) * divHeight ) - ONE;
- normVertexList.PushBack( vertex );
- }
-
- PropertyBuffer normVertices = PropertyBuffer::New( mQuadVertexFormat, normVertexList.Size() );
- PropertyBuffer normIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() );
- normVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &normVertexList[ 0 ] ) );
- normIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) );
-
- Geometry normGeometry = Geometry::New();
- normGeometry.AddVertexBuffer( normVertices );
- normGeometry.SetIndexBuffer( normIndices );
-
- Material normMaterial = Material::New( mGlyphManager.GetGlyphShadowShader() );
- Sampler normSampler = mGlyphManager.GetSampler( meshRecord.mAtlasId );
- normMaterial.AddSampler( normSampler );
- Dali::Renderer normRenderer = Dali::Renderer::New( normGeometry, normMaterial );
- Actor subActor = Actor::New();
- subActor.AddRenderer( normRenderer );
- subActor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
- subActor.SetSize( actorSize );
- subActor.SetColor( shadowColor );
-
- // Create a render task to render the effect
- RenderTask shadowTask = Stage::GetCurrent().GetRenderTaskList().CreateTask();
- shadowTask.SetTargetFrameBuffer( meshRecord.mBuffer );
- shadowTask.SetSourceActor( subActor );
- shadowTask.SetClearEnabled( true );
- shadowTask.SetClearColor( Vector4::ZERO );
- shadowTask.SetExclusive( true );
- shadowTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
- shadowTask.FinishedSignal().Connect( this, &AtlasRenderer::Impl::RenderComplete );
- mShadowTasks.push_back( shadowTask );
- actor.Add( subActor );
-
- return actor;
- }
-
- void RemoveShadowRenderTask( RenderTask renderTask )
- {
- if( renderTask )
- {
- renderTask.FinishedSignal().Disconnect( this, &AtlasRenderer::Impl::RenderComplete );
-
- // Guard to prevent accessing Stage after dali-core destruction
- if( Stage::IsInstalled() )
- {
- Stage::GetCurrent().GetRenderTaskList().RemoveTask( renderTask );
- }
- renderTask.Reset();
- }
- }
-
- void RenderComplete( RenderTask& renderTask )
- {
- // Get the actor used for render to buffer and remove it from the parent
- Actor renderActor = renderTask.GetSourceActor();
- if ( renderActor )
- {
- Actor parent = renderActor.GetParent();
- if ( parent )
- {
- parent.Remove( renderActor );
- }
- }
-
- RemoveShadowRenderTask( renderTask );
- }
-
- void RemoveAllShadowRenderTasks()
- {
- for ( std::vector< RenderTask >::iterator shadowIterator = mShadowTasks.begin();
- shadowIterator != mShadowTasks.end(); ++shadowIterator )
- {
- RemoveShadowRenderTask( *shadowIterator );
- }
- }
-
Actor mActor; ///< The actor parent which renders the text
AtlasGlyphManager mGlyphManager; ///< Glyph Manager to handle upload and caching
- std::vector< RenderTask > mShadowTasks; ///< Used to render shadows
TextAbstraction::FontClient mFontClient; ///> The font client used to supply glyph information
std::vector< MaxBlockSize > mBlockSizes; ///> Maximum size needed to contain a glyph in a block within a new atlas
- std::vector< uint32_t > mFace; ///> Face indices for a quad
- Vector< TextCacheEntry > mTextCache;
- Property::Map mQuadVertexFormat;
- Property::Map mQuadIndexFormat;
- int mDepth;
+ Vector< TextCacheEntry > mTextCache; ///> Caches data from previous render
+ Property::Map mQuadVertexFormat; ///> Describes the vertex format for text
+ Property::Map mQuadIndexFormat; ///> Describes the index format for text
+ int mDepth; ///> DepthIndex passed by control when connect to stage
};
Text::RendererPtr AtlasRenderer::New()
AtlasRenderer::~AtlasRenderer()
{
- mImpl->RemoveAllShadowRenderTasks();
-
mImpl->RemoveText();
delete mImpl;
}
// Get the glyphs per character table.
const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
- const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
// If the vector is void, there is no right to left characters.
const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
// Traverses glyphs in visual order. To do that use the visual to logical conversion table.
CharacterIndex visualIndex = startCharacter;
+ Length numberOfCharacters = 0u;
for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
{
// The character in logical order.
// Get the script of the character.
const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
- // The first glyph for that character in logical order.
- const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
// The number of glyphs for that character
const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
+ ++numberOfCharacters;
- // Get the metrics for the group of glyphs.
- GlyphMetrics glyphMetrics;
- GetGlyphsMetrics( glyphLogicalOrderIndex,
- numberOfGlyphs,
- glyphMetrics,
- mVisualModel,
- mMetrics );
- const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
+ if( 0u != numberOfGlyphs )
+ {
+ // Get the first character/glyph of the group of glyphs.
+ const CharacterIndex firstVisualCharacterIndex = 1u + visualIndex - numberOfCharacters;
+ const CharacterIndex firstLogicalCharacterIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + firstVisualCharacterIndex ) : firstVisualCharacterIndex;
+ const GlyphIndex firstLogicalGlyphIndex = *( charactersToGlyphBuffer + firstLogicalCharacterIndex );
- // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»»...
- const Length numberOfCharactersInLigature = HasLigatureMustBreak( script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
- const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
+ // Get the metrics for the group of glyphs.
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( firstLogicalGlyphIndex,
+ numberOfGlyphs,
+ glyphMetrics,
+ mVisualModel,
+ mMetrics );
- for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
- {
- // Find the mid-point of the area containing the glyph
- const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
+ // Get the position of the first glyph.
+ const Vector2& position = *( positionsBuffer + firstLogicalGlyphIndex );
+
+ // Whether the glyph can be split, like Latin ligatures fi, ff or Arabic ï»».
+ const bool isInterglyphIndex = ( numberOfCharacters > numberOfGlyphs ) && HasLigatureMustBreak( script );
+ const Length numberOfBlocks = isInterglyphIndex ? numberOfCharacters : 1u;
+ const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfBlocks );
+
+ GlyphIndex index = 0u;
+ for( ; !matched && ( index < numberOfBlocks ); ++index )
+ {
+ // Find the mid-point of the area containing the glyph
+ const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
- if( visualX < glyphCenter )
+ if( visualX < glyphCenter )
+ {
+ matched = true;
+ break;
+ }
+ }
+
+ if( matched )
{
- visualIndex += index;
- matched = true;
+ visualIndex = firstVisualCharacterIndex + index;
break;
}
- }
- if( matched )
- {
- break;
+ numberOfCharacters = 0u;
}
+
}
+
// Return the logical position of the cursor in characters.
if( !matched )
InsertText( stringToPaste, Text::Controller::COMMIT );
mImpl->ChangeState( EventData::EDITING );
mImpl->RequestRelayout();
+
+ // Do this last since it provides callbacks into application code
+ mImpl->mControlInterface.TextChanged();
}
void Controller::PasteClipboardItemEvent()
{
+ // Retrieve the clipboard contents first
ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
std::string stringToPaste( notifier.GetContent() );
+
+ // Commit the current pre-edit text; the contents of the clipboard should be appended
+ mImpl->ResetImfManager();
+
+ // Paste
PasteText( stringToPaste );
}
const unsigned int TOOLKIT_MAJOR_VERSION = 1;
const unsigned int TOOLKIT_MINOR_VERSION = 1;
-const unsigned int TOOLKIT_MICRO_VERSION = 6;
+const unsigned int TOOLKIT_MICRO_VERSION = 7;
const char * const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
},
"scrollview":
{
- "overshoot-effect-color":"B018"
+ "overshoot-effect-color":"B018",
+ "overshoot-animation-speed":120.0,
+ "overshoot-size":[480.0,42.0]
+ },
+ "itemview":
+ {
+ "overshoot-effect-color":"B018",
+ "overshoot-animation-speed":120.0,
+ "overshoot-size":[480.0,42.0]
}
}
}
},
"scrollview":
{
- "overshoot-effect-color":"B018"
+ "overshoot-effect-color":"B018",
+ "overshoot-animation-speed":360.0,
+ "overshoot-size":[720.0,130.0]
+ },
+ "itemview":
+ {
+ "overshoot-effect-color":"B018",
+ "overshoot-animation-speed":360.0,
+ "overshoot-size":[720.0,130.0]
}
}
}
Images for that style should be in their own folder named images.
-Common images not specific for a particular style will be in the dali-toolkit common images folder.
+Common images not specific for a particular style will be in the images-common folder located in the style folder.
*
*/
Name: dali-toolkit
Summary: The OpenGLES Canvas Core Library Toolkit
-Version: 1.1.6
+Version: 1.1.7
Release: 1
Group: System/Libraries
License: Apache-2.0, BSD-2.0, MIT