(Image) Add support for 9-patch images 34/19434/1
authorDavid Steele <david.steele@partner.samsung.com>
Thu, 3 Apr 2014 17:38:11 +0000 (18:38 +0100)
committerVictor Cebollada <v.cebollada@samsung.com>
Fri, 11 Apr 2014 14:19:49 +0000 (15:19 +0100)
[Issue#]       N/A
[Problem]      There is no support for 9 patch images with the
stretch / child info information stored in a 1 pixel border within
the image
[Cause]        N/A
[Solution]     Added new class NinePatchImage, which is automatically
used by Image::New when a 9 patch filename is seen.

Adding a 9 patch image to an ImageActor via an Image handle will generate
an alternative BitmapImage with the border cropped out. It will set up the
ImageActor to use the 9 patch style and set the stretch areas up appropriately.

Adding a 9 patch image to an ImageActor via a NinePatchImage handle will
use the image as is (i.e. without the border cropped out).

[Verification] Build Repo

Change-Id: I8fa5cc34220ea9abcf085c600279ef92bc12da00
Signed-off-by: David Steele <david.steele@partner.samsung.com>
18 files changed:
automated-tests/dali-test-suite-utils/test-platform-abstraction.h
dali/integration-api/platform-abstraction.h
dali/integration-api/resource-cache.h
dali/internal/event/actors/image-actor-impl.cpp
dali/internal/event/actors/image-actor-impl.h
dali/internal/event/images/bitmap-image-impl.cpp
dali/internal/event/images/bitmap-image-impl.h
dali/internal/event/images/image-impl.cpp
dali/internal/event/images/image-impl.h
dali/internal/event/images/nine-patch-image-impl.cpp [new file with mode: 0644]
dali/internal/event/images/nine-patch-image-impl.h [new file with mode: 0644]
dali/internal/file.list
dali/internal/render/common/texture-cache-dispatcher.h
dali/public-api/dali-core.h
dali/public-api/file.list
dali/public-api/images/nine-patch-image.cpp [new file with mode: 0644]
dali/public-api/images/nine-patch-image.h [new file with mode: 0644]
dali/public-api/images/pixel.cpp

index 6ae2627..be0a170 100644 (file)
@@ -26,6 +26,8 @@
 #include <dali/integration-api/platform-abstraction.h>
 
 #include <dali/integration-api/glyph-set.h>
+#include <dali/integration-api/resource-cache.h>
+#include <dali/integration-api/resource-types.h>
 #include <test-trace-call-stack.h>
 
 namespace Dali
@@ -144,6 +146,12 @@ public:
     mRequest = new Integration::ResourceRequest(request);
   }
 
+  virtual Integration::ResourcePointer LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath)
+  {
+    mTrace.PushCall("LoadResourceSynchronously", "");
+    return mResources.loadedResource;
+  }
+
   /**
    * @copydoc PlatformAbstraction::SaveResource()
    */
index ed05512..dc57708 100644 (file)
@@ -114,6 +114,18 @@ public:
   virtual void LoadResource(const ResourceRequest& request) = 0;
 
   /**
+   * Request a resource from the native filesystem. This is a synchronous request, i.e.
+   * it will block the main loop whilst executing. It should therefore be used sparingly.
+   *
+   * Multi-threading note: this method will be called from the main thread only i.e. not
+   * from within the Core::Render() method.
+   * @param[in] resourceType The type of resource to load
+   * @param[in] resourcePath The path to the resource
+   * @return A pointer to a ref-counted resource
+   */
+  virtual ResourcePointer LoadResourceSynchronously( const ResourceType& resourceType, const std::string& resourcePath ) = 0;
+
+  /**
    * Request that a resource be saved to the native filesystem.
    * This is an asynchronous request.
    */
index 5c5b5cc..ac07845 100644 (file)
@@ -27,8 +27,6 @@ namespace Dali
 
 namespace Integration
 {
-typedef IntrusivePtr<Dali::RefObject> ResourcePointer;
-
 /**
  * Used to determine why a resource IO operation has failed.
  */
index 406a6de..6cec2f2 100644 (file)
@@ -18,6 +18,7 @@
 #include <dali/internal/event/actors/image-actor-impl.h>
 
 // INTERNAL INCLUDES
+#include <dali/internal/event/images/nine-patch-image-impl.h>
 #include <dali/public-api/object/type-registry.h>
 #include <dali/internal/event/common/property-index-ranges.h>
 #include <dali/internal/event/images/image-connector.h>
@@ -94,28 +95,46 @@ std::string StyleString(const ImageActor::Style style)
 }
 }
 
