-//
-// 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.
-//
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
// CLASS HEADER
#include <dali/internal/event/images/nine-patch-image-impl.h>
+// EXTERNAL INCLUDES
+#include <cstring> // for memcmp
+
// INTERNAL INCLUDES
+#include <dali/public-api/object/type-registry.h>
#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
break;
}
+ case Dali::Pixel::INVALID:
case Dali::Pixel::COMPRESSED_R11_EAC:
case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC:
case Dali::Pixel::COMPRESSED_RG11_EAC:
case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC:
case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR:
+ case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
+ case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
{
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;
}
+
+ case Dali::Pixel::RGB16F:
+ case Dali::Pixel::RGB32F:
+ {
+ DALI_LOG_ERROR("Pixel format not compatible.\n");
+ byteOffset=0;
+ bitMask=0;
+ break;
+ }
}
}
namespace Internal
{
+namespace
+{
+TypeRegistration mType( typeid( Dali::NinePatchImage ), typeid( Dali::Image ), NULL );
+} // unnamed namespace
-NinePatchImagePtr NinePatchImage::New( const std::string& filename, const Dali::ImageAttributes& attributes, LoadPolicy loadPol, ReleasePolicy releasePol )
+NinePatchImagePtr NinePatchImage::New( const std::string& filename )
{
- Internal::NinePatchImagePtr internal( new NinePatchImage( filename, attributes, loadPol, releasePol ) );
+ Internal::NinePatchImagePtr internal( new NinePatchImage( filename ) );
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),
+NinePatchImage::NinePatchImage( const std::string& filename )
+: ResourceImage(),
mParsedBorder(false)
{
- Initialize();
-
- Integration::PlatformAbstraction& platformAbstraction = Internal::ThreadLocalStorage::Get().GetPlatformAbstraction();
+ mUrl = filename;
+ ThreadLocalStorage& tls = ThreadLocalStorage::Get();
- Vector2 closestSize;
- platformAbstraction.GetClosestImageSize( filename, attributes, closestSize );
- ImageAttributes loadedAttrs;
- loadedAttrs.SetSize( closestSize );
- mWidth = closestSize.width;
- mHeight = closestSize.height;
- Integration::BitmapResourceType resourceType( loadedAttrs );
+ Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
+ Integration::BitmapResourceType resourceType;
// Note, bitmap is only destroyed when the image is destroyed.
- Integration::ResourcePointer resource = platformAbstraction.LoadResourceSynchronously(resourceType, filename);
- mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
+ Integration::ResourcePointer resource = platformAbstraction.LoadImageSynchronously( resourceType, filename );
+ if( resource )
+ {
+ mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
+
+ mWidth = mBitmap->GetImageWidth();
+ mHeight = mBitmap->GetImageHeight();
+ mTexture = Texture::New( Dali::TextureType::TEXTURE_2D, mBitmap->GetPixelFormat(), mWidth, mHeight );
+
+ size_t bufferSize = mBitmap->GetBufferSize();
+ unsigned char* buffer = new unsigned char[bufferSize];
+ memcpy( buffer, mBitmap->GetBuffer(), bufferSize );
+ PixelDataPtr pixelData = PixelData::New( buffer, bufferSize, mWidth, mHeight, mBitmap->GetPixelFormat(), Dali::PixelData::DELETE_ARRAY );
+ mTexture->Upload( pixelData );
+ }
+ else
+ {
+ mBitmap.Reset();
+ mWidth = 0;
+ mHeight = 0;
+ }
}
-NinePatchImage* NinePatchImage::GetNinePatchImage( Image* image)
+NinePatchImage* NinePatchImage::DownCast( Image* image)
{
return dynamic_cast<NinePatchImage*>(image);
}
{
}
-Vector4 NinePatchImage::GetStretchBorders()
+const NinePatchImage::StretchRanges& NinePatchImage::GetStretchPixelsX()
+{
+ if( ! mParsedBorder )
+ {
+ ParseBorders();
+ }
+ return mStretchPixelsX;
+}
+
+const NinePatchImage::StretchRanges& NinePatchImage::GetStretchPixelsY()
{
if( ! mParsedBorder )
{
ParseBorders();
}
- return mStretchBorders;
+ return mStretchPixelsY;
}
Rect<int> NinePatchImage::GetChildRectangle()
return mChildRectangle;
}
-Internal::BitmapImagePtr NinePatchImage::CreateCroppedBitmapImage()
+Internal::BufferImagePtr NinePatchImage::CreateCroppedBufferImage()
{
- Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
-
- BitmapImagePtr cropped = BitmapImage::New( mWidth-2, mHeight-2, pixelFormat,
- Dali::Image::Immediate, Dali::Image::Never );
+ BufferImagePtr cropped;
- Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
- DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
-
- if( srcProfile )
+ if( ! mBitmap )
+ {
+ DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
+ }
+ else
{
- PixelBuffer* destPixels = cropped->GetBuffer();
- unsigned int destStride = cropped->GetBufferStride();
- unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
+ Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
- PixelBuffer* srcPixels = mBitmap->GetBuffer();
- unsigned int srcStride = srcProfile->GetBufferStride();
+ cropped = BufferImage::New( mWidth-2, mHeight-2, pixelFormat );
- for( unsigned int row=1; row < mHeight-1; ++row )
+ Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
+ DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
+
+ if( srcProfile )
{
- PixelBuffer* src = srcPixels + row*srcStride + pixelWidth;
- PixelBuffer* dest = destPixels + (row-1)*destStride;
- memcpy(dest, src, destStride );
+ 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
+ RectArea area;
+ cropped->Update(area); // default area has no width or height
+ }
return cropped;
}
-void NinePatchImage::Initialize()
+const std::string& NinePatchImage::GetUrl() const
{
- ThreadLocalStorage& tls = ThreadLocalStorage::Get();
- mResourceClient = &tls.GetResourceClient();
+ return mUrl;
}
-void NinePatchImage::Connect()
-{
- if( mConnectionCount == 0 && !mTicket )
- {
- const ImageTicketPtr& t = mResourceClient->AddBitmapImage(mBitmap.Get());
- mTicket = t.Get();
- mTicket->AddObserver(*this);
- }
-
- ++mConnectionCount;
-}
-
-void NinePatchImage::Disconnect()
+void NinePatchImage::ParseBorders()
{
- if( mConnectionCount > 0 )
+ if( !mBitmap )
{
- --mConnectionCount;
+ DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
+ return;
}
-}
+ mStretchPixelsX.Clear();
+ mStretchPixelsY.Clear();
-void NinePatchImage::ParseBorders()
-{
Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
- Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
- DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
+ const 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 alphaByte = 0;
+ int alphaBits = 0;
+ Pixel::GetAlphaOffsetAndMask( pixelFormat, alphaByte, alphaBits );
int testByte = alphaByte;
int testBits = alphaBits;
int testValue = alphaBits; // Opaque == stretch
if( ! alphaBits )
{
- testByte = redByte;
- testBits = redBits;
+ GetRedOffsetAndMask( pixelFormat, testByte, testBits );
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 )
+ unsigned int pixelWidth = GetBytesPerPixel( pixelFormat );
+ const PixelBuffer* srcPixels = mBitmap->GetBuffer();
+ unsigned int srcStride = srcProfile->GetBufferStride();
+
+ //TOP
+ const PixelBuffer* top = srcPixels + pixelWidth;
+ unsigned int index = 0;
+ unsigned int width = mBitmap->GetImageWidth();
+ unsigned int height = mBitmap->GetImageHeight();
+
+ for(; index < width - 2; )
{
- if( (top[testByte] & testBits) == testValue )
+ Uint16Pair range = ParseRange( index, width - 2, top, pixelWidth, testByte, testBits, testValue );
+ if( range.GetX() != 0xFFFF )
{
- if(startX1 < 0)
- {
- startX1 = col;
- }
- }
- else if(startX1 >= 0 && endX1 < 0)
- {
- endX1 = col-1;
- break;
+ mStretchPixelsX.PushBack( range );
}
+ }
- if( (bottom[testByte] & testBits) == testValue )
+ //LEFT
+ const PixelBuffer* left = srcPixels + srcStride;
+ index = 0;
+ for(; index < height - 2; )
+ {
+ Uint16Pair range = ParseRange( index, height - 2, left, srcStride, testByte, testBits, testValue );
+ if( range.GetX() != 0xFFFF )
{
- if(startX2 < 0)
- {
- startX2 = col;
- }
+ mStretchPixelsY.PushBack( range );
}
- else if(startX2 >= 0 && endX2 < 0)
- {
- endX2 = col-1;
+ }
+
+ //If there are no stretch pixels then make the entire image stretchable
+ if( mStretchPixelsX.Size() == 0 )
+ {
+ mStretchPixelsX.PushBack( Uint16Pair( 0, width - 2 ) );
+ }
+ if( mStretchPixelsY.Size() == 0 )
+ {
+ mStretchPixelsY.PushBack( Uint16Pair( 0, height - 2 ) );
+ }
+
+ //Child Rectangle
+ //BOTTOM
+ const PixelBuffer* bottom = srcPixels + ( height - 1 ) * srcStride + pixelWidth;
+ index = 0;
+ Uint16Pair contentRangeX = ParseRange( index, width - 2, bottom, pixelWidth, testByte, testBits, testValue );
+ if( contentRangeX.GetX() == 0xFFFF )
+ {
+ contentRangeX = Uint16Pair();
+ }
+
+ //RIGHT
+ const PixelBuffer* right = srcPixels + srcStride + ( width - 1 ) * pixelWidth;
+ index = 0;
+ Uint16Pair contentRangeY = ParseRange( index, height - 2, right, srcStride, testByte, testBits, testValue );
+ if( contentRangeY.GetX() == 0xFFFF )
+ {
+ contentRangeY = Uint16Pair();
+ }
+
+ mChildRectangle.x = contentRangeX.GetX() + 1;
+ mChildRectangle.y = contentRangeY.GetX() + 1;
+ mChildRectangle.width = contentRangeX.GetY() - contentRangeX.GetX();
+ mChildRectangle.height = contentRangeY.GetY() - contentRangeY.GetX();
+
+ mParsedBorder = true;
+ }
+}
+
+Uint16Pair NinePatchImage::ParseRange( unsigned int& index, unsigned int width, const PixelBuffer* & pixel, unsigned int pixelStride, int testByte, int testBits, int testValue )
+{
+ unsigned int start = 0xFFFF;
+ for( ; index < width; ++index, pixel += pixelStride )
+ {
+ if( ( pixel[ testByte ] & testBits ) == testValue )
+ {
+ start = index;
+ ++index;
+ pixel += pixelStride;
break;
- }
- top+=pixelWidth;
- bottom+=pixelWidth;
}
+ }
- // Read the left and right columns:
- PixelBuffer* left = srcPixels + srcStride;
- PixelBuffer* right = left + (srcStride - pixelWidth);
+ unsigned int end = width;
+ for( ; index < width; ++index, pixel += pixelStride )
+ {
+ if( ( pixel[ testByte ] & testBits ) != testValue )
+ {
+ end = index;
+ ++index;
+ pixel += pixelStride;
+ break;
+ }
+ }
+
+ return Uint16Pair( start, end );
+}
- // (Also read the last row to ensure end value gets set)
- for( unsigned int row=1; row < mHeight; ++row )
+bool NinePatchImage::IsNinePatchUrl( const std::string& url )
+{
+ bool match = false;
+
+ std::string::const_reverse_iterator iter = url.rbegin();
+ enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
+ while(iter < url.rend())
+ {
+ switch(state)
{
- if((left[testByte] & testBits) == testValue)
+ case SUFFIX:
{
- if(startY1 < 0)
+ if(*iter == '.')
{
- startY1 = row;
+ state = HASH;
+ }
+ else if(!isalnum(*iter))
+ {
+ state = DONE;
}
}
- else if(startY1 >= 0 && endY1 < 0)
+ break;
+ case HASH:
{
- endY1 = row-1;
- break;
+ if( *iter == '#' || *iter == '9' )
+ {
+ state = HASH_DOT;
+ }
+ else
+ {
+ state = DONE;
+ }
}
-
- if((right[testByte] & testBits) == testValue)
+ break;
+ case HASH_DOT:
{
- if(startY2 < 0)
+ if(*iter == '.')
{
- startY2 = row;
+ match = true;
}
+ state = DONE; // Stop testing characters
}
- else if(startY2 >= 0 && endY2 < 0)
+ break;
+ case DONE:
{
- endY2 = row-1;
- break;
}
- left += srcStride;
- right += srcStride;
+ break;
}
- 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;
+ // Satisfy prevent
+ if( state == DONE )
+ {
+ break;
+ }
- mParsedBorder = true;
+ ++iter;
}
+ return match;
}
} // namespace Internal