Merge "Fix SVACE error in KeyboardFocusManager" into devel/master
authorDavid Steele <david.steele@samsung.com>
Fri, 16 Dec 2016 15:08:44 +0000 (07:08 -0800)
committerGerrit Code Review <gerrit@review.vlan103.tizen.org>
Fri, 16 Dec 2016 15:08:44 +0000 (07:08 -0800)
25 files changed:
automated-tests/resources/anim.gif [new file with mode: 0644]
automated-tests/src/dali-toolkit-internal/utc-Dali-VisualFactoryResolveUrl.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
dali-toolkit/devel-api/image-loader/image-atlas.cpp
dali-toolkit/devel-api/image-loader/image-atlas.h
dali-toolkit/internal/controls/table-view/table-view-impl.cpp
dali-toolkit/internal/file.list
dali-toolkit/internal/image-loader/atlas-packer.cpp
dali-toolkit/internal/image-loader/atlas-packer.h
dali-toolkit/internal/image-loader/image-atlas-impl.cpp
dali-toolkit/internal/image-loader/image-atlas-impl.h
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/animated-image/animated-image-visual.h [new file with mode: 0644]
dali-toolkit/internal/visuals/color/color-visual.cpp
dali-toolkit/internal/visuals/gradient/gradient-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/svg/svg-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.cpp
dali-toolkit/internal/visuals/visual-factory-cache.cpp
dali-toolkit/internal/visuals/visual-factory-impl.cpp
dali-toolkit/internal/visuals/visual-factory-resolve-url.h
docs/content/images/visuals/animated-image-visual.gif [new file with mode: 0644]
docs/content/shared-javascript-and-cpp-documentation/visuals.md

diff --git a/automated-tests/resources/anim.gif b/automated-tests/resources/anim.gif
new file mode 100644 (file)
index 0000000..f66f076
Binary files /dev/null and b/automated-tests/resources/anim.gif differ
index f859b74..8235355 100644 (file)
@@ -30,8 +30,6 @@ int UtcDaliResolveUrlRegularImage(void)
 
   DALI_TEST_EQUALS( UrlType::REGULAR_IMAGE, ResolveUrlType("foobar.jpeg"), TEST_LOCATION );
 
-  DALI_TEST_EQUALS( UrlType::REGULAR_IMAGE, ResolveUrlType("foobar.gif"), TEST_LOCATION );
-
   DALI_TEST_EQUALS( UrlType::REGULAR_IMAGE, ResolveUrlType("foobar.PNG"), TEST_LOCATION );
 
   DALI_TEST_EQUALS( UrlType::REGULAR_IMAGE, ResolveUrlType("foobar.Png123"), TEST_LOCATION );
@@ -78,8 +76,6 @@ int UtcDaliResolveUrlNPatch(void)
 {
   tet_infoline( "UtcDaliResolveUrl N_PATCH" );
 
-  DALI_TEST_EQUALS( UrlType::N_PATCH, ResolveUrlType("foobar.9.gif"), TEST_LOCATION );
-
   DALI_TEST_EQUALS( UrlType::N_PATCH, ResolveUrlType("foobar.#.png"), TEST_LOCATION );
 
   DALI_TEST_EQUALS( UrlType::N_PATCH, ResolveUrlType("foobar.9.9.bmp"), TEST_LOCATION );
@@ -94,3 +90,26 @@ int UtcDaliResolveUrlNPatch(void)
 
   END_TEST;
 }
+
+int UtcDaliResolveUrlGif(void)
+{
+  tet_infoline( "UtcDaliResolveUrl GIF" );
+
+  DALI_TEST_EQUALS( UrlType::GIF, ResolveUrlType("foobar.gif"), TEST_LOCATION );
+
+  DALI_TEST_EQUALS( UrlType::GIF, ResolveUrlType("foobar.gif.gif"), TEST_LOCATION );
+
+  DALI_TEST_EQUALS( UrlType::GIF, ResolveUrlType("foobar.giF"), TEST_LOCATION );
+
+  DALI_TEST_EQUALS( UrlType::GIF, ResolveUrlType("foobar.GIF"), TEST_LOCATION );
+
+  DALI_TEST_EQUALS( UrlType::GIF, ResolveUrlType(".GiF"), TEST_LOCATION );
+
+  // GIFs aren't N-patch
+  DALI_TEST_EQUALS( UrlType::GIF, ResolveUrlType("foobar.9.gif"), TEST_LOCATION );
+
+  DALI_TEST_EQUALS( UrlType::REGULAR_IMAGE, ResolveUrlType("gif.png"), TEST_LOCATION );
+  DALI_TEST_EQUALS( UrlType::REGULAR_IMAGE, ResolveUrlType("gif.gif1"), TEST_LOCATION );
+
+  END_TEST;
+}
index 0bee5e5..17b4432 100644 (file)
@@ -40,14 +40,35 @@ static const char* gImageNonExist = "non-exist.jpg";
 
 const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
 
+PixelData CreatePixelData( unsigned int width, unsigned int height )
+{
+  unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::RGBA8888 );
+
+  unsigned char* buffer= reinterpret_cast<unsigned char*>( malloc( bufferSize ) );
+  PixelData pixelData = PixelData::New( buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE );
+
+  return pixelData;
+}
+
 Rect<int> TextureCoordinateToPixelArea( const Vector4& textureCoordinate, float size )
 {
   Vector4 temp = textureCoordinate * size;
   Rect<int> pixelArea;
   pixelArea.x = static_cast<int>( temp.x );
   pixelArea.y = static_cast<int>( temp.y );
-  pixelArea.width = static_cast<int>( temp.z-temp.x+1.f );
-  pixelArea.height = static_cast<int>( temp.w-temp.y+1.f );
+  pixelArea.width = static_cast<int>( temp.z-temp.x+1.01f );
+  pixelArea.height = static_cast<int>( temp.w-temp.y+1.01f );
+
+  return pixelArea;
+}
+
+Rect<int> TextureCoordinateToPixelArea( const Vector4& textureCoordinate, float width, float height )
+{
+  Rect<int> pixelArea;
+  pixelArea.x = static_cast<int>( textureCoordinate.x*width );
+  pixelArea.y = static_cast<int>( textureCoordinate.y*height);
+  pixelArea.width = static_cast<int>( (textureCoordinate.z-textureCoordinate.x)*width+1.01f );
+  pixelArea.height = static_cast<int>( (textureCoordinate.w-textureCoordinate.y)*height+1.01f );
 
   return pixelArea;
 }