-ImageActorPtr ImageActor::New( Image* image )
+ImageActorPtr ImageActor::New( Image* anImage )
 {
   ImageActorPtr actor( new ImageActor() );
+  ImagePtr theImage( anImage );
 
   // Second-phase construction
   actor->Initialize();
+  NinePatchImage* ninePatchImage = NULL;
+
+  // Automatically convert upcasted nine-patch images to cropped bitmap
+  ninePatchImage = NinePatchImage::GetNinePatchImage( anImage );
+
+  if( ninePatchImage != NULL )
+  {
+    theImage = ninePatchImage->CreateCroppedBitmapImage();
+  }
 
   // Create the attachment
-  actor->mImageAttachment = ImageAttachment::New( *actor->mNode, image );
+  actor->mImageAttachment = ImageAttachment::New( *actor->mNode, theImage.Get() );
   actor->Attach( *actor->mImageAttachment );
 
   // Adjust the actor's size
-  if( image )
+  if( theImage )
   {
-    actor->mImageNext.Set( image, false );
-    actor->OnImageSet( *image );
-    actor->SetNaturalSize( *image );
+    actor->mImageNext.Set( theImage.Get(), false );
+    actor->OnImageSet( *theImage );
+    actor->SetNaturalSize( *theImage );
+  }
+
+  if( ninePatchImage != NULL )
+  {
+    actor->SetStyle( Dali::ImageActor::STYLE_NINE_PATCH );
+    Vector4 border = ninePatchImage->GetStretchBorders();
+    actor->SetNinePatchBorder( border, true );
   }
 
   return actor;
 }
 
