From 3cc0f2d0472ec32adb2cf40a4c00dafbda04641c Mon Sep 17 00:00:00 2001 From: David Steele Date: Thu, 3 Apr 2014 18:38:11 +0100 Subject: [PATCH] (Image) Add support for 9-patch images [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 --- .../test-platform-abstraction.h | 8 + dali/integration-api/platform-abstraction.h | 12 + dali/integration-api/resource-cache.h | 2 - dali/internal/event/actors/image-actor-impl.cpp | 31 +- dali/internal/event/actors/image-actor-impl.h | 7 +- dali/internal/event/images/bitmap-image-impl.cpp | 13 +- dali/internal/event/images/bitmap-image-impl.h | 31 +- dali/internal/event/images/image-impl.cpp | 102 ++++-- dali/internal/event/images/image-impl.h | 8 + .../event/images/nine-patch-image-impl.cpp | 373 +++++++++++++++++++++ dali/internal/event/images/nine-patch-image-impl.h | 172 ++++++++++ dali/internal/file.list | 1 + .../render/common/texture-cache-dispatcher.h | 6 +- dali/public-api/dali-core.h | 1 + dali/public-api/file.list | 4 +- dali/public-api/images/nine-patch-image.cpp | 72 ++++ dali/public-api/images/nine-patch-image.h | 119 +++++++ dali/public-api/images/pixel.cpp | 8 +- 18 files changed, 907 insertions(+), 63 deletions(-) create mode 100644 dali/internal/event/images/nine-patch-image-impl.cpp create mode 100644 dali/internal/event/images/nine-patch-image-impl.h create mode 100644 dali/public-api/images/nine-patch-image.cpp create mode 100644 dali/public-api/images/nine-patch-image.h diff --git a/automated-tests/dali-test-suite-utils/test-platform-abstraction.h b/automated-tests/dali-test-suite-utils/test-platform-abstraction.h index 6ae2627..be0a170 100644 --- a/automated-tests/dali-test-suite-utils/test-platform-abstraction.h +++ b/automated-tests/dali-test-suite-utils/test-platform-abstraction.h @@ -26,6 +26,8 @@ #include #include +#include +#include #include 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() */ diff --git a/dali/integration-api/platform-abstraction.h b/dali/integration-api/platform-abstraction.h index ed05512..dc57708 100644 --- a/dali/integration-api/platform-abstraction.h +++ b/dali/integration-api/platform-abstraction.h @@ -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. */ diff --git a/dali/integration-api/resource-cache.h b/dali/integration-api/resource-cache.h index 5c5b5cc..ac07845 100644 --- a/dali/integration-api/resource-cache.h +++ b/dali/integration-api/resource-cache.h @@ -27,8 +27,6 @@ namespace Dali namespace Integration { -typedef IntrusivePtr ResourcePointer; - /** * Used to determine why a resource IO operation has failed. */ diff --git a/dali/internal/event/actors/image-actor-impl.cpp b/dali/internal/event/actors/image-actor-impl.cpp index 406a6de..6cec2f2 100644 --- a/dali/internal/event/actors/image-actor-impl.cpp +++ b/dali/internal/event/actors/image-actor-impl.cpp @@ -18,6 +18,7 @@ #include // INTERNAL INCLUDES +#include #include #include #include @@ -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 diff --git a/dali/internal/event/actors/image-actor-impl.h b/dali/internal/event/actors/image-actor-impl.h index 3488e52..370752a 100644 --- a/dali/internal/event/actors/image-actor-impl.h +++ b/dali/internal/event/actors/image-actor-impl.h @@ -24,6 +24,7 @@ #include #include #include +#include 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. diff --git a/dali/internal/event/images/bitmap-image-impl.cpp b/dali/internal/event/images/bitmap-image-impl.cpp index eab6083..e8cf9b7 100644 --- a/dali/internal/event/images/bitmap-image-impl.cpp +++ b/dali/internal/event/images/bitmap-image-impl.cpp @@ -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(); } diff --git a/dali/internal/event/images/bitmap-image-impl.h b/dali/internal/event/images/bitmap-image-impl.h index 7bbd52a..4d35241 100644 --- a/dali/internal/event/images/bitmap-image-impl.h +++ b/dali/internal/event/images/bitmap-image-impl.h @@ -35,10 +35,6 @@ typedef IntrusivePtr 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; }; diff --git a/dali/internal/event/images/image-impl.cpp b/dali/internal/event/images/image-impl.cpp index e61e84e..8186ac2 100644 --- a/dali/internal/event/images/image-impl.cpp +++ b/dali/internal/event/images/image-impl.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include 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 diff --git a/dali/internal/event/images/image-impl.h b/dali/internal/event/images/image-impl.h index 23d614e..8fa94a0 100644 --- a/dali/internal/event/images/image-impl.h +++ b/dali/internal/event/images/image-impl.h @@ -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 index 0000000..c655760 --- /dev/null +++ b/dali/internal/event/images/nine-patch-image-impl.cpp @@ -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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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( resource.Get()); +} + +NinePatchImage* NinePatchImage::GetNinePatchImage( Image* image) +{ + return dynamic_cast(image); +} + +NinePatchImage::~NinePatchImage() +{ +} + +Vector4 NinePatchImage::GetStretchBorders() +{ + if( ! mParsedBorder ) + { + ParseBorders(); + } + return mStretchBorders; +} + +Rect 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 index 0000000..ea757a1 --- /dev/null +++ b/dali/internal/event/images/nine-patch-image-impl.h @@ -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 +#include + +namespace Dali +{ + +namespace Internal +{ + +class NinePatchImage; +typedef IntrusivePtr 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 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 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(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(image); +} + +} // namespace Dali + +#endif // __DALI_INTERNAL_NINE_PATCH_IMAGE_H__ diff --git a/dali/internal/file.list b/dali/internal/file.list index 401b790..088abc4 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -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 \ diff --git a/dali/internal/render/common/texture-cache-dispatcher.h b/dali/internal/render/common/texture-cache-dispatcher.h index 271895a..af6ebe3 100644 --- a/dali/internal/render/common/texture-cache-dispatcher.h +++ b/dali/internal/render/common/texture-cache-dispatcher.h @@ -28,15 +28,11 @@ #include #include #include +#include namespace Dali { -namespace Integration -{ -class Bitmap; -} - namespace Internal { typedef Integration::ResourceId ResourceId; diff --git a/dali/public-api/dali-core.h b/dali/public-api/dali-core.h index 24792e1..d6f8ae2 100644 --- a/dali/public-api/dali-core.h +++ b/dali/public-api/dali-core.h @@ -45,6 +45,7 @@ #include #include +#include #include #include diff --git a/dali/public-api/file.list b/dali/public-api/file.list index 1e8d8c9..9f5ee60 100644 --- a/dali/public-api/file.list +++ b/dali/public-api/file.list @@ -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 index 0000000..1093847 --- /dev/null +++ b/dali/public-api/images/nine-patch-image.cpp @@ -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 + +// INTERNAL INCLUDES +#include +#include +#include + + +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(handle.GetObjectPtr()) ); +} + +Vector4 NinePatchImage::GetStretchBorders() +{ + return GetImplementation(*this).GetStretchBorders(); +} + +Rect 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 index 0000000..b247755 --- /dev/null +++ b/dali/public-api/images/nine-patch-image.h @@ -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 +#include +#include +#include + +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 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__ diff --git a/dali/public-api/images/pixel.cpp b/dali/public-api/images/pixel.cpp index e1b50b3..7500a37 100644 --- a/dali/public-api/images/pixel.cpp +++ b/dali/public-api/images/pixel.cpp @@ -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: -- 2.7.4