@@ -434,3 +455,56 @@ int UtcDaliImageAtlasImageView(void)
 
   END_TEST;
 }
+
+int UtcDaliImageAtlasPackToAtlas(void)
+{
+  ToolkitTestApplication application;
+
+  std::vector<PixelData> pixelDataContainer;
+  pixelDataContainer.push_back( CreatePixelData( 20, 30 ) );
+  pixelDataContainer.push_back( CreatePixelData( 10, 10 ) );
+  pixelDataContainer.push_back( CreatePixelData( 45, 30 ) );
+  pixelDataContainer.push_back( CreatePixelData( 20, 20 ) );
+
+  Dali::Vector<Vector4> textureRects;
+  Texture texture = ImageAtlas::PackToAtlas( pixelDataContainer, textureRects  );
+
+ // --------------
+ // |            |
+ // |    45*30   |
+//  |            |
+//  --------------
+//  | 20 |    | 20*20
+//  |  * |____|
+//  | 30 |  |  10*10
+//  --------
+
+  DALI_TEST_EQUALS( texture.GetWidth(), 45, TEST_LOCATION );
+  DALI_TEST_EQUALS( texture.GetHeight(), 60, TEST_LOCATION );
+
+  Rect<int> pixelArea = TextureCoordinateToPixelArea(textureRects[0], texture.GetWidth(), texture.GetHeight());
+  DALI_TEST_EQUALS( pixelArea.x, 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.y, 30, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.width, 20, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.height, 30, TEST_LOCATION );
+
+  pixelArea = TextureCoordinateToPixelArea(textureRects[1], texture.GetWidth(), texture.GetHeight());
+  DALI_TEST_EQUALS( pixelArea.x, 20, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.y, 50, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.width, 10, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.height, 10, TEST_LOCATION );
+
+  pixelArea = TextureCoordinateToPixelArea(textureRects[2], texture.GetWidth(), texture.GetHeight());
+  DALI_TEST_EQUALS( pixelArea.x, 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.y, 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.width, 45, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.height, 30, TEST_LOCATION );
+
+  pixelArea = TextureCoordinateToPixelArea(textureRects[3], texture.GetWidth(), texture.GetHeight());
+  DALI_TEST_EQUALS( pixelArea.x, 20, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.y, 30, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.width, 20, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.height, 20, TEST_LOCATION );
+
+  END_TEST;
+}
index fae713a..4885bc2 100644 (file)
@@ -38,6 +38,7 @@ namespace
 const char* TEST_IMAGE_FILE_NAME =  TEST_RESOURCE_DIR "/gallery-small-1.jpg";
 const char* TEST_NPATCH_FILE_NAME =  "gallery_image_01.9.jpg";
 const char* TEST_SVG_FILE_NAME = TEST_RESOURCE_DIR "/svg1.svg";
+const char* TEST_GIF_FILE_NAME = TEST_RESOURCE_DIR "/anim.gif";
 const char* TEST_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube.obj";
 const char* TEST_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal.mtl";
 const char* TEST_RESOURCE_LOCATION = TEST_RESOURCE_DIR "/";
@@ -292,6 +293,14 @@ int UtcDaliVisualSize(void)
   const float height = textVisual.GetHeightForWidth( 40.f );
   DALI_TEST_EQUALS( height, 40.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
 
+  //AnimatedImageVisual
+  Visual::Base animatedImageVisual = factory.CreateVisual( TEST_GIF_FILE_NAME, ImageDimensions() );
+  animatedImageVisual.SetTransformAndSize(DefaultTransform(), controlSize );
+  animatedImageVisual.GetNaturalSize(naturalSize);
+  // TEST_GIF_FILE: anim.gif
+  // resolution: 50*50, frame count: 4, frame delay: 0.2 second for each frame
+  DALI_TEST_EQUALS( naturalSize, Vector2(50.f, 50.f), TEST_LOCATION );
+
   END_TEST;
 }
 
@@ -990,6 +999,45 @@ int UtcDaliVisualGetPropertyMap10(void)
   END_TEST;
 }
 
+int UtcDaliVisualGetPropertyMap11(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualGetPropertyMap7: AnimatedImageVisual" );
+
+  // request SvgVisual with a property map
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  Visual::Base svgVisual = factory.CreateVisual( Property::Map()
+                                                 .Add( Visual::Property::TYPE,  Visual::IMAGE )
+                                                 .Add( ImageVisual::Property::URL, TEST_GIF_FILE_NAME ) );
+
+  Property::Map resultMap;
+  svgVisual.CreatePropertyMap( resultMap );
+  // check the property values from the returned map from a visual
+  Property::Value* value = resultMap.Find( Visual::Property::TYPE,  Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<int>() == Visual::IMAGE );
+
+  value = resultMap.Find( ImageVisual::Property::URL,  Property::STRING );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<std::string>() == TEST_GIF_FILE_NAME );
+
+  // request SvgVisual with an URL
+  Visual::Base svgVisual2 = factory.CreateVisual( TEST_GIF_FILE_NAME, ImageDimensions() );
+  resultMap.Clear();
+  svgVisual2.CreatePropertyMap( resultMap );
+  // check the property values from the returned map from a visual
+  value = resultMap.Find( Visual::Property::TYPE,  Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<int>() == Visual::IMAGE );
+
+  value = resultMap.Find( ImageVisual::Property::URL,  Property::STRING );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<std::string>() == TEST_GIF_FILE_NAME );
+
+  END_TEST;
+}
+
 int UtcDaliVisualGetPropertyMapBatchImageVisualNoAtlas(void)
 {
   ToolkitTestApplication application;
index b186ea4..0e9c014 100644 (file)
@@ -17,6 +17,7 @@
 #include <iostream>
 #include <stdlib.h>
 #include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-timer.h>
 #include <toolkit-bitmap-loader.h>
 #include <toolkit-event-thread-callback.h>
 #include <dali/public-api/rendering/renderer.h>
@@ -44,6 +45,9 @@ const char* TEST_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal.mtl";
 const char* TEST_SIMPLE_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube-Points-Only.obj";
 const char* TEST_SIMPLE_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal-Simple.mtl";
 
+// resolution: 50*50, frame count: 4, frame delay: 0.2 second for each frame
+const char* TEST_GIF_FILE_NAME = TEST_RESOURCE_DIR "/anim.gif";
+
 // resolution: 34*34, pixel format: RGBA8888
 static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
 // resolution: 600*600, pixel format: RGB888
@@ -2051,3 +2055,82 @@ int UtcDaliVisualFactoryGetBatchImageVisual3(void)
 
   END_TEST;
 }
