X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fvisuals%2Fnpatch%2Fnpatch-visual.cpp;h=c8b601a4e61771c78b2693a61bc350805347b805;hp=4cf199a4e08ce21c3de0dc819d10d69f01190966;hb=071b25e591f0fff83d85087e3963fbe6387b7551;hpb=831bd867847c32ac74b56c08757b0bed74f73238 diff --git a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp old mode 100644 new mode 100755 index 4cf199a..c8b601a --- a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp +++ b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 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. @@ -19,21 +19,24 @@ #include "npatch-visual.h" // EXTERNAL INCLUDES -#include #include #include +#include #include +#include +#include // INTERNAL INCLUDES #include -#include +#include +#include +#include #include #include #include #include #include - namespace Dali { @@ -45,18 +48,17 @@ namespace Internal namespace { -const char * const BORDER_ONLY("borderOnly"); - const char* VERTEX_SHADER = DALI_COMPOSE_SHADER( attribute mediump vec2 aPosition;\n varying mediump vec2 vTexCoord;\n - uniform mediump mat4 uMvpMatrix;\n + varying mediump vec2 vMaskTexCoord;\n + uniform highp mat4 uMvpMatrix;\n uniform mediump vec3 uSize;\n uniform mediump vec2 uNinePatchFactorsX[ FACTOR_SIZE_X ];\n uniform mediump vec2 uNinePatchFactorsY[ FACTOR_SIZE_Y ];\n \n - //Visual size and offset + // Visual size and offset uniform mediump vec2 offset;\n uniform mediump vec2 size;\n uniform mediump vec4 offsetSizeMode;\n @@ -71,16 +73,17 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER( mediump vec2 fixedTotal = vec2( uNinePatchFactorsX[ FACTOR_SIZE_X - 1 ].x, uNinePatchFactorsY[ FACTOR_SIZE_Y - 1 ].x );\n mediump vec2 stretchTotal = vec2( uNinePatchFactorsX[ FACTOR_SIZE_X - 1 ].y, uNinePatchFactorsY[ FACTOR_SIZE_Y - 1 ].y );\n \n - vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n - - mediump vec4 vertexPosition = vec4( ( fixedFactor + ( visualSize.xy - fixedTotal ) * stretch / stretchTotal ) + anchorPoint*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n + \n + mediump vec4 gridPosition = vec4( fixedFactor + ( visualSize.xy - fixedTotal ) * stretch / stretchTotal, 0.0, 1.0 );\n + mediump vec4 vertexPosition = gridPosition;\n vertexPosition.xy -= visualSize.xy * vec2( 0.5, 0.5 );\n - + vertexPosition.xy += anchorPoint*visualSize + (visualOffset + origin)*uSize.xy;\n vertexPosition = uMvpMatrix * vertexPosition;\n \n vTexCoord = ( fixedFactor + stretch ) / ( fixedTotal + stretchTotal );\n + vMaskTexCoord = gridPosition.xy / visualSize;\n \n gl_Position = vertexPosition;\n }\n @@ -89,40 +92,40 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER( const char* VERTEX_SHADER_3X3 = DALI_COMPOSE_SHADER( attribute mediump vec2 aPosition;\n varying mediump vec2 vTexCoord;\n - uniform mediump mat4 uModelMatrix;\n - uniform mediump mat4 uMvpMatrix;\n + varying mediump vec2 vMaskTexCoord;\n + uniform highp mat4 uMvpMatrix;\n uniform mediump vec3 uSize;\n uniform mediump vec2 uFixed[ 3 ];\n uniform mediump vec2 uStretchTotal;\n \n - //Visual size and offset uniform mediump vec2 offset;\n uniform mediump vec2 size;\n uniform mediump vec4 offsetSizeMode;\n uniform mediump vec2 origin;\n uniform mediump vec2 anchorPoint;\n - + \n void main()\n {\n vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n - - mediump vec2 scale = vec2( length( uModelMatrix[ 0 ].xyz ), length( uModelMatrix[ 1 ].xyz ) );\n - mediump vec2 size = visualSize.xy * scale;\n + \n + mediump vec2 size = visualSize.xy;\n \n mediump vec2 fixedFactor = vec2( uFixed[ int( ( aPosition.x + 1.0 ) * 0.5 ) ].x, uFixed[ int( ( aPosition.y + 1.0 ) * 0.5 ) ].y );\n mediump vec2 stretch = floor( aPosition * 0.5 );\n mediump vec2 fixedTotal = uFixed[ 2 ];\n \n - mediump vec4 vertexPosition = vec4( fixedFactor + ( size - fixedTotal ) * stretch, 0.0, 1.0 ); + mediump vec4 gridPosition = vec4( fixedFactor + ( size - fixedTotal ) * stretch, 0.0, 1.0 );\n + mediump vec4 vertexPosition = gridPosition;\n vertexPosition.xy -= size * vec2( 0.5, 0.5 );\n - vertexPosition.xy = vertexPosition.xy / scale + anchorPoint*size + (visualOffset + origin)*uSize.xy;\ + vertexPosition.xy += anchorPoint*size + (visualOffset + origin)*uSize.xy;\n \n vertexPosition = uMvpMatrix * vertexPosition;\n \n vTexCoord = ( fixedFactor + stretch * uStretchTotal ) / ( fixedTotal + uStretchTotal );\n \n + vMaskTexCoord = gridPosition.xy / size;\n gl_Position = vertexPosition;\n }\n ); @@ -131,10 +134,38 @@ const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER( varying mediump vec2 vTexCoord;\n uniform sampler2D sTexture;\n uniform lowp vec4 uColor;\n + uniform lowp vec3 mixColor;\n + uniform lowp float preMultipliedAlpha;\n + \n + void main()\n + {\n + gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor * vec4( mixColor, 1.0 );\n + }\n +); + +const char* FRAGMENT_MASK_SHADER = DALI_COMPOSE_SHADER( + varying mediump vec2 vTexCoord;\n + varying mediump vec2 vMaskTexCoord;\n + uniform sampler2D sTexture;\n + uniform sampler2D sMask;\n + uniform lowp vec4 uColor;\n + uniform lowp vec3 mixColor;\n + uniform lowp float preMultipliedAlpha;\n + uniform mediump float auxiliaryImageAlpha;\n \n void main()\n {\n - gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n + // Where mask image is transparent, all of background image must show through. + // where mask image is opaque, only mask should be shown + // where mask is translucent, less of background should be shown. + // auxiliaryImageAlpha controls how much of mask is visible + + mediump vec4 color = texture2D( sTexture, vTexCoord );\n + mediump vec4 mask = texture2D( sMask, vMaskTexCoord );\n + + mediump vec3 mixedColor = color.rgb * mix( 1.0-mask.a, 1.0, 1.0-auxiliaryImageAlpha) + + mask.rgb*mask.a * auxiliaryImageAlpha;\n + gl_FragColor = vec4(mixedColor,1.0) * uColor * vec4( mixColor, 1.0 );\n }\n ); @@ -190,13 +221,13 @@ void AddVertex( Vector< Vector2 >& vertices, unsigned int x, unsigned int y ) vertices.PushBack( Vector2( x, y ) ); } -void RegisterStretchProperties( Renderer& renderer, const char * uniformName, const NinePatchImage::StretchRanges& stretchPixels, uint16_t imageExtent) +void RegisterStretchProperties( Renderer& renderer, const char * uniformName, const NPatchLoader::StretchRanges& stretchPixels, uint16_t imageExtent) { uint16_t prevEnd = 0; uint16_t prevFix = 0; uint16_t prevStretch = 0; unsigned int i = 1; - for( NinePatchImage::StretchRanges::ConstIterator it = stretchPixels.Begin(); it != stretchPixels.End(); ++it, ++i ) + for( NPatchLoader::StretchRanges::ConstIterator it = stretchPixels.Begin(); it != stretchPixels.End(); ++it, ++i ) { uint16_t start = it->GetX(); uint16_t end = it->GetY(); @@ -225,348 +256,361 @@ void RegisterStretchProperties( Renderer& renderer, const char * uniformName, co /////////////////NPatchVisual//////////////// -NPatchVisualPtr NPatchVisual::New( VisualFactoryCache& factoryCache ) +NPatchVisualPtr NPatchVisual::New( VisualFactoryCache& factoryCache, const VisualUrl& imageUrl, const Property::Map& properties ) { - return new NPatchVisual( factoryCache ); -} - -NPatchVisualPtr NPatchVisual::New( VisualFactoryCache& factoryCache, const std::string& imageUrl, bool borderOnly ) -{ - NPatchVisual* nPatchVisual = new NPatchVisual( factoryCache, borderOnly ); + NPatchVisualPtr nPatchVisual( new NPatchVisual( factoryCache ) ); nPatchVisual->mImageUrl = imageUrl; - - NinePatchImage image = NinePatchImage::New( imageUrl ); - nPatchVisual->InitializeFromImage( image ); + nPatchVisual->SetProperties( properties ); return nPatchVisual; } -NPatchVisualPtr NPatchVisual::New( VisualFactoryCache& factoryCache, NinePatchImage image, bool borderOnly ) +NPatchVisualPtr NPatchVisual::New( VisualFactoryCache& factoryCache, const VisualUrl& imageUrl ) { - NPatchVisual* nPatchVisual = new NPatchVisual( factoryCache, borderOnly ); - nPatchVisual->mImage = image; - - nPatchVisual->InitializeFromImage( image ); + NPatchVisualPtr nPatchVisual( new NPatchVisual( factoryCache ) ); + nPatchVisual->mImageUrl = imageUrl; return nPatchVisual; } -NPatchVisual::NPatchVisual( VisualFactoryCache& factoryCache, bool borderOnly ) -: Visual::Base( factoryCache ), - mImage(), - mCroppedImage(), - mImageUrl(), - mStretchPixelsX(), - mStretchPixelsY(), - mImageSize(), - mBorderOnly( borderOnly ) +NPatchVisualPtr NPatchVisual::New( VisualFactoryCache& factoryCache, NinePatchImage image ) { + NPatchVisualPtr nPatchVisual( new NPatchVisual( factoryCache ) ); + VisualUrl visualUrl( image.GetUrl() ); + nPatchVisual->mImageUrl = visualUrl; + return nPatchVisual; } -NPatchVisual::~NPatchVisual() +void NPatchVisual::LoadImages() { -} + TextureManager& textureManager = mFactoryCache.GetTextureManager(); + bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; -void NPatchVisual::DoSetProperties( const Property::Map& propertyMap ) -{ - Property::Value* imageURLValue = propertyMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME ); - if( imageURLValue ) + if( NPatchLoader::UNINITIALIZED_ID == mId && mImageUrl.IsLocalResource() ) { - //Read the borderOnly property first since InitialiseFromImage relies on mBorderOnly to be properly set - Property::Value* borderOnlyValue = propertyMap.Find( Toolkit::ImageVisual::Property::BORDER_ONLY, BORDER_ONLY ); - if( borderOnlyValue ) - { - borderOnlyValue->Get( mBorderOnly ); - } + bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false; + mId = mLoader.Load( textureManager, this, mImageUrl.GetUrl(), mBorder, preMultiplyOnLoad, synchronousLoading ); - if( imageURLValue->Get( mImageUrl ) ) + const NPatchLoader::Data* data; + if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted ) { - NinePatchImage nPatch = NinePatchImage::New( mImageUrl ); - InitializeFromImage( nPatch ); - } - else - { - InitializeFromBrokenImage(); - DALI_LOG_ERROR( "The property '%s' is not a string\n", IMAGE_URL_NAME ); + EnablePreMultipliedAlpha( preMultiplyOnLoad ); } } -} -void NPatchVisual::GetNaturalSize( Vector2& naturalSize ) const -{ - if( mImage ) + if( !mAuxiliaryPixelBuffer && mAuxiliaryUrl.IsValid() && mAuxiliaryUrl.IsLocalResource() ) { - naturalSize.x = mImage.GetWidth(); - naturalSize.y = mImage.GetHeight(); - } - else if( !mImageUrl.empty() ) - { - ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl ); - naturalSize.x = dimentions.GetWidth(); - naturalSize.y = dimentions.GetHeight(); - } - else - { - naturalSize = Vector2::ZERO; + // Load the auxiliary image + auto preMultiplyOnLoading = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + mAuxiliaryPixelBuffer = textureManager.LoadPixelBuffer( mAuxiliaryUrl, Dali::ImageDimensions(), FittingMode::DEFAULT, + SamplingMode::BOX_THEN_LINEAR, synchronousLoading, + this, true, preMultiplyOnLoading ); } } -Geometry NPatchVisual::CreateGeometry() +void NPatchVisual::GetNaturalSize( Vector2& naturalSize ) { - Geometry geometry; - if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 ) + naturalSize.x = 0u; + naturalSize.y = 0u; + + // load now if not already loaded + const NPatchLoader::Data* data; + if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted ) { - if( !mBorderOnly ) - { - geometry = mFactoryCache.GetGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY ); - if( !geometry ) - { - geometry = CreateGeometry( Uint16Pair( 3, 3 ) ); - mFactoryCache.SaveGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY, geometry ); - } - } - else + naturalSize.x = data->croppedWidth; + naturalSize.y = data->croppedHeight; + } + else + { + if( mImageUrl.IsValid() ) { - geometry = mFactoryCache.GetGeometry( VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY ); - if( !geometry ) + ImageDimensions dimensions = Dali::GetOriginalImageSize( mImageUrl.GetUrl() ); + if( dimensions != ImageDimensions( 0, 0 ) ) { - geometry = CreateGeometryBorder( Uint16Pair( 3, 3 ) ); - mFactoryCache.SaveGeometry( VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY, geometry ); + naturalSize.x = dimensions.GetWidth(); + naturalSize.y = dimensions.GetHeight(); } } } - else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0) + + if( mAuxiliaryPixelBuffer ) { - Uint16Pair gridSize( 2 * mStretchPixelsX.Size() + 1, 2 * mStretchPixelsY.Size() + 1 ); - geometry = !mBorderOnly ? CreateGeometry( gridSize ) : CreateGeometryBorder( gridSize ); + naturalSize.x = std::max( naturalSize.x, float(mAuxiliaryPixelBuffer.GetWidth()) ); + naturalSize.y = std::max( naturalSize.y, float(mAuxiliaryPixelBuffer.GetHeight()) ); } - - return geometry; } -Shader NPatchVisual::CreateShader() +void NPatchVisual::DoSetProperties( const Property::Map& propertyMap ) { - Shader shader; - if( !mImpl->mCustomShader ) - { - if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 ) - { - shader = mFactoryCache.GetShader( VisualFactoryCache::NINE_PATCH_SHADER ); - if( !shader ) - { - shader = Shader::New( VERTEX_SHADER_3X3, FRAGMENT_SHADER ); - mFactoryCache.SaveShader( VisualFactoryCache::NINE_PATCH_SHADER, shader ); - } - } - else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0) - { - std::stringstream vertexShader; - vertexShader << "#define FACTOR_SIZE_X " << mStretchPixelsX.Size() + 2 << "\n" - << "#define FACTOR_SIZE_Y " << mStretchPixelsY.Size() + 2 << "\n" - << VERTEX_SHADER; + // URL is already passed in via constructor - shader = Shader::New( vertexShader.str(), FRAGMENT_SHADER ); - } - } - else + Property::Value* borderOnlyValue = propertyMap.Find( Toolkit::ImageVisual::Property::BORDER_ONLY, BORDER_ONLY ); + if( borderOnlyValue ) { - const char* fragmentShader = FRAGMENT_SHADER; - Dali::Shader::Hint::Value hints = Dali::Shader::Hint::NONE; + borderOnlyValue->Get( mBorderOnly ); + } - if( !mImpl->mCustomShader->mFragmentShader.empty() ) + Property::Value* borderValue = propertyMap.Find( Toolkit::ImageVisual::Property::BORDER, BORDER ); + if( borderValue && ! borderValue->Get( mBorder ) ) // If value exists and is rect, just set mBorder + { + // Not a rect so try vector4 + Vector4 border; + if( borderValue->Get( border ) ) { - fragmentShader = mImpl->mCustomShader->mFragmentShader.c_str(); + mBorder.left = static_cast< int >( border.x ); + mBorder.right = static_cast< int >( border.y ); + mBorder.bottom = static_cast< int >( border.z ); + mBorder.top = static_cast< int >( border.w ); } - hints = mImpl->mCustomShader->mHints; + } - if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 ) - { - shader = Shader::New( VERTEX_SHADER_3X3, fragmentShader, hints ); - } - else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0) + Property::Value* auxImage = propertyMap.Find( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, AUXILIARY_IMAGE_NAME ); + if( auxImage ) + { + std::string url; + if( auxImage->Get( url ) ) { - std::stringstream vertexShader; - vertexShader << "#define FACTOR_SIZE_X " << mStretchPixelsX.Size() + 2 << "\n" - << "#define FACTOR_SIZE_Y " << mStretchPixelsY.Size() + 2 << "\n" - << VERTEX_SHADER; - - shader = Shader::New( vertexShader.str(), fragmentShader, hints ); + mAuxiliaryUrl = url; } } - return shader; -} - -void NPatchVisual::InitializeRenderer() -{ - Geometry geometry = CreateGeometry(); - Shader shader = CreateShader(); - - if( !geometry || !shader ) + Property::Value* auxImageAlpha = propertyMap.Find( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, AUXILIARY_IMAGE_ALPHA_NAME ); + if( auxImageAlpha ) { - DALI_LOG_ERROR("The 9 patch image '%s' doesn't have any valid stretch borders and so is not a valid 9 patch image\n", mImageUrl.c_str() ); - InitializeFromBrokenImage(); + auxImageAlpha->Get( mAuxiliaryImageAlpha ); } - TextureSet textureSet = TextureSet::New(); - mImpl->mRenderer = Renderer::New( geometry, shader ); - mImpl->mRenderer.SetTextures( textureSet ); - - //Register transform properties - mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT ); -} - - -void NPatchVisual::DoSetOnStage( Actor& actor ) -{ - if( !mCroppedImage ) + Property::Value* synchronousLoading = propertyMap.Find( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, SYNCHRONOUS_LOADING ); + if( synchronousLoading ) { - if( !mImageUrl.empty() ) + bool sync = false; + synchronousLoading->Get( sync ); + if( sync ) { - NinePatchImage nPatch = NinePatchImage::New( mImageUrl ); - InitializeFromImage( nPatch ); + mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; } - else if( mImage ) + else { - InitializeFromImage( mImage ); + mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; } } +} - //initialize the renderer after initializing from the image since we need to know the grid size from the image before creating the geometry - InitializeRenderer(); +void NPatchVisual::DoSetOnStage( Actor& actor ) +{ + // load when first go on stage + LoadImages(); - if( mCroppedImage ) + const NPatchLoader::Data* data; + if( mLoader.GetNPatchData( mId, data ) ) { - ApplyImageToSampler(); - } + Geometry geometry = CreateGeometry(); + Shader shader = CreateShader(); + + mImpl->mRenderer = Renderer::New( geometry, shader ); + + mPlacementActor = actor; + if( data->loadCompleted ) + { + ApplyTextureAndUniforms(); + actor.AddRenderer( mImpl->mRenderer ); + mPlacementActor.Reset(); - actor.AddRenderer( mImpl->mRenderer ); + // npatch loaded and ready to display + ResourceReady( Toolkit::Visual::ResourceStatus::READY ); + } + } } void NPatchVisual::DoSetOffStage( Actor& actor ) { - mCroppedImage.Reset(); actor.RemoveRenderer( mImpl->mRenderer ); mImpl->mRenderer.Reset(); + mPlacementActor.Reset(); +} + +void NPatchVisual::OnSetTransform() +{ + if( mImpl->mRenderer ) + { + mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT ); + } } void NPatchVisual::DoCreatePropertyMap( Property::Map& map ) const { map.Clear(); - map.Insert( Toolkit::VisualProperty::TYPE, Toolkit::Visual::IMAGE ); - if( !mImageUrl.empty() ) + map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::N_PATCH ); + map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() ); + map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly ); + map.Insert( Toolkit::ImageVisual::Property::BORDER, mBorder ); + + if( mAuxiliaryUrl.IsValid() ) { - map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl ); + map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl() ); + map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha ); } - else if( mImage ) +} + +void NPatchVisual::DoCreateInstancePropertyMap( Property::Map& map ) const +{ + if( mAuxiliaryUrl.IsValid() ) { - map.Insert( Toolkit::ImageVisual::Property::URL, mImage.GetUrl() ); + map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl() ); + map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha ); } - map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly ); } -void NPatchVisual::DoSetProperty( Dali::Property::Index index, const Dali::Property::Value& propertyValue ) +NPatchVisual::NPatchVisual( VisualFactoryCache& factoryCache ) +: Visual::Base( factoryCache, Visual::FittingMode::FILL ), + mPlacementActor(), + mLoader( factoryCache.GetNPatchLoader() ), + mImageUrl(), + mAuxiliaryUrl(), + mId( NPatchLoader::UNINITIALIZED_ID ), + mBorderOnly( false ), + mBorder(), + mAuxiliaryImageAlpha( 0.0f ) { - // TODO + EnablePreMultipliedAlpha( mFactoryCache.GetPreMultiplyOnLoad() ); } -Dali::Property::Value NPatchVisual::DoGetProperty( Dali::Property::Index index ) +NPatchVisual::~NPatchVisual() { - // TODO - return Dali::Property::Value(); } -void NPatchVisual::OnSetTransform() +Geometry NPatchVisual::CreateGeometry() { - if( mImpl->mRenderer ) + Geometry geometry; + const NPatchLoader::Data* data; + if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted ) { - mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT ); + if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 ) + { + if( DALI_UNLIKELY( mBorderOnly ) ) + { + geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY ); + } + else + { + geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY ); + } + } + else if( data->stretchPixelsX.Size() > 0 || data->stretchPixelsY.Size() > 0) + { + Uint16Pair gridSize( 2 * data->stretchPixelsX.Size() + 1, 2 * data->stretchPixelsY.Size() + 1 ); + geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize ); + } + } + else + { + // no N patch data so use default geometry + geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY ); } + return geometry; } - -void NPatchVisual::ChangeRenderer( bool oldBorderOnly, size_t oldGridX, size_t oldGridY ) +Shader NPatchVisual::CreateShader() { - //check to see if the border style has changed - - bool borderOnlyChanged = oldBorderOnly != mBorderOnly; - bool gridChanged = oldGridX != mStretchPixelsX.Size() || oldGridY != mStretchPixelsY.Size(); + Shader shader; + const NPatchLoader::Data* data; + // 0 is either no data (load failed?) or no stretch regions on image + // for both cases we use the default shader + NPatchLoader::StretchRanges::SizeType xStretchCount = 0; + NPatchLoader::StretchRanges::SizeType yStretchCount = 0; + + auto fragmentShader = mAuxiliaryPixelBuffer ? FRAGMENT_MASK_SHADER + : FRAGMENT_SHADER; + auto shaderType = mAuxiliaryPixelBuffer ? VisualFactoryCache::NINE_PATCH_MASK_SHADER + : VisualFactoryCache::NINE_PATCH_SHADER; + + // ask loader for the regions + if( mLoader.GetNPatchData( mId, data ) ) + { + xStretchCount = data->stretchPixelsX.Count(); + yStretchCount = data->stretchPixelsY.Count(); + } - if( borderOnlyChanged || gridChanged ) + if( DALI_LIKELY( !mImpl->mCustomShader ) ) { - Geometry geometry = CreateGeometry(); - if( geometry ) + if( DALI_LIKELY( ( xStretchCount == 1 && yStretchCount == 1 ) || + ( xStretchCount == 0 && yStretchCount == 0 ) ) ) { - mImpl->mRenderer.SetGeometry( geometry ); + shader = mFactoryCache.GetShader( shaderType ); + if( DALI_UNLIKELY( !shader ) ) + { + shader = Shader::New( VERTEX_SHADER_3X3, fragmentShader ); + // Only cache vanilla 9 patch shaders + mFactoryCache.SaveShader( shaderType, shader ); + } } - else + else if( xStretchCount > 0 || yStretchCount > 0) { - InitializeFromBrokenImage(); + std::stringstream vertexShader; + vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n" + << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n" + << VERTEX_SHADER; + + shader = Shader::New( vertexShader.str(), fragmentShader ); } } - - if( gridChanged ) + else { - Shader shader = CreateShader(); - TextureSet textureSet; - if( shader ) + Dali::Shader::Hint::Value hints = Dali::Shader::Hint::NONE; + + if( !mImpl->mCustomShader->mFragmentShader.empty() ) + { + fragmentShader = mImpl->mCustomShader->mFragmentShader.c_str(); + } + hints = mImpl->mCustomShader->mHints; + + /* Apply Custom Vertex Shader only if image is 9-patch */ + if( ( xStretchCount == 1 && yStretchCount == 1 ) || + ( xStretchCount == 0 && yStretchCount == 0 ) ) { - textureSet = mImpl->mRenderer.GetTextures(); - if( !textureSet ) + const char* vertexShader = VERTEX_SHADER_3X3; + + if( !mImpl->mCustomShader->mVertexShader.empty() ) { - InitializeFromBrokenImage(); + vertexShader = mImpl->mCustomShader->mVertexShader.c_str(); } - mImpl->mRenderer.SetShader( shader ); + shader = Shader::New( vertexShader, fragmentShader, hints ); } - } -} + else if( xStretchCount > 0 || yStretchCount > 0) + { + std::stringstream vertexShader; + vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n" + << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n" + << VERTEX_SHADER; -void NPatchVisual::InitializeFromImage( NinePatchImage nPatch ) -{ - mCroppedImage = nPatch.CreateCroppedBufferImage(); - if( !mCroppedImage ) - { - DALI_LOG_ERROR("'%s' specify a valid 9 patch image\n", mImageUrl.c_str() ); - InitializeFromBrokenImage(); - return; + shader = Shader::New( vertexShader.str(), fragmentShader, hints ); + } } - mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() ); - - mStretchPixelsX = nPatch.GetStretchPixelsX(); - mStretchPixelsY = nPatch.GetStretchPixelsY(); + return shader; } -void NPatchVisual::InitializeFromBrokenImage() +void NPatchVisual::ApplyTextureAndUniforms() { - mCroppedImage = VisualFactoryCache::GetBrokenVisualImage(); - mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() ); - - mStretchPixelsX.Clear(); - mStretchPixelsX.PushBack( Uint16Pair( 0, mImageSize.GetWidth() ) ); - mStretchPixelsY.Clear(); - mStretchPixelsY.PushBack( Uint16Pair( 0, mImageSize.GetHeight() ) ); -} + const NPatchLoader::Data* data; + TextureSet textureSet; -void NPatchVisual::ApplyImageToSampler() -{ - TextureSet textureSet = mImpl->mRenderer.GetTextures(); - if( textureSet ) + if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted ) { - TextureSetImage( textureSet, 0u, mCroppedImage ); + textureSet = data->textureSet; - if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 ) + if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 ) { //special case for 9 patch - Uint16Pair stretchX = mStretchPixelsX[ 0 ]; - Uint16Pair stretchY = mStretchPixelsY[ 0 ]; + Uint16Pair stretchX = data->stretchPixelsX[ 0 ]; + Uint16Pair stretchY = data->stretchPixelsY[ 0 ]; - uint16_t stretchWidth = stretchX.GetY() - stretchX.GetX(); - uint16_t stretchHeight = stretchY.GetY() - stretchY.GetX(); + uint16_t stretchWidth = ( stretchX.GetY() >= stretchX.GetX() ) ? stretchX.GetY() - stretchX.GetX() : 0; + uint16_t stretchHeight = ( stretchY.GetY() >= stretchY.GetX() ) ? stretchY.GetY() - stretchY.GetX() : 0; mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO ); mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) ); - mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( mImageSize.GetWidth() - stretchWidth, mImageSize.GetHeight() - stretchHeight ) ); + mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->croppedWidth - stretchWidth, data->croppedHeight - stretchHeight ) ); mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) ); } else @@ -574,13 +618,70 @@ void NPatchVisual::ApplyImageToSampler() mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO ); mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO ); - RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", mStretchPixelsX, mImageSize.GetWidth() ); - RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", mStretchPixelsY, mImageSize.GetHeight() ); + RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->stretchPixelsX, data->croppedWidth ); + RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->stretchPixelsY, data->croppedHeight ); + } + } + else + { + DALI_LOG_ERROR("The N patch image '%s' is not a valid N patch image\n", mImageUrl.GetUrl().c_str() ); + textureSet = TextureSet::New(); + + Image croppedImage = mFactoryCache.GetBrokenVisualImage(); + TextureSetImage( textureSet, 0u, croppedImage ); + mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO ); + mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2::ZERO ); + mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2::ZERO ); + mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( croppedImage.GetWidth(), croppedImage.GetHeight() ) ); + } + + if( mAuxiliaryPixelBuffer ) + { + // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the + // same size as the unstretched NPatch. This will give slightly higher quality results than just relying + // on GL interpolation alone. + if( mAuxiliaryPixelBuffer.GetWidth() < data->croppedWidth && + mAuxiliaryPixelBuffer.GetHeight() < data->croppedHeight ) + { + mAuxiliaryPixelBuffer.Resize( data->croppedWidth, data->croppedHeight ); + } + + // Note, this resets mAuxiliaryPixelBuffer handle + auto auxiliaryPixelData = Devel::PixelBuffer::Convert( mAuxiliaryPixelBuffer ); + + auto texture = Texture::New( TextureType::TEXTURE_2D, + auxiliaryPixelData.GetPixelFormat(), auxiliaryPixelData.GetWidth(), + auxiliaryPixelData.GetHeight() ); + texture.Upload( auxiliaryPixelData ); + textureSet.SetTexture( 1, texture ); + DevelHandle::RegisterProperty( mImpl->mRenderer, DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, + AUXILIARY_IMAGE_ALPHA_NAME, mAuxiliaryImageAlpha ); + } + mImpl->mRenderer.SetTextures( textureSet ); + + // Register transform properties + mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT ); +} + +Geometry NPatchVisual::GetNinePatchGeometry( VisualFactoryCache::GeometryType subType ) +{ + Geometry geometry = mFactoryCache.GetGeometry( subType ); + if( !geometry ) + { + if( DALI_LIKELY( VisualFactoryCache::NINE_PATCH_GEOMETRY == subType ) ) + { + geometry = CreateGridGeometry( Uint16Pair( 3, 3 ) ); + } + else if( VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY == subType ) + { + geometry = CreateBorderGeometry( Uint16Pair( 3, 3 ) ); } + mFactoryCache.SaveGeometry( subType, geometry ); } + return geometry; } -Geometry NPatchVisual::CreateGeometry( Uint16Pair gridSize ) +Geometry NPatchVisual::CreateGridGeometry( Uint16Pair gridSize ) { uint16_t gridWidth = gridSize.GetWidth(); uint16_t gridHeight = gridSize.GetHeight(); @@ -598,7 +699,6 @@ Geometry NPatchVisual::CreateGeometry( Uint16Pair gridSize ) } // Create indices - //TODO: compare performance with triangle strip when Geometry supports it Vector< unsigned short > indices; indices.Reserve( gridWidth * gridHeight * 6 ); @@ -615,7 +715,7 @@ Geometry NPatchVisual::CreateGeometry( Uint16Pair gridSize ) return GenerateGeometry( vertices, indices ); } -Geometry NPatchVisual::CreateGeometryBorder( Uint16Pair gridSize ) +Geometry NPatchVisual::CreateBorderGeometry( Uint16Pair gridSize ) { uint16_t gridWidth = gridSize.GetWidth(); uint16_t gridHeight = gridSize.GetHeight(); @@ -655,7 +755,6 @@ Geometry NPatchVisual::CreateGeometryBorder( Uint16Pair gridSize ) } // Create indices - //TODO: compare performance with triangle strip when Geometry supports it Vector< unsigned short > indices; indices.Reserve( gridWidth * gridHeight * 6 ); @@ -713,6 +812,56 @@ Geometry NPatchVisual::CreateGeometryBorder( Uint16Pair gridSize ) return GenerateGeometry( vertices, indices ); } +void NPatchVisual::SetResource() +{ + const NPatchLoader::Data* data; + if( mImpl->mRenderer && mLoader.GetNPatchData( mId, data ) ) + { + Geometry geometry = CreateGeometry(); + Shader shader = CreateShader(); + + mImpl->mRenderer.SetGeometry( geometry ); + mImpl->mRenderer.SetShader( shader ); + + Actor actor = mPlacementActor.GetHandle(); + if( actor ) + { + ApplyTextureAndUniforms(); + actor.AddRenderer( mImpl->mRenderer ); + mPlacementActor.Reset(); + + // npatch loaded and ready to display + ResourceReady( Toolkit::Visual::ResourceStatus::READY ); + } + } +} + +void NPatchVisual::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) +{ + if( url.GetUrl() == mAuxiliaryUrl.GetUrl() ) + { + mAuxiliaryPixelBuffer = pixelBuffer; + const NPatchLoader::Data* data; + if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted ) + { + SetResource(); + } + } + else + { + if( loadSuccess ) + { + mLoader.SetNPatchData( mId, pixelBuffer ); + EnablePreMultipliedAlpha( preMultiplied ); + } + + if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() ) + { + SetResource(); + } + } +} + } // namespace Internal } // namespace Toolkit