X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fvisuals%2Fnpatch-loader.cpp;h=e89ebcb22c2714c4fe77367c3aaf1094f828c81c;hp=6e3c8ec04de64b76930233570bb709bd07ee212f;hb=b8061231d4161501d25747516d09ced92ee71bee;hpb=a2e3740073b4cb92846a68831ad5bb35b12ac69a diff --git a/dali-toolkit/internal/visuals/npatch-loader.cpp b/dali-toolkit/internal/visuals/npatch-loader.cpp index 6e3c8ec..e89ebcb 100644 --- a/dali-toolkit/internal/visuals/npatch-loader.cpp +++ b/dali-toolkit/internal/visuals/npatch-loader.cpp @@ -19,8 +19,10 @@ #include // EXTERNAL HEADER +#include +#include #include -#include +#include namespace Dali { @@ -31,6 +33,223 @@ namespace Toolkit namespace Internal { +namespace NPatchBuffer +{ + +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::INVALID: + 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: + 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; + } + } +} + +Uint16Pair ParseRange( unsigned int& index, unsigned int width, unsigned char*& 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; + } + } + + 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 ); +} + +void ParseBorders( Devel::PixelBuffer& pixelBuffer, NPatchLoader::Data* data ) +{ + data->stretchPixelsX.Clear(); + data->stretchPixelsY.Clear(); + + Pixel::Format pixelFormat = pixelBuffer.GetPixelFormat(); + + int alphaByte = 0; + int alphaBits = 0; + Pixel::GetAlphaOffsetAndMask( pixelFormat, alphaByte, alphaBits ); + + int testByte = alphaByte; + int testBits = alphaBits; + int testValue = alphaBits; // Opaque == stretch + if( !alphaBits ) + { + GetRedOffsetAndMask( pixelFormat, testByte, testBits ); + testValue = 0; // Black == stretch + } + + unsigned int bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat ); + unsigned int width = pixelBuffer.GetWidth(); + unsigned int height = pixelBuffer.GetHeight(); + unsigned char* srcPixels = pixelBuffer.GetBuffer(); + unsigned int srcStride = width * bytesPerPixel; + + // TOP + unsigned char* top = srcPixels + bytesPerPixel; + unsigned int index = 0; + + for( ; index < width - 2; ) + { + Uint16Pair range = ParseRange( index, width - 2, top, bytesPerPixel, testByte, testBits, testValue ); + if( range.GetX() != 0xFFFF ) + { + data->stretchPixelsX.PushBack( range ); + } + } + + // LEFT + unsigned char* left = srcPixels + srcStride; + index = 0; + for( ; index < height - 2; ) + { + Uint16Pair range = ParseRange( index, height - 2, left, srcStride, testByte, testBits, testValue ); + if( range.GetX() != 0xFFFF ) + { + data->stretchPixelsY.PushBack( range ); + } + } + + // If there are no stretch pixels then make the entire image stretchable + if( data->stretchPixelsX.Size() == 0 ) + { + data->stretchPixelsX.PushBack( Uint16Pair( 0, width - 2 ) ); + } + if( data->stretchPixelsY.Size() == 0 ) + { + data->stretchPixelsY.PushBack( Uint16Pair( 0, height - 2 ) ); + } +} + +} // namespace NPatchBuffer + NPatchLoader::NPatchLoader() { } @@ -39,11 +258,13 @@ NPatchLoader::~NPatchLoader() { } -std::size_t NPatchLoader::Load( const std::string& url ) +std::size_t NPatchLoader::Load( const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad ) { std::size_t hash = CalculateHash( url ); OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID; const OwnerContainer< Data* >::SizeType count = mCache.Count(); + int cachedIndex = -1; + for( ; index < count; ++index ) { if( mCache[ index ]->hash == hash ) @@ -51,31 +272,94 @@ std::size_t NPatchLoader::Load( const std::string& url ) // hash match, check url as well in case of hash collision if( mCache[ index ]->url == url ) { - return index+1u; // valid indices are from 1 onwards + // Use cached data + if( mCache[ index ]->border == border ) + { + return index+1u; // valid indices are from 1 onwards + } + else + { + cachedIndex = index; + } } } } + + if( cachedIndex != -1 ) + { + // Same url but border is different - use the existing texture + Data* data = new Data(); + data->hash = hash; + data->url = url; + data->croppedWidth = mCache[ cachedIndex ]->croppedWidth; + data->croppedHeight = mCache[ cachedIndex ]->croppedHeight; + + data->textureSet = mCache[ cachedIndex ]->textureSet; + + StretchRanges stretchRangesX; + stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) ); + + StretchRanges stretchRangesY; + stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) ); + + data->stretchPixelsX = stretchRangesX; + data->stretchPixelsY = stretchRangesY; + data->border = border; + + mCache.PushBack( data ); + + return mCache.Count(); // valid ids start from 1u + } + // got to the end so no match, decode N patch and append new item to cache - NinePatchImage ninePatch = NinePatchImage::New( url ); - if( ninePatch ) + Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromFile( url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true ); + if( pixelBuffer ) { - BufferImage croppedImage = ninePatch.CreateCroppedBufferImage(); - if( croppedImage ) - { - Data* data = new Data(); - data->hash = hash; - data->url = url; - data->textureSet = TextureSet::New(); - TextureSetImage( data->textureSet, 0u, croppedImage ); - data->croppedWidth = croppedImage.GetWidth(); - data->croppedHeight = croppedImage.GetHeight(); - data->stretchPixelsX = ninePatch.GetStretchPixelsX(); - data->stretchPixelsY = ninePatch.GetStretchPixelsY(); - mCache.PushBack( data ); - - return mCache.Count(); // valid ids start from 1u + Data* data = new Data(); + data->hash = hash; + data->url = url; + + if( border == Rect< int >( 0, 0, 0, 0 ) ) + { + NPatchBuffer::ParseBorders( pixelBuffer, data ); + + data->border = Rect< int >( 0, 0, 0, 0 ); + + // Crop the image + pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 ); + } + else + { + data->stretchPixelsX.PushBack( Uint16Pair( border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( border.right )) ? pixelBuffer.GetWidth() - border.right : 0 ) ) ); + data->stretchPixelsY.PushBack( Uint16Pair( border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( border.bottom )) ? pixelBuffer.GetHeight() - border.bottom : 0 ) ) ); + data->border = border; } + + data->croppedWidth = pixelBuffer.GetWidth(); + data->croppedHeight = pixelBuffer.GetHeight(); + + if( preMultiplyOnLoad && Pixel::HasAlpha( pixelBuffer.GetPixelFormat() ) ) + { + pixelBuffer.MultiplyColorByAlpha(); + } + else + { + preMultiplyOnLoad = false; + } + + PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer + + Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() ); + texture.Upload( pixels ); + + data->textureSet = TextureSet::New(); + data->textureSet.SetTexture( 0u, texture ); + + mCache.PushBack( data ); + + return mCache.Count(); // valid ids start from 1u } + return 0u; }