+
+int UtcDaliVisualFactoryGetAnimatedImageVisual(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualFactoryGetAnimatedImageVisual: Request animated image visual with a gif url" );
+
+  VisualFactory factory = VisualFactory::Get();
+  Visual::Base visual = factory.CreateVisual( TEST_GIF_FILE_NAME, ImageDimensions() );
+  DALI_TEST_CHECK( visual );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl actor = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+  Stage::GetCurrent().Add( actor );
+
+  application.SendNotification();
+  application.Render();
+
+  // renderer is added to actor
+  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+  // test the uniforms which used to handle the atlas rect
+  // the four frames should be located inside atlas as follows: atlas size 100*100
+  // -------------
+  // |     |     |
+  // |  0  |  1  |
+  // -------------
+  // |     |     |
+  // |  2  |  3  |
+  // -------------
+
+  Renderer renderer = actor.GetRendererAt( 0u );
+  DALI_TEST_CHECK( renderer );
+
+  Property::Value atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
+  // take into consideration the half pixel correction
+  DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(0.5f, 0.5f, 49.5f, 49.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  // waiting for the resource uploading
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+  // Force the timer used by the animatedImageVisual to tick,
+  Dali::Timer timer = Timer::New( 0 );
+  timer.MockEmitSignal();
+  application.SendNotification();
+  application.Render();
+  atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
+  // take into consideration the half pixel correction
+  DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(50.5f, 0.5f, 99.5f, 49.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  // Force the timer used by the animatedImageVisual to tick,
+  timer.MockEmitSignal();
+  application.SendNotification();
+  application.Render();
+  atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
+  // take into consideration the half pixel correction
+  DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(0.5f, 50.5f, 49.5f, 99.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  // Force the timer used by the animatedImageVisual to tick,
+  timer.MockEmitSignal();
+  application.SendNotification();
+  application.Render();
+  atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
+  // take into consideration the half pixel correction
+  DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(50.5f, 50.5f, 99.5f, 99.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  // Test SetOffStage().
+  actor.Unparent();
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+  END_TEST;
+}
index fd6825e..84903b3 100644 (file)
@@ -35,6 +35,11 @@ ImageAtlas::~ImageAtlas()
 {
 }
 
+Texture ImageAtlas::PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects  )
+{
+  return Internal::ImageAtlas::PackToAtlas( pixelData, textureRects );
+}
+
 ImageAtlas::ImageAtlas(Internal::ImageAtlas* internal)
 : BaseHandle( internal )
 {
index 98796bd..a0305fd 100644 (file)
@@ -20,6 +20,7 @@
 // EXTERNAL INCLUDES
 #include <string>
 #include <stdint.h>
+#include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/base-handle.h>
 #include <dali/public-api/images/image-operations.h>
 #include <dali/public-api/images/pixel.h>
@@ -34,6 +35,7 @@ namespace Dali
 
 namespace Toolkit
 {
+
 namespace Internal DALI_INTERNAL
 {
 class ImageAtlas;
@@ -54,6 +56,14 @@ public:
 public:
 
   /**
+   * @brief Pack a group of  pixel data into atlas.
+   * @param[in] pixelData The group of the pixel data to be packed into the atlas.
+   * @param[out] textureRects The list of texture areas where each frame is located inside the atlas.
+   * @return The atlas texture.
+   */
+  static Texture PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects  );
+
+  /**
    * @brief Create a new ImageAtlas.
    *
    * @param [in] width          The atlas width in pixels.
index 4a655aa..e3b0334 100644 (file)
@@ -1319,51 +1319,74 @@ Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolki
       int numberOfColumns = GetColumns();
       int numberOfRows = GetRows();
 
+      bool lastCell = false;
+      Actor nextValidActor;
+
       switch ( direction )
       {
         case Toolkit::Control::KeyboardFocus::LEFT:
         {
-          if(--currentColumn < 0)
+          do
           {
-            currentColumn = numberOfColumns - 1;
-            if(--currentRow < 0)
+            if(--currentColumn < 0)
             {
-              currentRow = loopEnabled ? numberOfRows - 1 : 0;
-              focusLost = (currentRow == 0);
+              currentColumn = numberOfColumns - 1;
+              if(--currentRow < 0)
+              {
+                lastCell = true;
+                currentRow = loopEnabled ? numberOfRows - 1 : 0;
+                focusLost = (currentRow == 0);
+              }
             }
-          }
+            nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
+          } while ( !nextValidActor && !lastCell );
           break;
         }
         case Toolkit::Control::KeyboardFocus::RIGHT:
         {
-          if(++currentColumn > numberOfColumns - 1)
+          do
           {
-            currentColumn = 0;
-            if(++currentRow > numberOfRows - 1)
+            if(++currentColumn > numberOfColumns - 1)
             {
-              currentRow = loopEnabled ? 0 : numberOfRows - 1;
-              focusLost = (currentRow == numberOfRows - 1);
+              currentColumn = 0;
+              if(++currentRow > numberOfRows - 1)
+              {
+                lastCell = true;
+                currentRow = loopEnabled ? 0 : numberOfRows - 1;
+                focusLost = (currentRow == numberOfRows - 1);
+              }
             }
-          }
+            nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
+          } while ( !nextValidActor && !lastCell );
           break;
         }
         case Toolkit::Control::KeyboardFocus::UP:
         {
-          if(--currentRow < 0)
+          do
           {
-            currentRow = loopEnabled ? numberOfRows - 1 : 0;
-            focusLost = (currentRow == 0);
-          }
+            if(--currentRow < 0)
+            {
+              lastCell = true;
+              currentRow = loopEnabled ? numberOfRows - 1 : 0;
+              focusLost = (currentRow == 0);
+            }
+            nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
+          } while ( !nextValidActor && !lastCell );
           break;
         }
         case Toolkit::Control::KeyboardFocus::DOWN:
 
         {
-          if(++currentRow > numberOfRows - 1)
+          do
           {
-            currentRow = loopEnabled ? 0 : numberOfRows - 1;
-            focusLost = (currentRow == numberOfRows - 1);
-          }
+            if(++currentRow > numberOfRows - 1)
+            {
+              lastCell = true;
+              currentRow = loopEnabled ? 0 : numberOfRows - 1;
+              focusLost = (currentRow == numberOfRows - 1);
+            }
+            nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
+          } while ( !nextValidActor && !lastCell );
           break;
         }
       }
index 7fa3af7..a4f49d9 100644 (file)
@@ -17,6 +17,7 @@ toolkit_src_files = \
    $(toolkit_src_dir)/visuals/visual-factory-cache.cpp \
    $(toolkit_src_dir)/visuals/visual-factory-impl.cpp \
    $(toolkit_src_dir)/visuals/visual-string-constants.cpp \
+   $(toolkit_src_dir)/visuals/animated-image/animated-image-visual.cpp \
    $(toolkit_src_dir)/visuals/border/border-visual.cpp \
    $(toolkit_src_dir)/visuals/color/color-visual.cpp \
    $(toolkit_src_dir)/visuals/gradient/gradient.cpp \
index f452399..4212188 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL HEADER
 #include <stdlib.h> // For abs()
+#include <dali/integration-api/debug.h>
 
 namespace Dali
 {
@@ -38,6 +39,18 @@ bool ApproximatelyEqual( uint32_t a, uint32_t b  )
   return abs( a-b ) <= 1;
 }
 
+uint16_t MaxDimension( const Uint16Pair& dimensions )
+{
+  return dimensions.GetWidth() >= dimensions.GetHeight() ? dimensions.GetWidth() : dimensions.GetHeight();
+}
+
+void Swap( Uint16Pair& first, Uint16Pair& second )
+{
+  Uint16Pair temp = first;
+  first = second;
+  second = temp;
+}
+
 }
 
 AtlasPacker::Node::Node( Node* parent, SizeType x, SizeType y, SizeType width, SizeType height  )
@@ -195,6 +208,103 @@ void AtlasPacker::DeleteNode( Node *node )
   }
 }
 
+Uint16Pair AtlasPacker::GroupPack( const Dali::Vector<Uint16Pair>& blockSizes, Dali::Vector<Uint16Pair>& packPositions )
+{
+  uint16_t count = blockSizes.Count();
+  packPositions.Resize( count );
+
+  // Sort the blocks according to its maximum dimension. The bigger blocks are packed first.
+  Dali::Vector<Uint16Pair> packOrder;
+  packOrder.Resize( count );
+  for( uint16_t i = 0; i < count; i++ )
+  {
+    packOrder[i].SetX( MaxDimension( blockSizes[i] ) );
+    packOrder[i].SetY( i );
+  }
+  for( uint16_t i = 0; i < count-1; i++ )
+    for( uint16_t j = 0; j < count-i-1; j++ )
+    {
+      if( packOrder[j].GetX() < packOrder[j+1].GetX() )
+      {
+        Swap( packOrder[j], packOrder[j+1] );
+      }
+    }
+
+  int index = packOrder[0].GetY();
+  AtlasPacker packer( blockSizes[index].GetWidth(), blockSizes[index].GetHeight() );
+
+  SizeType packPositionX, packPositionY;
+  // pack the blocks one by one with descending size, grows as necessary to accommodate each subsequent block.
+  for( uint16_t i = 0; i < count; i++ )
+  {
+    index = packOrder[i].GetY();
+    packer.GrowPack( blockSizes[index].GetWidth(), blockSizes[index].GetHeight(),
+                     packPositionX, packPositionY );
+    packPositions[index].SetX( packPositionX );
+    packPositions[index].SetY( packPositionY );
+  }
+
+  return Uint16Pair( packer.mRoot->rectArea.width, packer.mRoot->rectArea.height );
+}
+
+void AtlasPacker::GrowPack( SizeType blockWidth, SizeType blockHeight,
+                            SizeType& packPositionX, SizeType& packPositionY )
+{
+  Node* firstFit = InsertNode( mRoot, blockWidth, blockHeight );
+  if( firstFit == NULL )
+  {
+    // Could fit in the current left space, grow the partition tree to get more space.
+    GrowNode( blockWidth, blockHeight );
+    firstFit = InsertNode( mRoot->child[1], blockWidth, blockHeight );
+  }
+
+  if( firstFit != NULL )
+  {
+    firstFit->occupied = true;
+    packPositionX = firstFit->rectArea.x;
+    packPositionY = firstFit->rectArea.y;
+  }
+}
+
+void AtlasPacker::GrowNode( SizeType blockWidth, SizeType blockHeight )
+{
+  // Attempts to maintain a roughly square ratio when choosing the growing direction: right or down
+  bool canGrowRight = blockWidth <= mRoot->rectArea.width;
+  bool canGrowDown = blockHeight <= mRoot->rectArea.height;
+
+  bool shouldGrowRight = canGrowRight && mRoot->rectArea.height >= mRoot->rectArea.width+blockWidth;
+  bool shouldGrowDown = canGrowDown && mRoot->rectArea.width >= mRoot->rectArea.height+blockHeight;
+
+  if( canGrowRight && canGrowRight )
+  {
+    shouldGrowRight = mRoot->rectArea.width+blockWidth <= mRoot->rectArea.height+blockHeight;
+    shouldGrowDown = !shouldGrowRight;
+  }
+
+  if( shouldGrowRight || ( canGrowRight && !shouldGrowDown ) )
+  {
+    Node* newRoot = new Node( NULL, 0u, 0u, mRoot->rectArea.width+blockWidth, mRoot->rectArea.height );
+    newRoot->occupied = true;
+    newRoot->child[0] = mRoot;
+    newRoot->child[1] = new Node( newRoot, mRoot->rectArea.width, 0u, blockWidth, mRoot->rectArea.height );
+
+    mRoot = newRoot;
+  }
+  else if( shouldGrowDown || ( canGrowDown && !shouldGrowRight ) )
+  {
+    Node* newRoot = new Node( NULL, 0u, 0u, mRoot->rectArea.width, mRoot->rectArea.height+blockHeight );
+    newRoot->occupied = true;
+    newRoot->child[0] = mRoot;
+    newRoot->child[1] = new Node( newRoot, 0u, mRoot->rectArea.height, mRoot->rectArea.width, blockHeight );
+
+    mRoot = newRoot;
+  }
+  else
+  {
+    DALI_LOG_ERROR( " Atlas Packer failed to grow: make sure the packing order is sorted with the block size to avoid this happening");
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index bab993f..2d6c95b 100644 (file)
@@ -18,7 +18,9 @@
  */
 
 #include <stdint.h>
+#include <dali/public-api/common/dali-vector.h>
 #include <dali/public-api/math/rect.h>
+#include <dali/public-api/math/uint-16-pair.h>
 
 namespace Dali
 {
@@ -99,6 +101,14 @@ public:
    */
   unsigned int GetAvailableArea() const;
 
+  /**
+   * Pack a group of blocks with different sizes, calculate the required packing size and the position of each block.
+   * @param[in] blockSizes The size list of the blocks .
+   * @param[out] packPositions The packing position of each block.
+   * @return The required size to accommodate all the blocks.
+   */
+  static Uint16Pair GroupPack( const Dali::Vector<Uint16Pair>& blockSizes, Dali::Vector<Uint16Pair>& packPositions );
+
 private:
 
   /*
@@ -146,11 +156,30 @@ private:
    */
   void DeleteNode( Node* node );
 
+  /**
+   * Pack a block into the atlas. If there is no enough room, grow the partition tree.
+   *
+   * @param[in] blockWidth The width of the block to pack.
+   * @param[in] blockHeight The height of the block to pack.
+   * @param[out] packPositionX The x coordinate of the position to pack the block.
+   * @param[out] packPositionY The y coordinate of the position to pack the block.
+   */
+  void GrowPack( SizeType blockWidth, SizeType blockHeight,
+                 SizeType& packPositionX, SizeType& packPositionY );
+
+  /**
+   * Add extra node into the partition tree to accommodate the given block.
+   *
+   * @param[in] blockWidth The width of the block to pack.
+   * @param[in] blockHeight The height of the block to pack.
+   */
+  void GrowNode( SizeType blockWidth, SizeType blockHeight );
+
   // Undefined
-  AtlasPacker( const AtlasPacker& imageAtlas);
+  AtlasPacker( const AtlasPacker& atlasPacker);
 
   // Undefined
-  AtlasPacker& operator=( const AtlasPacker& imageAtlas );
+  AtlasPacker& operator=( const AtlasPacker& atlasPacker );
 
 private:
 
index bdd2780..da8fe90 100644 (file)
@@ -34,6 +34,49 @@ namespace Toolkit
 namespace Internal
 {
 
+Texture ImageAtlas::PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects  )
+{
+  // Record each block size
+  Dali::Vector<Uint16Pair> blockSizes;
+  SizeType count = pixelData.size();
+  for( SizeType index = 0; index < count; index++ )
+  {
+    blockSizes.PushBack( ImageDimensions( pixelData[index].GetWidth(), pixelData[index].GetHeight() ) );
+  }
+
+  // Ask atlasPacker for packing position of each block
+  Dali::Vector<Uint16Pair> packPositions;
+  ImageDimensions atlasSize = AtlasPacker::GroupPack( blockSizes, packPositions );
+
+  // Prepare for outout texture rect array
+  textureRects.Clear();
+  textureRects.Resize( count );
+
+  // create the texture for uploading the multiple pixel data
+  Texture atlasTexture = Texture::New( Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888, atlasSize.GetWidth(), atlasSize.GetHeight() );
+
+  float atlasWidth = static_cast<float>( atlasTexture.GetWidth() );
+  float atlasHeight = static_cast<float>( atlasTexture.GetHeight() );
+  int packPositionX, packPositionY;
+  // Upload the pixel data one by one to its packing position, and record the texture rects
+  for( SizeType index = 0; index < count; index++ )
+  {
+    packPositionX = packPositions[index].GetX();
+    packPositionY = packPositions[index].GetY();
+    atlasTexture.Upload( pixelData[index], 0u, 0u,
+                         packPositionX, packPositionY,
+                         pixelData[index].GetWidth(), pixelData[index].GetHeight() );
+
+    // Apply the half pixel correction to avoid the color bleeding between neighbour blocks
+    textureRects[index].x = ( static_cast<float>( packPositionX ) +0.5f ) / atlasWidth; // left
+    textureRects[index].y = ( static_cast<float>( packPositionY ) +0.5f ) / atlasHeight; // right
+    textureRects[index].z = ( static_cast<float>( packPositionX + pixelData[index].GetWidth() )-0.5f ) / atlasWidth; // right
+    textureRects[index].w = ( static_cast<float>( packPositionY + pixelData[index].GetHeight() )-0.5f ) / atlasHeight;// bottom
+  }
+
+  return atlasTexture;
+}
+
 ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat )
 : mAtlas( Texture::New( Dali::TextureType::TEXTURE_2D, pixelFormat, width, height ) ),
   mPacker( width, height ),
index 4f735ef..2980627 100644 (file)
@@ -47,6 +47,11 @@ public:
   typedef Toolkit::ImageAtlas::SizeType SizeType;
 
   /**
+   * @copydoc ImageAtlas::PackToAtlas( const std::vector<PixelData>&, Dali::Vector<Vector4>& )
+   */
+  static Texture PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects  );
+
+  /**
    * Constructor
    * @param [in] width          The atlas width in pixels.
    * @param [in] height         The atlas height in pixels.
diff --git a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
new file mode 100644 (file)
index 0000000..5a3a154
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016 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 "animated-image-visual.h"
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/gif-loading.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
+#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/internal/visuals/visual-factory-impl.h>
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
+#include <dali-toolkit/internal/visuals/image/image-visual.h>
+#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const std::string& imageUrl, const Property::Map& properties )
+{
+  AnimatedImageVisual* visual = new AnimatedImageVisual( factoryCache );
+  visual->mImageUrl = imageUrl;
+  visual->SetProperties( properties );
+
+  return visual;
+}
+
+AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const std::string& imageUrl )
+{
+  AnimatedImageVisual* visual = new AnimatedImageVisual( factoryCache );
+  visual->mImageUrl = imageUrl;
+
+  return visual;
+}
+
+AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache )
+: Visual::Base( factoryCache ),
+  mFrameDelayTimer(),
+  mImageUrl(),
+  mImageSize(),
+  mCurrentFrameIndex( 0 )
+{}
+
+AnimatedImageVisual::~AnimatedImageVisual()
+{
+}
+
+void AnimatedImageVisual::GetNaturalSize( Vector2& naturalSize )
+{
+  if( mImageSize.GetWidth() == 0 &&  mImageSize.GetHeight() == 0)
+  {
+    mImageSize = Dali::GetGifImageSize( mImageUrl );
+  }
+
+  naturalSize.width = mImageSize.GetWidth();
+  naturalSize.height = mImageSize.GetHeight();
+}
+
+void AnimatedImageVisual::DoCreatePropertyMap( Property::Map& map ) const
+{
+  map.Clear();
+  map.Insert( Toolkit::DevelVisual::Property::TYPE, Toolkit::Visual::IMAGE );
+  if( !mImageUrl.empty() )
+  {
+    map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl );
+  }
+}
+
+void AnimatedImageVisual::DoSetProperties( const Property::Map& propertyMap )
+{
+  // url already passed in from constructor
+}
+
+void AnimatedImageVisual::DoSetOnStage( Actor& actor )
+{
+  Texture texture = PrepareAnimatedImage();
+  if( texture ) // if the image loading is successful
+  {
+    Shader shader = ImageVisual::GetImageShader( mFactoryCache, true, true );
+    Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
+    TextureSet textureSet = TextureSet::New();
+    mImpl->mRenderer = Renderer::New( geometry, shader );
+    mImpl->mRenderer.SetTextures( textureSet );
+    textureSet.SetTexture( 0u, PrepareAnimatedImage() );
+    mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mTextureRectContainer[0] );
+
+    // Register transform properties
+    mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
+
+    mCurrentFrameIndex = 0;
+    if( mFrameDelayContainer.Count() > 1 )
+    {
+      mFrameDelayTimer = Timer::New( mFrameDelayContainer[0] );
+      mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
+      mFrameDelayTimer.Start();
+    }
+
+    actor.AddRenderer( mImpl->mRenderer );
+  }
+}
+
+void AnimatedImageVisual::DoSetOffStage( Actor& actor )
+{
+  if( !mImpl->mRenderer )
+  {
+    return;
+  }
+
+  if( mFrameDelayTimer )
+  {
+    mFrameDelayTimer.Stop();
+    mFrameDelayTimer.Reset();
+  }
+
+  mTextureRectContainer.Clear();
+  mFrameDelayContainer.Clear();
+
+  actor.RemoveRenderer( mImpl->mRenderer );
+  mImpl->mRenderer.Reset();
+}
+
+void AnimatedImageVisual::OnSetTransform()
+{
+  if( mImpl->mRenderer )
+  {
+    mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
+  }
+}
+
+Texture AnimatedImageVisual::PrepareAnimatedImage()
+{
+  // load from image file
+  std::vector<Dali::PixelData> pixelDataList;
+  if( Dali::LoadAnimatedGifFromFile( mImageUrl.c_str() , pixelDataList, mFrameDelayContainer ) )
+  {
+    mImageSize.SetWidth( pixelDataList[0].GetWidth() );
+    mImageSize.SetHeight( pixelDataList[0].GetHeight() );
+
+    return Toolkit::ImageAtlas::PackToAtlas( pixelDataList, mTextureRectContainer );
+  }
+
+  return Texture();
+}
+
+bool AnimatedImageVisual::DisplayNextFrame()
+{
+  mCurrentFrameIndex = (mCurrentFrameIndex+1) % mFrameDelayContainer.Count();
+  mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mTextureRectContainer[mCurrentFrameIndex] );
+  if( mFrameDelayTimer.GetInterval() != mFrameDelayContainer[mCurrentFrameIndex] )
+  {
+    mFrameDelayTimer.SetInterval( mFrameDelayContainer[mCurrentFrameIndex] );
+  }
+
+  return true;
+}
+
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.h b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.h
new file mode 100644 (file)
index 0000000..15dff43
--- /dev/null
@@ -0,0 +1,156 @@
+#ifndef DALI_TOOLKIT_INTERNAL_ANIMATED_IMAGE_VISUAL_H
+#define DALI_TOOLKIT_INTERNAL_ANIMATED_IMAGE_VISUAL_H
+
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/math/vector4.h>
+#include <dali/public-api/adaptor-framework/timer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/visual-base-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class AnimatedImageVisual;
+typedef IntrusivePtr< AnimatedImageVisual > AnimatedImageVisualPtr;
+
+/**
+ * The visual which renders an animated image
+ *
+ * The following property is essential
+ *
+ * | %Property Name           | Type             |
+ * |--------------------------|------------------|
+ * | url                      | STRING           |
+ *
+ */
+
+class AnimatedImageVisual : public Visual::Base, public ConnectionTracker
+{
+
+public:
+
+  /**
+   * @brief Create the animated image Visual using the image URL.
+   *
+   * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+   * @param[in] imageUrl The URL to svg resource to use
+   * @param[in] properties A Property::Map containing settings for this visual
+   * @return A smart-pointer to the newly allocated visual.
+   */
+  static AnimatedImageVisualPtr New( VisualFactoryCache& factoryCache, const std::string& imageUrl, const Property::Map& properties );
+
+  /**
+   * @brief Create the animated image visual using the image URL.
+   *
+   * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+   * @param[in] imageUrl The URL to animated image resource to use
+   */
+  static AnimatedImageVisualPtr New( VisualFactoryCache& factoryCache, const std::string& imageUrl );
+
+public:  // from Visual
+
+  /**
+   * @copydoc Visual::Base::GetNaturalSize
+   */
+  virtual void GetNaturalSize( Vector2& naturalSize );
+
+  /**
+   * @copydoc Visual::Base::CreatePropertyMap
+   */
+  virtual void DoCreatePropertyMap( Property::Map& map ) const;
+
+protected:
+
+  /**
+   * @brief Constructor.
+   *
+   * @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+   */
+  AnimatedImageVisual( VisualFactoryCache& factoryCache );
+
+  /**
+   * @brief A reference counted object may only be deleted by calling Unreference().
+   */
+  virtual ~AnimatedImageVisual();
+
+  /**
+   * @copydoc Visual::Base::DoSetProperties
+   */
+  virtual void DoSetProperties( const Property::Map& propertyMap );
+
+  /**
+   * @copydoc Visual::Base::DoSetOnStage
+   */
+  virtual void DoSetOnStage( Actor& actor );
+
+  /**
+   * @copydoc Visual::Base::DoSetOffStage
+   */
+  virtual void DoSetOffStage( Actor& actor );
+
+  /**
+   * @copydoc Visual::Base::OnSetTransform
+   */
+  virtual void OnSetTransform();
+
+private:
+
+  /**
+   * Load the animated image and pack the frames into atlas.
+   * @return That atlas texture.
+   */
+  Texture PrepareAnimatedImage();
+
+  /**
+   * Display the next frame. It is called when the mFrameDelayTimes ticks.
+   */
+  bool DisplayNextFrame();
+
+  // Undefined
+  AnimatedImageVisual( const AnimatedImageVisual& animatedImageVisual );
+
+  // Undefined
+  AnimatedImageVisual& operator=( const AnimatedImageVisual& animatedImageVisual );
+
+private:
+
+  Timer mFrameDelayTimer;
+  Dali::Vector<Vector4> mTextureRectContainer;
+  Dali::Vector<uint32_t> mFrameDelayContainer;
+  std::string  mImageUrl;
+  ImageDimensions mImageSize;
+  uint32_t mCurrentFrameIndex;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+#endif /* DALI_TOOLKIT_INTERNAL_ANIMATED_IMAGE_VISUAL_H */
index 08dfd20..5bc3c6e 100644 (file)
@@ -131,11 +131,6 @@ void ColorVisual::OnSetTransform()
 void ColorVisual::InitializeRenderer()
 {
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-  if( !geometry )
-  {
-    geometry =  VisualFactoryCache::CreateQuadGeometry();
-    mFactoryCache.SaveGeometry( VisualFactoryCache::QUAD_GEOMETRY, geometry );
-  }
 
   Shader shader = mFactoryCache.GetShader( VisualFactoryCache::COLOR_SHADER );
   if( !shader )
index 80c899f..7826984 100644 (file)
@@ -322,11 +322,6 @@ void GradientVisual::DoCreatePropertyMap( Property::Map& map ) const
 void GradientVisual::InitializeRenderer()
 {
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-  if( !geometry )
-  {
-    geometry =  VisualFactoryCache::CreateQuadGeometry();
-    mFactoryCache.SaveGeometry( VisualFactoryCache::QUAD_GEOMETRY, geometry );
-  }
 
   Toolkit::GradientVisual::Units::Type gradientUnits = mGradient->GetGradientUnits();
   VisualFactoryCache::ShaderType shaderType = GetShaderType( mGradientType, gradientUnits );
index 69a1094..b61c23b 100644 (file)
@@ -185,11 +185,6 @@ Geometry CreateGeometry( VisualFactoryCache& factoryCache, ImageDimensions gridS
   if( gridSize == ImageDimensions( 1, 1 ) )
   {
     geometry = factoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-    if( !geometry )
-    {
-      geometry =  VisualFactoryCache::CreateQuadGeometry();
-      factoryCache.SaveGeometry( VisualFactoryCache::QUAD_GEOMETRY, geometry );
-    }
   }
   else
   {
index 5dbb80f..955b84c 100644 (file)
@@ -100,11 +100,6 @@ void SvgVisual::DoSetOnStage( Actor& actor )
 {
   Shader shader = ImageVisual::GetImageShader( mFactoryCache, true, true );
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-  if( !geometry )
-  {
-    geometry =  mFactoryCache.CreateQuadGeometry();
-    mFactoryCache.SaveGeometry( VisualFactoryCache::QUAD_GEOMETRY, geometry );
-  }
   TextureSet textureSet = TextureSet::New();
   mImpl->mRenderer = Renderer::New( geometry, shader );
   mImpl->mRenderer.SetTextures( textureSet );
index 2e7fa6d..4145bf7 100644 (file)
@@ -269,11 +269,6 @@ void TextVisual::DoSetOnStage( Actor& actor )
   mControl = actor;
 
   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-  if( !geometry )
-  {
-    geometry =  VisualFactoryCache::CreateQuadGeometry();
-    mFactoryCache.SaveGeometry( VisualFactoryCache::QUAD_GEOMETRY , geometry );
-  }
 
   Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP );
   mFactoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP, shader );
index bf6814d..05e32ff 100644 (file)
@@ -52,6 +52,11 @@ VisualFactoryCache::~VisualFactoryCache()
 
 Geometry VisualFactoryCache::GetGeometry( GeometryType type )
 {
+  if( !mGeometry[type] && type == QUAD_GEOMETRY )
+  {
+    mGeometry[type] = CreateQuadGeometry();
+  }
+
   return mGeometry[type];
 }
 
index 92cc53c..239f5c4 100644 (file)
@@ -40,6 +40,7 @@
 #include <dali-toolkit/internal/visuals/primitive/primitive-visual.h>
 #include <dali-toolkit/internal/visuals/svg/svg-visual.h>
 #include <dali-toolkit/internal/visuals/text/text-visual.h>
+#include <dali-toolkit/internal/visuals/animated-image/animated-image-visual.h>
 #include <dali-toolkit/internal/visuals/wireframe/wireframe-visual.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
 #include <dali-toolkit/internal/visuals/visual-factory-resolve-url.h>
@@ -151,6 +152,10 @@ Toolkit::Visual::Base VisualFactory::CreateVisual( const Property::Map& property
           {
             visualPtr = SvgVisual::New( *( mFactoryCache.Get() ), imageUrl, propertyMap );
           }
+          else if( UrlType::GIF == type )
+          {
+            visualPtr = AnimatedImageVisual::New( *( mFactoryCache.Get() ), imageUrl, propertyMap );
+          }
           else // Regular image
           {
             bool batchingEnabled( false );
@@ -259,6 +264,10 @@ Toolkit::Visual::Base VisualFactory::CreateVisual( const std::string& url, Image
   {
     visualPtr = SvgVisual::New( *( mFactoryCache.Get() ), url );
   }
+  else if( UrlType::GIF == type )
+  {
+    visualPtr = AnimatedImageVisual::New( *( mFactoryCache.Get() ), url );
+  }
   else // Regular image
   {
     visualPtr = ImageVisual::New( *( mFactoryCache.Get() ), url, size );
index 1d3ffe3..2c1c45b 100644 (file)
@@ -37,7 +37,8 @@ namespace UrlType
   {
     REGULAR_IMAGE,
     N_PATCH,
-    SVG
+    SVG,
+    GIF
   };
 }
 
@@ -55,7 +56,9 @@ inline UrlType::Type ResolveUrlType( const std::string& url )
     // parsing from the end for better chance of early outs
     enum { SUFFIX, HASH, HASH_DOT } state = SUFFIX;
     char SVG[ 4 ] = { 'g', 'v', 's', '.' };
+    char GIF[ 4 ] = { 'f', 'i', 'g', '.' };
     unsigned int svgScore = 0;
+    unsigned int gifScore = 0;
     int index = count;
     while( --index >= 0 )
     {
@@ -69,6 +72,14 @@ inline UrlType::Type ResolveUrlType( const std::string& url )
           return UrlType::SVG;
         }
       }
+      if( ( offsetFromEnd < sizeof(GIF) )&&( tolower( currentChar ) == GIF[ offsetFromEnd ] ) )
+      {
+        // early out if GIF
+        if( ++gifScore == sizeof(GIF) )
+        {
+          return UrlType::GIF;
+        }
+      }
       switch( state )
       {
         case SUFFIX:
diff --git a/docs/content/images/visuals/animated-image-visual.gif b/docs/content/images/visuals/animated-image-visual.gif
new file mode 100644 (file)
index 0000000..ddc3312
Binary files /dev/null and b/docs/content/images/visuals/animated-image-visual.gif differ
index 457d8e1..eaaaed5 100644 (file)
@@ -30,7 +30,7 @@ Visuals have a **transform** field in the property map to allow layouting within
 | Dali::Toolkit::Visual::Transform::Property::OFFSET | offset | VECTOR2 | No      | The offset of the visual. |
 | Dali::Toolkit::Visual::Transform::Property::SIZE | size | VECTOR2 | No      | The size of the visual. |
 | Dali::Toolkit::Visual::Transform::Property::OFFSET_SIZE_MODE | offsetSizeMode | VECTOR4 | No      | Whether the size or offset components are Relative or Absolute [More info](@ref offset-size-mode)|
-| Dali::Toolkit::Visual::Transform::Property::ORIGIN | origin | INTEGER or STRING | No      | The origin of the visual within the control's area. [More info](@ref align-type)] |
+| Dali::Toolkit::Visual::Transform::Property::ORIGIN | origin | INTEGER or STRING | No      | The origin of the visual within the control's area. [More info](@ref align-type) |
 | Dali::Toolkit::Visual::Transform::Property::ANCHOR_POINT | anchorPoint | INTEGER or STRING | No      | The anchor point of the visual. [More info](@ref align-type)|
  
 
@@ -282,6 +282,7 @@ Depending on the extension of the image, a different visual is provided to rende
  + [Normal (Quad)](@ref image-visual)
  + [N-Patch](@ref n-patch-visual)
  + [SVG](@ref svg-visual)
+ + [Animated Image]( @ref animated-image-visual )
  
 ___________________________
  
@@ -440,6 +441,45 @@ control.background =
   url : "path-to-image.svg"
 };
 ~~~
+
+___________________________________________________________________________________________________
+
+## Animated Image Visual {#animated-image-visual}
+
+Renders an animated image into the visual's quad geometry. Currently, only the GIF format is supported.
+
+![ ](../assets/img/visuals/animated-image-visual.gif)
+![ ](animated-image-visual.gif)
+
+#### Properties Supported
+
+**VisualType:** Dali::Toolkit::Visual::IMAGE, "IMAGE"
+
+| Property                                  | String | Type    | Required | Description                      |
+|-------------------------------------------|--------|:-------:|:--------:|----------------------------------|
+| Dali::Toolkit::ImageVisual::Property::URL | url    | STRING  | Yes      | The URL of the animated image.    |
+
+#### Usage
+
+~~~{.cpp}
+// C++
+Dali::Toolkit::Control control = Dali::Toolkit::Control::New();
+
+control.SetProperty( Control::Property::BACKGROUND,
+                     Property::Map().Add( Visual::Property::TYPE, Dali::Toolkit::Visual::IMAGE )
+                                    .Add( Dali::Toolkit::ImageVisual::Property::URL, "path-to-image.gif" ) );
+~~~
+
+~~~{.js}
+// JavaScript
+var control = new dali.Control( "Control" );
+
+control.background =
+{
+  visualType : "IMAGE",
+  url : "path-to-image.gif"
+};
+~~~
 ___________________________________________________________________________________________________
 
 ## Border Visual {#border-visual}