+
 ImageActorPtr ImageActor::New( Image* image, const PixelArea& pixelArea )
 {
   // re-use basic New
index 3488e52..370752a 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/internal/event/actors/renderable-actor-impl.h>
 #include <dali/internal/event/actor-attachments/image-attachment-impl.h>
 #include <dali/internal/event/animation/animation-impl.h>
+#include <dali/internal/event/images/nine-patch-image-impl.h>
 
 namespace Dali
 {
@@ -54,16 +55,16 @@ public:
   typedef Dali::ImageActor::PixelArea PixelArea;
 
   /**
-   * Create an initialised image actor.
+   * @brief Create an initialised image actor.
    * When the image is loaded the actors size will reset to the image size,
    * unless a custom size chosen via Actor:SetSize().
-   * @param  [in] image A pointer to the image object to display or NULL not to display anything.
+   * @param[in] image A pointer to the image object to display or NULL not to display anything.
    * @return A smart-pointer to a newly allocated image actor.
    */
   static ImageActorPtr New( Image* image );
 
   /**
-   * Create an initialised image actor
+   * @brief Create an initialised image actor.
    * When the image is loaded the actors size will reset to the image size,
    * unless a custom size chosen via Actor:SetSize().
    * @param [in] image A pointer to the image object to display or NULL not to display anything.
index eab6083..e8cf9b7 100644 (file)
@@ -30,19 +30,17 @@ namespace Dali
 namespace Internal
 {
 
-
-BitmapImagePtr BitmapImage::New( unsigned int width, unsigned int height, Pixel::Format pixelformat, LoadPolicy loadPol, ReleasePolicy releasePol )
+BitmapImage* BitmapImage::New( unsigned int width, unsigned int height, Pixel::Format pixelformat, LoadPolicy loadPol, ReleasePolicy releasePol )
 {
-  Internal::BitmapImagePtr internal( new BitmapImage( width, height, pixelformat, loadPol, releasePol ) );
+  BitmapImage* internal = new BitmapImage( width, height, pixelformat, loadPol, releasePol );
   internal->Initialize();
-
   return internal;
 }
 
-BitmapImagePtr BitmapImage::New( PixelBuffer* pixBuf, unsigned int width, unsigned int height, Pixel::Format pixelformat, unsigned int stride, ReleasePolicy releasePol )
+BitmapImage* BitmapImage::New( PixelBuffer* pixBuf, unsigned int width, unsigned int height, Pixel::Format pixelformat, unsigned int stride, ReleasePolicy releasePol )
 {
-  Internal::BitmapImagePtr internal( new BitmapImage( pixBuf, width, height, pixelformat, stride, releasePol ) );
-
+  BitmapImage* internal = new BitmapImage( pixBuf, width, height, pixelformat, stride, releasePol );
+  internal->Initialize();
   return internal;
 }
 
@@ -151,7 +149,6 @@ unsigned int BitmapImage::GetBufferStride() const
 void BitmapImage::Initialize()
 {
   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
-  mUpdateManager   = &tls.GetUpdateManager();
   mResourceClient = &tls.GetResourceClient();
 }
 
index 7bbd52a..4d35241 100644 (file)
@@ -35,10 +35,6 @@ typedef IntrusivePtr<BitmapImage> BitmapImagePtr;
 class ResourceClient;
 class ResourceManager;
 
-namespace SceneGraph
-{
-class UpdateManager;
-}
 /**
  * BitmapImage represents an image resource that can be added to actors etc.
  * Its pixel buffer data is provided by the application developer.
@@ -59,11 +55,11 @@ public:
    * @param [in] loadPol     controls time of loading a resource from the filesystem (default: load when Image is created).
    * @param [in] releasePol  optionally relase memory when image is not visible on screen (default: keep image data until Image object is alive).
    */
-  static BitmapImagePtr New(unsigned int width,
-                            unsigned int height,
-                            Pixel::Format pixelformat,
-                            LoadPolicy loadPol=ImageLoadPolicyDefault,
-                            ReleasePolicy releasePol=ImageReleasePolicyDefault);
+  static BitmapImage* New( unsigned int width,
+                           unsigned int height,
+                           Pixel::Format pixelformat,
+                           LoadPolicy loadPol=ImageLoadPolicyDefault,
+                           ReleasePolicy releasePol=ImageReleasePolicyDefault);
 
   /**
    * Create a new BitmapImage, which uses external data source.
@@ -79,13 +75,12 @@ public:
    * @param [in] stride      the internal stride of the pixelbuffer in pixels
    * @param [in] releasePol  optionally relase memory when image is not visible on screen (default: keep image data until Image object is alive).
    */
-  static BitmapImagePtr New(PixelBuffer* pixBuf,
-                            unsigned int width,
-                            unsigned int height,
-                            Pixel::Format pixelformat,
-                            unsigned int stride,
-                            ReleasePolicy releasePol=ImageReleasePolicyDefault);
-
+  static BitmapImage* New( PixelBuffer* pixBuf,
+                           unsigned int width,
+                           unsigned int height,
+                           Pixel::Format pixelformat,
+                           unsigned int stride,
+                           ReleasePolicy releasePol=ImageReleasePolicyDefault );
 
   /**
    * Create a new BitmapImage.
@@ -188,11 +183,11 @@ protected: // From Resource
   Integration::Bitmap * GetBitmap() const;
 
 private:
-
   bool mIsDataExternal; ///< whether application holds ownership of pixel buffer or not
 
   ResourceClient*            mResourceClient;
-  SceneGraph::UpdateManager* mUpdateManager;
+
+protected:
   Integration::BitmapPtr     mBitmapCached;
 };
 
index e61e84e..8186ac2 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali/internal/event/common/thread-local-storage.h>
 #include <dali/internal/event/resources/resource-client.h>
 #include <dali/internal/event/images/image-factory.h>
+#include <dali/internal/event/images/nine-patch-image-impl.h>
 #include <dali/internal/event/common/stage-impl.h>
 
 using namespace Dali::Integration;
@@ -53,31 +54,39 @@ Image* Image::New()
 
 Image* Image::New( const std::string& filename, const Dali::ImageAttributes& attributes, LoadPolicy loadPol, ReleasePolicy releasePol )
 {
-  Image* image = new Image( loadPol, releasePol );
-
-  if( ! filename.empty() )
+  if( IsNinePatchFileName(filename) )
   {
-    Vector2 closestSize;
-
-    Internal::ThreadLocalStorage::Get().GetPlatformAbstraction().GetClosestImageSize( filename, attributes, closestSize );
-    image->mWidth = closestSize.width;
-    image->mHeight = closestSize.height;
+    NinePatchImage* image = new NinePatchImage( filename, attributes, loadPol, releasePol );
+    return image;
   }
+  else
+  {
+    Image* image = new Image( loadPol, releasePol );
 
-  image->mRequest = image->mImageFactory.RegisterRequest( filename, &attributes );
+    if( ! filename.empty() )
+    {
+      Vector2 closestSize;
 
-  if( Dali::Image::Immediate == loadPol )
-  {
-    // Trigger loading of the image on a seperate resource thread as soon as it
-    // can be scheduled:
-    image->mTicket = image->mImageFactory.Load( image->mRequest.Get() );
-    image->mTicket->AddObserver( *image );
-  }
-  // else lazily load image data later, only when it is needed to draw something:
+      Internal::ThreadLocalStorage::Get().GetPlatformAbstraction().GetClosestImageSize( filename, attributes, closestSize );
+      image->mWidth = closestSize.width;
+      image->mHeight = closestSize.height;
+    }
 
-  DALI_LOG_SET_OBJECT_STRING( image, filename );
+    image->mRequest = image->mImageFactory.RegisterRequest( filename, &attributes );
 
-  return image;
+    if( Dali::Image::Immediate == loadPol )
+    {
+      // Trigger loading of the image on a seperate resource thread as soon as it
+      // can be scheduled:
+      image->mTicket = image->mImageFactory.Load( image->mRequest.Get() );
+      image->mTicket->AddObserver( *image );
+    }
+    // else lazily load image data later, only when it is needed to draw something:
+
+    DALI_LOG_SET_OBJECT_STRING( image, filename );
+
+    return image;
+  }
 }
 
 Image* Image::New( NativeImage& nativeImg, LoadPolicy loadPol, ReleasePolicy releasePol )
@@ -290,6 +299,61 @@ void Image::SetTicket( ResourceTicket* ticket )
   }
 }
 
+bool Image::IsNinePatchFileName( std::string filename )
+{
+  bool match = false;
+
+  std::string::const_iterator iter = filename.end();
+  iter--;
+  enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
+  while(iter >= filename.begin() && state != DONE)
+  {
+    switch(state)
+    {
+      case SUFFIX:
+      {
+        if(*iter == '.')
+        {
+          state = HASH;
+        }
+        else if(!isalnum(*iter))
+        {
+          state = DONE;
+        }
+      }
+      break;
+      case HASH:
+      {
+        if( *iter == '#' || *iter == '9' )
+        {
+          state = HASH_DOT;
+        }
+        else
+        {
+          state = DONE;
+        }
+      }
+      break;
+      case HASH_DOT:
+      {
+        if(*iter == '.')
+        {
+          state = DONE;
+          match = true;
+        }
+      }
+      break;
+      case DONE:
+      {
+      }
+      break;
+    }
+    iter--;
+  }
+  return match;
+}
+
+
 } // namespace Internal
 
 } // namespace Dali
index 23d614e..8fa94a0 100644 (file)
@@ -243,6 +243,14 @@ private:
    */
   void SetTicket( ResourceTicket* ticket );
 
+  /**
+   * Helper method to determine if the filename indicates that the image has a 9 patch border.
+   * @param[in] filename The filename to check
+   * @return true if it is a 9 patch image
+   */
+  static bool IsNinePatchFileName( std::string filename );
+
+
 protected:
   unsigned int mWidth;
   unsigned int mHeight;
diff --git a/dali/internal/event/images/nine-patch-image-impl.cpp b/dali/internal/event/images/nine-patch-image-impl.cpp
new file mode 100644 (file)
index 0000000..c655760
--- /dev/null
@@ -0,0 +1,373 @@
+//
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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/internal/event/images/nine-patch-image-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/bitmap.h>
+#include <dali/internal/event/images/bitmap-external.h>
+#include <dali/internal/event/common/thread-local-storage.h>
+#include <dali/internal/event/resources/resource-client.h>
+#include <dali/internal/update/manager/update-manager.h>
+#include <dali/internal/event/images/image-factory.h>
+#include <dali/integration-api/platform-abstraction.h>
+#include <dali/integration-api/resource-types.h>
+#include <dali/integration-api/resource-cache.h>
+
+
+namespace
+{
+void GetRedOffsetAndMask(Dali::Pixel::Format pixelFormat, int& byteOffset, int& bitMask)
+{
+  switch (pixelFormat)
+  {
+    case Dali::Pixel::A8:
+    case Dali::Pixel::L8:
+    case Dali::Pixel::LA88:
+    {
+      byteOffset=0;
+      bitMask=0;
+      break;
+    }
+
+    case Dali::Pixel::RGB888:
+    case Dali::Pixel::RGB8888:
+    case Dali::Pixel::RGBA8888:
+    {
+      byteOffset=0;
+      bitMask=0xFF;
+      break;
+    }
+    case Dali::Pixel::BGR8888:
+    case Dali::Pixel::BGRA8888:
+    {
+      byteOffset=2;
+      bitMask=0xff;
+      break;
+    }
+    case Dali::Pixel::RGB565:
+    {
+      byteOffset=0;
+      bitMask=0xf8;
+      break;
+    }
+    case Dali::Pixel::BGR565:
+    {
+      byteOffset=1;
+      bitMask=0x1f;
+      break;
+    }
+
+    case Dali::Pixel::RGBA4444:
+    {
+      byteOffset=0;
+      bitMask=0xf0;
+      break;
+    }
+    case Dali::Pixel::BGRA4444:
+    {
+      byteOffset=1;
+      bitMask=0xf0;
+      break;
+    }
+
+    case Dali::Pixel::RGBA5551:
+    {
+      byteOffset=0;
+      bitMask=0xf8;
+      break;
+    }
+
+    case Dali::Pixel::BGRA5551:
+    {
+      byteOffset=1;
+      bitMask=0x1e;
+      break;
+    }
+
+    case Dali::Pixel::COMPRESSED_R11_EAC:
+    case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC:
+    case Dali::Pixel::COMPRESSED_RG11_EAC:
+    case Dali::Pixel::COMPRESSED_SIGNED_RG11_EAC:
+    case Dali::Pixel::COMPRESSED_RGB8_ETC2:
+    case Dali::Pixel::COMPRESSED_SRGB8_ETC2:
+    case Dali::Pixel::COMPRESSED_RGB8_ETC1:
+    case Dali::Pixel::COMPRESSED_RGB_PVRTC_4BPPV1:
+    case Dali::Pixel::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+    case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC:
+    case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+    {
+      DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
+      byteOffset=0;
+      bitMask=0;
+      break;
+    }
+  }
+}
+
+} // anonymous namespace
+
+
+namespace Dali
+{
+namespace Internal
+{
+
+
+NinePatchImagePtr NinePatchImage::New( const std::string& filename, const Dali::ImageAttributes& attributes, LoadPolicy loadPol, ReleasePolicy releasePol )
+{
+  Internal::NinePatchImagePtr internal( new NinePatchImage( filename, attributes, loadPol, releasePol ) );
+  internal->Initialize();
+  return internal;
+}
+
+NinePatchImage::NinePatchImage( const std::string& filename, const Dali::ImageAttributes& attributes, LoadPolicy loadPol, ReleasePolicy releasePol)
+: Image(Dali::Image::Immediate, Dali::Image::Never),
+  mParsedBorder(false)
+{
+  Initialize();
+
+  Integration::PlatformAbstraction& platformAbstraction = Internal::ThreadLocalStorage::Get().GetPlatformAbstraction();
+
+  Vector2 closestSize;
+  platformAbstraction.GetClosestImageSize( filename, attributes, closestSize );
+  ImageAttributes loadedAttrs;
+  loadedAttrs.SetSize( closestSize );
+  mWidth = closestSize.width;
+  mHeight = closestSize.height;
+  Integration::BitmapResourceType resourceType( loadedAttrs );
+
+  // Note, bitmap is only destroyed when the image is destroyed.
+  Integration::ResourcePointer resource = platformAbstraction.LoadResourceSynchronously(resourceType, filename);
+  mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
+}
+
+NinePatchImage* NinePatchImage::GetNinePatchImage( Image* image)
+{
+  return dynamic_cast<NinePatchImage*>(image);
+}
+
+NinePatchImage::~NinePatchImage()
+{
+}
+
+Vector4 NinePatchImage::GetStretchBorders()
+{
+  if( ! mParsedBorder )
+  {
+    ParseBorders();
+  }
+  return mStretchBorders;
+}
+
+Rect<int> NinePatchImage::GetChildRectangle()
+{
+  if( ! mParsedBorder )
+  {
+    ParseBorders();
+  }
+  return mChildRectangle;
+}
+
+Internal::BitmapImagePtr NinePatchImage::CreateCroppedBitmapImage()
+{
+  Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
+
+  BitmapImagePtr cropped = BitmapImage::New( mWidth-2, mHeight-2, pixelFormat,
+                                             Dali::Image::Immediate, Dali::Image::Never );
+
+  Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
+  DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
+
+  if( srcProfile )
+  {
+    PixelBuffer* destPixels = cropped->GetBuffer();
+    unsigned int destStride = cropped->GetBufferStride();
+    unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
+
+    PixelBuffer* srcPixels = mBitmap->GetBuffer();
+    unsigned int srcStride = srcProfile->GetBufferStride();
+
+    for( unsigned int row=1; row < mHeight-1; ++row )
+    {
+      PixelBuffer* src  = srcPixels + row*srcStride + pixelWidth;
+      PixelBuffer* dest = destPixels + (row-1)*destStride;
+      memcpy(dest, src, destStride );
+    }
+  }
+
+  RectArea area;
+  cropped->Update(area); // default area has no width or height
+  return cropped;
+}
+
+void NinePatchImage::Initialize()
+{
+  ThreadLocalStorage& tls = ThreadLocalStorage::Get();
+  mResourceClient = &tls.GetResourceClient();
+}
+
+void NinePatchImage::Connect()
+{
+  if( mConnectionCount == 0 && !mTicket )
+  {
+    const ImageTicketPtr& t = mResourceClient->AddBitmapImage(mBitmap.Get());
+    mTicket = t.Get();
+    mTicket->AddObserver(*this);
+  }
+
+  ++mConnectionCount;
+}
+
+void NinePatchImage::Disconnect()
+{
+  if( mConnectionCount > 0 )
+  {
+    --mConnectionCount;
+  }
+}
+
+
+void NinePatchImage::ParseBorders()
+{
+  Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
+
+  Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
+  DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
+
+  if( srcProfile )
+  {
+    unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
+    PixelBuffer* srcPixels = mBitmap->GetBuffer();
+    unsigned int srcStride = srcProfile->GetBufferStride();
+
+    int alphaByte=0;
+    int alphaBits=0;
+    Pixel::GetAlphaOffsetAndMask(pixelFormat, alphaByte, alphaBits);
+    int redByte=0;
+    int redBits=0;
+    GetRedOffsetAndMask(pixelFormat, redByte, redBits);
+
+    int testByte = alphaByte;
+    int testBits = alphaBits;
+    int testValue = alphaBits; // Opaque == stretch
+    if( ! alphaBits )
+    {
+      testByte = redByte;
+      testBits = redBits;
+      testValue = 0;           // Black == stretch
+    }
+
+    int startX1=-1;
+    int endX1=-1;
+    int startY1=-1;
+    int endY1=-1;
+    int startX2=-1;
+    int endX2=-1;
+    int startY2=-1;
+    int endY2=-1;
+
+    PixelBuffer* top = srcPixels + pixelWidth;
+    PixelBuffer* bottom = srcPixels + (mHeight-1)*srcStride + pixelWidth;
+
+    // Read the top and bottom rows:
+    // (Also read the last column to ensure end value gets set)
+    for( unsigned int col=1; col < mWidth; ++col )
+    {
+      if( (top[testByte] & testBits) == testValue )
+      {
+        if(startX1 < 0)
+        {
+          startX1 = col;
+        }
+      }
+      else if(startX1 >= 0 && endX1 < 0)
+      {
+        endX1 = col-1;
+        break;
+      }
+
+      if( (bottom[testByte] & testBits) == testValue )
+      {
+        if(startX2 < 0)
+        {
+          startX2 = col;
+        }
+      }
+      else if(startX2 >= 0 && endX2 < 0)
+      {
+        endX2 = col-1;
+        break;
+      }
+      top+=pixelWidth;
+      bottom+=pixelWidth;
+    }
+
+    // Read the left and right columns:
+    PixelBuffer* left  = srcPixels + srcStride;
+    PixelBuffer* right = left + (srcStride - pixelWidth);
+
+    // (Also read the last row to ensure end value gets set)
+    for( unsigned int row=1; row < mHeight; ++row )
+    {
+      if((left[testByte] & testBits) == testValue)
+      {
+        if(startY1 < 0)
+        {
+          startY1 = row;
+        }
+      }
+      else if(startY1 >= 0 && endY1 < 0)
+      {
+        endY1 = row-1;
+        break;
+      }
+
+      if((right[testByte] & testBits) == testValue)
+      {
+        if(startY2 < 0)
+        {
+          startY2 = row;
+        }
+      }
+      else if(startY2 >= 0 && endY2 < 0)
+      {
+        endY2 = row-1;
+        break;
+      }
+      left += srcStride;
+      right += srcStride;
+    }
+
+    mStretchBorders.x = startX1-1;
+    mStretchBorders.y = startY1-1;
+    mStretchBorders.z = mWidth-endX1-1;
+    mStretchBorders.w = mHeight-endY1-1;
+
+    mChildRectangle.x = startX2-1;
+    mChildRectangle.y = startY2-1;
+    mChildRectangle.width = endX2-startX2;
+    mChildRectangle.height = endY2-startY2;
+
+    mParsedBorder = true;
+  }
+}
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/dali/internal/event/images/nine-patch-image-impl.h b/dali/internal/event/images/nine-patch-image-impl.h
new file mode 100644 (file)
index 0000000..ea757a1
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef __DALI_INTERNAL_NINE_PATCH_IMAGE_H__
+#define __DALI_INTERNAL_NINE_PATCH_IMAGE_H__
+
+//
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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/public-api/images/nine-patch-image.h>
+#include <dali/internal/event/images/bitmap-image-impl.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+class NinePatchImage;
+typedef IntrusivePtr<NinePatchImage> NinePatchImagePtr;
+
+class ResourceClient;
+class ResourceManager;
+
+namespace SceneGraph
+{
+class UpdateManager;
+}
+
+/**
+ * NinePatchImage represents an image resource that can be added to actors etc.
+ * It's image data has a border which determines stretch and fill areas
+ * Its pixel buffer data is loaded synchronously from file.
+ */
+class NinePatchImage : public Image
+{
+public:
+
+  /**
+   * Create a new NinePatchImage.
+   * Also a pixel buffer for image data is allocated.
+   * Dali has ownership of the buffer.
+   * @param [in] filename    File to load synchronously into buffer
+   * @param [in] attributes  Image attributes of the file
+   * @param [in] loadPol     controls time of loading a resource from the filesystem (default: load when Image is created).
+   * @param [in] releasePol  optionally relase memory when image is not visible on screen (default: keep image data until Image object is alive).
+   */
+  static NinePatchImagePtr New( const std::string& filename,
+                                const ImageAttributes& attributes,
+                                LoadPolicy    loadPol    = ImageLoadPolicyDefault,
+                                ReleasePolicy releasePol = ImageReleasePolicyDefault );
+
+  /**
+   * Create a new NinePatchImage
+   * For better performance and portability use power of two dimensions.
+   * The maximum size of the image is limited by GL_MAX_TEXTURE_SIZE.
+   * @param [in] filename    File to load synchronously into buffer
+   * @param [in] attributes  Image attributes of the file
+   * @param [in] loadPol     controls time of loading a resource from the filesystem (default: load when Image is created).
+   * @param [in] releasePol  optionally relase memory when image is not visible on screen (default: keep image data until Image object is alive).
+   */
+  NinePatchImage( const std::string& filename,
+                  const ImageAttributes& attributes,
+                  LoadPolicy    loadPol    = ImageLoadPolicyDefault,
+                  ReleasePolicy releasePol = ImageReleasePolicyDefault );
+
+  /**
+   * Convert Image object to a 9 patch image object if possible.
+   * @param[in] image The image to convert
+   * @return A pointer to the 9 patch image object, or NULL
+   * if the conversion is not possible.
+   */
+  static NinePatchImage* GetNinePatchImage( Image* image);
+
+
+protected:
+  /**
+   * A reference counted object may only be deleted by calling Unreference()
+   */
+  virtual ~NinePatchImage();
+
+public:
+  /**
+   * Get the stretch borders
+   * @return The border in pixels from the left, top, right, and bottom of the image respectively.
+   */
+  Vector4 GetStretchBorders();
+
+  /**
+   * Get the child rectangle
+   * @return the position and size of the child rectangle
+   */
+  Rect<int> GetChildRectangle();
+
+  /**
+   * @brief Create a cropped image from the bitmap with the 1 pixel border cropped off.
+   * This does not change the internal bitmap.
+   *
+   * @return the cropped bitmap.
+   */
+  BitmapImagePtr CreateCroppedBitmapImage();
+
+private:
+  /**
+   * Initializes internal data.
+   */
+  void Initialize();
+
+protected: // From Resource
+  /**
+   * @copydoc Dali::Internal::Image::Connect
+   */
+  virtual void Connect();
+
+  /**
+   * @copydoc Dali::Internal::Image::Disconnect
+   */
+  virtual void Disconnect();
+
+private:
+  /**
+   * Read the borders of the bitmap and determine the child area
+   * and stretch borders
+   */
+  void ParseBorders();
+
+private:
+  ResourceClient*               mResourceClient;
+  Integration::BitmapPtr        mBitmap;
+  SceneGraph::UpdateManager*    mUpdateManager;
+  Vector4                       mStretchBorders;
+  Rect<int>                     mChildRectangle;
+  bool                          mParsedBorder;
+};
+
+} // namespace Internal
+
+/**
+ * Helper methods for public API.
+ */
+inline Internal::NinePatchImage& GetImplementation(Dali::NinePatchImage& handle)
+{
+  DALI_ASSERT_ALWAYS( handle && "NinePatchImage handle is empty" );
+
+  BaseObject& image = handle.GetBaseObject();
+
+  return static_cast<Internal::NinePatchImage&>(image);
+}
+
+inline const Internal::NinePatchImage& GetImplementation(const Dali::NinePatchImage& handle)
+{
+  DALI_ASSERT_ALWAYS( handle && "NinePatchImage handle is empty" );
+
+  const BaseObject& image = handle.GetBaseObject();
+
+  return static_cast<const Internal::NinePatchImage&>(image);
+}
+
+} // namespace Dali
+
+#endif // __DALI_INTERNAL_NINE_PATCH_IMAGE_H__
index 401b790..088abc4 100644 (file)
@@ -86,6 +86,7 @@ internal_src_files = \
   $(internal_src_dir)/event/images/image-connector.cpp \
   $(internal_src_dir)/event/images/image-factory.cpp \
   $(internal_src_dir)/event/images/image-factory-cache.cpp \
+  $(internal_src_dir)/event/images/nine-patch-image-impl.cpp \
   $(internal_src_dir)/event/modeling/animatable-mesh-impl.cpp \
   $(internal_src_dir)/event/modeling/entity-impl.cpp \
   $(internal_src_dir)/event/modeling/light-impl.cpp \
index 271895a..af6ebe3 100644 (file)
 #include <dali/internal/update/common/scene-graph-buffers.h>
 #include <dali/internal/common/bitmap-upload.h>
 #include <dali/integration-api/resource-declarations.h>
+#include <dali/integration-api/bitmap.h>
 
 namespace Dali
 {
 
-namespace Integration
-{
-class Bitmap;
-}
-
 namespace Internal
 {
 typedef Integration::ResourceId ResourceId;
index 24792e1..d6f8ae2 100644 (file)
@@ -45,6 +45,7 @@
 #include <dali/public-api/geometry/spline.h>
 
 #include <dali/public-api/images/distance-field.h>
+#include <dali/public-api/images/nine-patch-image.h>
 
 #include <dali/public-api/modeling/bone.h>
 #include <dali/public-api/modeling/entity-animator-map.h>
index 1e8d8c9..9f5ee60 100644 (file)
@@ -62,6 +62,7 @@ public_api_src_files = \
   $(public_api_src_dir)/images/bitmap-image.cpp \
   $(public_api_src_dir)/images/frame-buffer-image.cpp \
   $(public_api_src_dir)/images/encoded-buffer-image.cpp \
+  $(public_api_src_dir)/images/nine-patch-image.cpp \
   $(public_api_src_dir)/math/angle-axis.cpp \
   $(public_api_src_dir)/math/compile-time-math.cpp \
   $(public_api_src_dir)/math/degree.cpp \
@@ -150,7 +151,8 @@ public_api_core_geometry_header_files = \
   $(public_api_src_dir)/geometry/spline.h
 
 public_api_core_images_header_files = \
-  $(public_api_src_dir)/images/distance-field.h
+  $(public_api_src_dir)/images/distance-field.h \
+  $(public_api_src_dir)/images/nine-patch-image.h
 
 public_api_core_math_header_files =
 
diff --git a/dali/public-api/images/nine-patch-image.cpp b/dali/public-api/images/nine-patch-image.cpp
new file mode 100644 (file)
index 0000000..1093847
--- /dev/null
@@ -0,0 +1,72 @@
+//
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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/public-api/images/nine-patch-image.h>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/images/image-attributes.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/internal/event/images/nine-patch-image-impl.h>
+
+
+namespace Dali
+{
+
+NinePatchImage::NinePatchImage()
+{
+}
+
+NinePatchImage::NinePatchImage(Internal::NinePatchImage* internal)
+: Image(internal)
+{
+}
+
+NinePatchImage::~NinePatchImage()
+{
+}
+
+NinePatchImage NinePatchImage::New( const std::string& filename )
+{
+  ImageAttributes defaultAttrs;
+
+  Internal::NinePatchImagePtr internal = Internal::NinePatchImage::New( filename, defaultAttrs, Image::Immediate, Image::Never );
+  return NinePatchImage(internal.Get());
+}
+
+NinePatchImage NinePatchImage::DownCast( BaseHandle handle )
+{
+  return NinePatchImage( dynamic_cast<Dali::Internal::NinePatchImage*>(handle.GetObjectPtr()) );
+}
+
+Vector4 NinePatchImage::GetStretchBorders()
+{
+  return GetImplementation(*this).GetStretchBorders();
+}
+
+Rect<int> NinePatchImage::GetChildRectangle()
+{
+  return GetImplementation(*this).GetChildRectangle();
+}
+
+BitmapImage NinePatchImage::CreateCroppedBitmapImage()
+{
+  Internal::BitmapImagePtr internal = GetImplementation(*this).CreateCroppedBitmapImage();
+  return BitmapImage(internal.Get());
+}
+
+
+} // namespace Dali
diff --git a/dali/public-api/images/nine-patch-image.h b/dali/public-api/images/nine-patch-image.h
new file mode 100644 (file)
index 0000000..b247755
--- /dev/null
@@ -0,0 +1,119 @@
+#ifndef __DALI_NINE_PATCH_IMAGE_H__
+#define __DALI_NINE_PATCH_IMAGE_H__
+
+//
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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/public-api/common/vector-wrapper.h>
+#include <dali/public-api/images/image.h>
+#include <dali/public-api/images/bitmap-image.h>
+#include <dali/public-api/math/rect.h>
+
+namespace Dali DALI_IMPORT_API
+{
+
+namespace Internal DALI_INTERNAL
+{
+class NinePatchImage;
+}
+
+
+/**
+ * @brief NinePatchImage represents an image resource that can be added to ImageActors.
+ * It contains a bitmap that is synchronously loaded from the file system that contains
+ * a 9 patch border - a 1 pixel border that describes the stretch borders and the child
+ * area.
+ *
+ * The class offers an API to read the stretch area and child area, but it does not
+ * remove the border from it's bitmap. An API can be used to obtain a BitmapImage with
+ * the border removed.
+ *
+ * Adding this image to an ImageActor using an Image handle will automatically convert
+ * to use the cropped BitmapImage - if you don't retain a handle to this object, it will
+ * be automatically destroyed.
+ */
+class NinePatchImage : public Image
+{
+public:
+  /**
+   * @brief Constructor which creates an uninitialized NinePatchImage object.
+   *
+   * Use Image::New(...) to create an initialised object.
+   */
+  NinePatchImage();
+
+  /**
+   * @brief Create a new NinePatchImage.
+   *
+   * A pixel buffer for image data is allocated and loaded from the filesystem.
+   * Dali has ownership of the buffer.
+   * @note: default resource management policies are Immediate and Never
+   *
+   * @param [in] filename    File to load synchronously into buffer
+   * @return a handle to a new instance of NinePatchImage
+   */
+  static NinePatchImage New( const std::string& filename );
+
+  /**
+   * @brief Downcast an Object handle to NinePatchImage.
+   *
+   * If handle points to a NinePatchImage the downcast produces valid
+   * handle. If not the returned handle is left uninitialized.
+   *
+   * @param[in] handle to An object
+   * @return handle to a NinePatchImage or an uninitialized handle
+   */
+  static NinePatchImage DownCast( BaseHandle handle );
+
+  /**
+   * @brief Destructor.
+   */
+  virtual ~NinePatchImage();
+
+  /**
+   * @copydoc Dali::BaseHandle::operator=
+   */
+  using BaseHandle::operator=;
+
+  /**
+   * Get the stretch borders
+   * @return The border in pixels from the left, top, right, and bottom of the image respectively.
+   */
+  Vector4 GetStretchBorders();
+
+  /**
+   * Get the child rectangle
+   * @return the position and size of the child rectangle
+   */
+  Rect<int> GetChildRectangle();
+
+  /**
+   * Creates a bitmap image from the bitmap with the 1 pixel border cropped off.
+   * This does not change the internal bitmap.
+   *
+   * @return The cropped BitmapImage
+   */
+  BitmapImage CreateCroppedBitmapImage();
+
+public: // Not intended for application developers
+
+  explicit DALI_INTERNAL NinePatchImage(Internal::NinePatchImage*);
+};
+
+} // namespace Dali
+
+#endif // __DALI_NINE_PATCH_IMAGE_H__
index e1b50b3..7500a37 100644 (file)
@@ -125,8 +125,14 @@ void Pixel::GetAlphaOffsetAndMask(Format pixelFormat, int& byteOffset, int& bitM
 {
   switch (pixelFormat)
   {
-    case L8:
     case A8:
+    {
+      byteOffset = 0;
+      bitMask    = 0xFF;
+    }
+    break;
+
+    case L8:
     case RGB888:
     case RGB565:
     case RGB8888: