X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Frenderers%2Fimage%2Fimage-renderer.cpp;h=058f2b90cb9bcc199470cb0ad75097f88463b74d;hp=477096035d44ab9976ba7d661e2280fd3db11f64;hb=24599f0aed006ccf0e2f20b92334b649f46c94d2;hpb=cde5ef8b9c6de8008865e29f3b1acab8f7cd2bbc diff --git a/dali-toolkit/internal/controls/renderers/image/image-renderer.cpp b/dali-toolkit/internal/controls/renderers/image/image-renderer.cpp index 4770960..058f2b9 100644 --- a/dali-toolkit/internal/controls/renderers/image/image-renderer.cpp +++ b/dali-toolkit/internal/controls/renderers/image/image-renderer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2016 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,14 +19,21 @@ #include "image-renderer.h" // EXTERNAL HEADER +#include // for strncasecmp #include +#include +#include +#include +#include #include // INTERNAL HEADER +#include #include #include #include #include +#include namespace Dali { @@ -39,39 +46,45 @@ namespace Internal namespace { -const char * const RENDERER_TYPE("renderer-type"); -const char * const RENDERER_TYPE_VALUE("image-renderer"); +const char HTTP_URL[] = "http://"; +const char HTTPS_URL[] = "https://"; // property names -const char * const IMAGE_URL_NAME( "image-url" ); -const char * const IMAGE_FITTING_MODE( "image-fitting-mode" ); -const char * const IMAGE_SAMPLING_MODE( "image-sampling-mode" ); -const char * const IMAGE_DESIRED_WIDTH( "image-desired-width" ); -const char * const IMAGE_DESIRED_HEIGHT( "image-desired-height" ); +const char * const IMAGE_FITTING_MODE( "fittingMode" ); +const char * const IMAGE_SAMPLING_MODE( "samplingMode" ); +const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" ); +const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" ); +const char * const SYNCHRONOUS_LOADING( "synchronousLoading" ); // fitting modes -const char * const SHRINK_TO_FIT("shrink-to-fit"); -const char * const SCALE_TO_FILL("scale-to-fill"); -const char * const FIT_WIDTH("fit-width"); -const char * const FIT_HEIGHT("fit-height"); -const char * const DEFAULT("default"); +const char * const SHRINK_TO_FIT("SHRINK_TO_FIT"); +const char * const SCALE_TO_FILL("SCALE_TO_FILL"); +const char * const FIT_WIDTH("FIT_WIDTH"); +const char * const FIT_HEIGHT("FIT_HEIGHT"); +const char * const DEFAULT("DEFAULT"); // sampling modes -const char * const BOX("box"); -const char * const NEAREST("nearest"); -const char * const LINEAR("linear"); -const char * const BOX_THEN_NEAREST("box-then-nearest"); -const char * const BOX_THEN_LINEAR("box-then-linear"); -const char * const NO_FILTER("no-filter"); -const char * const DONT_CARE("dont-care"); +const char * const BOX("BOX"); +const char * const NEAREST("NEAREST"); +const char * const LINEAR("LINEAR"); +const char * const BOX_THEN_NEAREST("BOX_THEN_NEAREST"); +const char * const BOX_THEN_LINEAR("BOX_THEN_LINEAR"); +const char * const NO_FILTER("NO_FILTER"); +const char * const DONT_CARE("DONT_CARE"); -std::string TEXTURE_UNIFORM_NAME = "sTexture"; +const std::string PIXEL_AREA_UNIFORM_NAME = "pixelArea"; + +const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); + +const char* DEFAULT_SAMPLER_TYPENAME = "sampler2D"; const char* VERTEX_SHADER = DALI_COMPOSE_SHADER( attribute mediump vec2 aPosition;\n - varying mediump vec2 vTexCoord;\n uniform mediump mat4 uMvpMatrix;\n uniform mediump vec3 uSize;\n + uniform mediump vec4 uAtlasRect;\n + uniform mediump vec4 pixelArea; + varying mediump vec2 vTexCoord;\n \n void main()\n {\n @@ -79,7 +92,7 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER( vertexPosition.xyz *= uSize;\n vertexPosition = uMvpMatrix * vertexPosition;\n \n - vTexCoord = aPosition + vec2(0.5);\n + vTexCoord = mix( uAtlasRect.xy, uAtlasRect.zw, pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) ) );\n gl_Position = vertexPosition;\n }\n ); @@ -95,43 +108,6 @@ const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER( }\n ); -void AddQuadIndices( Vector< unsigned int >& indices, unsigned int rowIdx, unsigned int nextRowIdx ) -{ - indices.PushBack( rowIdx ); - indices.PushBack( nextRowIdx + 1 ); - indices.PushBack( rowIdx + 1 ); - - indices.PushBack( rowIdx ); - indices.PushBack( nextRowIdx ); - indices.PushBack( nextRowIdx + 1 ); -} - -Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices ) -{ - Property::Map vertexFormat; - vertexFormat[ "aPosition" ] = Property::VECTOR2; - PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() ); - if( vertices.Size() > 0 ) - { - vertexPropertyBuffer.SetData( &vertices[ 0 ] ); - } - - Property::Map indexFormat; - indexFormat[ "indices" ] = Property::INTEGER; - PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() ); - if( indices.Size() > 0 ) - { - indexPropertyBuffer.SetData( &indices[ 0 ] ); - } - - // Create the geometry object - Geometry geometry = Geometry::New(); - geometry.AddVertexBuffer( vertexPropertyBuffer ); - geometry.SetIndexBuffer( indexPropertyBuffer ); - - return geometry; -} - Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize ) { Geometry geometry; @@ -141,42 +117,13 @@ Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gri geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY ); if( !geometry ) { - geometry = factoryCache.CreateQuadGeometry(); + geometry = RendererFactoryCache::CreateQuadGeometry(); factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry ); } } else { - uint16_t gridWidth = gridSize.GetWidth(); - uint16_t gridHeight = gridSize.GetHeight(); - - // Create vertices - Vector< Vector2 > vertices; - vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) ); - - for( int y = 0; y < gridHeight + 1; ++y ) - { - for( int x = 0; x < gridWidth + 1; ++x ) - { - vertices.PushBack( Vector2( (float)x/gridWidth - 0.5f, (float)y/gridHeight - 0.5f) ); - } - } - - // Create indices - Vector< unsigned int > indices; - indices.Reserve( gridWidth * gridHeight * 6 ); - - unsigned int rowIdx = 0; - unsigned int nextRowIdx = gridWidth + 1; - for( int y = 0; y < gridHeight; ++y, ++nextRowIdx, ++rowIdx ) - { - for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx ) - { - AddQuadIndices( indices, rowIdx, nextRowIdx ); - } - } - - return GenerateGeometry( vertices, indices ); + geometry = RendererFactoryCache::CreateGridGeometry( gridSize ); } return geometry; @@ -184,11 +131,14 @@ Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gri } //unnamed namespace -ImageRenderer::ImageRenderer() -: ControlRenderer(), +ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager ) +: ControlRenderer( factoryCache ), + mAtlasManager( atlasManager ), mDesiredSize(), mFittingMode( FittingMode::DEFAULT ), - mSamplingMode( SamplingMode::DEFAULT ) + mSamplingMode( SamplingMode::DEFAULT ), + mNativeFragmentShaderCode( ), + mNativeImageFlag( false ) { } @@ -196,9 +146,9 @@ ImageRenderer::~ImageRenderer() { } -void ImageRenderer::DoInitialize( RendererFactoryCache& factoryCache, const Property::Map& propertyMap ) +void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap ) { - Initialize(factoryCache); + std::string oldImageUrl = mImageUrl; Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME ); if( imageURLValue ) @@ -302,6 +252,55 @@ void ImageRenderer::DoInitialize( RendererFactoryCache& factoryCache, const Prop } mDesiredSize = ImageDimensions( desiredWidth, desiredHeight ); + + } + + Property::Value* syncLoading = propertyMap.Find( SYNCHRONOUS_LOADING ); + if( syncLoading ) + { + bool sync; + syncLoading->Get( sync ); + if( sync ) + { + mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; + } + else + { + mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; + } + } + + // if sync loading is required, the loading should start immediately when new image url is set or the actor is off stage + // ( for on-stage actor with image url unchanged, resource loading is already finished) + if( ( !mImpl->mRenderer || imageURLValue) && IsSynchronousResourceLoading() ) + { + DoSynchronousResourceLoading(); + } + + // remove old renderer if exit + if( mImpl->mRenderer ) + { + if( actor ) //remove old renderer from actor + { + actor.RemoveRenderer( mImpl->mRenderer ); + } + if( !oldImageUrl.empty() ) //clean old renderer from cache + { + CleanCache( oldImageUrl ); + } + } + + NativeImage nativeImage = NativeImage::DownCast( mImage ); + + if( nativeImage ) + { + SetNativeFragmentShaderCode( nativeImage ); + } + + // if actor is on stage, create new renderer and apply to actor + if( actor && actor.OnStage() ) + { + SetOnStage( actor ); } } @@ -344,14 +343,225 @@ void ImageRenderer::SetOffset( const Vector2& offset ) { } +Renderer ImageRenderer::CreateRenderer() const +{ + Geometry geometry; + Shader shader; + + // If mImage is nativeImage with custom sampler or prefix, mNativeFragmentShaderCode will be applied. + // Renderer can't be shared between NativeImage and other image types. + if( !mNativeFragmentShaderCode.empty() ) + { + return CreateNativeImageRenderer(); + } + + if( !mImpl->mCustomShader ) + { + geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) ); + + shader = GetImageShader(mFactoryCache); + } + else + { + geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize ); + if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() ) + { + shader = GetImageShader(mFactoryCache); + } + else + { + shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader, + mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader, + mImpl->mCustomShader->mHints ); + if( mImpl->mCustomShader->mVertexShader.empty() ) + { + shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT ); + shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT ); + } + } + } + + Renderer renderer = Renderer::New( geometry, shader ); + + return renderer; +} + +Renderer ImageRenderer::CreateNativeImageRenderer() const +{ + Geometry geometry; + Shader shader; + + if( !mImpl->mCustomShader ) + { + geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) ); + + shader = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode ); + shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT ); + shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT ); + } + else + { + geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize ); + shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader, + mNativeFragmentShaderCode, + mImpl->mCustomShader->mHints ); + if( mImpl->mCustomShader->mVertexShader.empty() ) + { + shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT ); + shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT ); + } + } + + TextureSet textureSet = TextureSet::New(); + Renderer renderer = Renderer::New( geometry, shader ); + renderer.SetTextures( textureSet ); + + return renderer; +} + + +bool ImageRenderer::IsSynchronousResourceLoading() const +{ + return mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; +} + +void ImageRenderer::DoSynchronousResourceLoading() +{ + if( !mImageUrl.empty() ) + { + BitmapLoader loader = BitmapLoader::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode ); + loader.Load(); + mPixels = loader.GetPixelData(); + } +} + +Image ImageRenderer::LoadImage( const std::string& url, bool synchronousLoading ) +{ + if( synchronousLoading ) + { + if( !mPixels ) + { + // use broken image + return RendererFactory::GetBrokenRendererImage(); + } + Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() ); + image.Upload( mPixels, 0, 0 ); + return image; + } + else + { + ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode ); + resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded ); + return resourceImage; + } +} + +TextureSet ImageRenderer::CreateTextureSet( Vector4& textureRect, const std::string& url, bool synchronousLoading ) +{ + TextureSet textureSet; + textureRect = FULL_TEXTURE_RECT; + if( synchronousLoading ) + { + if( !mPixels ) + { + // use broken image + textureSet = TextureSet::New(); + TextureSetImage( textureSet, 0u, RendererFactory::GetBrokenRendererImage() ); + } + else + { + textureSet = mAtlasManager.Add(textureRect, mPixels ); + if( !textureSet ) // big image, no atlasing + { + Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() ); + image.Upload( mPixels, 0, 0 ); + textureSet = TextureSet::New(); + TextureSetImage( textureSet, 0u, image ); + } + } + } + else + { + textureSet = mAtlasManager.Add(textureRect, url, mDesiredSize, mFittingMode, mSamplingMode ); + if( !textureSet ) // big image, no atlasing + { + ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode ); + resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded ); + textureSet = TextureSet::New(); + TextureSetImage( textureSet, 0u, resourceImage ); + } + } + + return textureSet; +} + +void ImageRenderer::InitializeRenderer( const std::string& imageUrl ) +{ + if( imageUrl.empty() ) + { + return; + } + + mImageUrl = imageUrl; + mImpl->mRenderer.Reset(); + + if( !mImpl->mCustomShader && + ( strncasecmp( imageUrl.c_str(), HTTP_URL, sizeof(HTTP_URL) -1 ) != 0 ) && // ignore remote images + ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) ) + { + mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl ); + if( !mImpl->mRenderer ) + { + Vector4 atlasRect; + TextureSet textureSet = CreateTextureSet(atlasRect, imageUrl, IsSynchronousResourceLoading() ); + Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) ); + Shader shader( GetImageShader(mFactoryCache) ); + mImpl->mRenderer = Renderer::New( geometry, shader ); + mImpl->mRenderer.SetTextures( textureSet ); + if( atlasRect != FULL_TEXTURE_RECT ) + { + mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect ); + } + mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer ); + } + + mImpl->mFlags |= Impl::IS_FROM_CACHE; + } + else + { + // for custom shader or remote image, renderer is not cached and atlas is not applied + + mImpl->mFlags &= ~Impl::IS_FROM_CACHE; + mImpl->mRenderer = CreateRenderer(); + Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() ); + ApplyImageToSampler( image ); + } +} + +void ImageRenderer::InitializeRenderer( const Image& image ) +{ + mImpl->mFlags &= ~Impl::IS_FROM_CACHE; + + mImpl->mRenderer = CreateRenderer(); + + if( image ) + { + ApplyImageToSampler( image ); + } +} + + void ImageRenderer::DoSetOnStage( Actor& actor ) { - if( !mImageUrl.empty() && !mImage ) + if( !mImageUrl.empty() ) { - mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode ); + InitializeRenderer( mImageUrl ); + } + else + { + InitializeRenderer( mImage ); } - ApplyImageToSampler(); } void ImageRenderer::DoSetOffStage( Actor& actor ) @@ -359,14 +569,24 @@ void ImageRenderer::DoSetOffStage( Actor& actor ) //If we own the image then make sure we release it when we go off stage if( !mImageUrl.empty() ) { + actor.RemoveRenderer( mImpl->mRenderer ); + CleanCache(mImageUrl); mImage.Reset(); } + else + { + actor.RemoveRenderer( mImpl->mRenderer ); + mImpl->mRenderer.Reset(); + } } void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const { map.Clear(); - map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE ); + map.Insert( RENDERER_TYPE, IMAGE_RENDERER ); + + bool sync = IsSynchronousResourceLoading(); + map.Insert( SYNCHRONOUS_LOADING, sync ); if( !mImageUrl.empty() ) { map.Insert( IMAGE_URL_NAME, mImageUrl ); @@ -459,121 +679,212 @@ void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const } } -void ImageRenderer::Initialize( RendererFactoryCache& factoryCache ) +Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache ) { - if( !mImpl->mCustomShader ) + Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER ); + if( !shader ) { - mImpl->mGeometry = CreateGeometry( factoryCache, ImageDimensions( 1, 1 ) ); + shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ); + factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader ); + shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT ); + shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT ); + } + return shader; +} - mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER ); +void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode ) +{ + if( mImageUrl != imageUrl ) + { + std::string oldImageUrl = mImageUrl; + mImageUrl = imageUrl; + mDesiredSize = size; + mFittingMode = fittingMode; + mSamplingMode = samplingMode; + mImage.Reset(); - if( !mImpl->mShader ) + if( IsSynchronousResourceLoading() ) { - mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ); - factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader ); + DoSynchronousResourceLoading(); } - } - else - { - mImpl->mGeometry = CreateGeometry( factoryCache, mImpl->mCustomShader->mGridSize ); - if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() ) + if( mImpl->mRenderer ) { - mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER ); + if( GetIsFromCache() ) // if renderer is from cache, remove the old one + { + //remove old renderer + if( actor ) + { + actor.RemoveRenderer( mImpl->mRenderer ); + } - if( !mImpl->mShader ) + //clean the cache + if( !oldImageUrl.empty() ) + { + CleanCache(oldImageUrl); + } + + if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor + { + SetOnStage(actor); + } + } + else // if renderer is not from cache, reuse the same renderer and only change the texture { - mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ); - factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader ); + Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() ); + ApplyImageToSampler( image ); } } - else - { - mImpl->mShader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader, - mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader, - mImpl->mCustomShader->mHints ); - } } +} - if( mImpl->mRenderer ) +void ImageRenderer::SetImage( Actor& actor, const Image& image ) +{ + if( mImage != image ) { - mImpl->mRenderer.SetGeometry( mImpl->mGeometry ); - Material material = mImpl->mRenderer.GetMaterial(); - if( material ) + NativeImage newNativeImage = NativeImage::DownCast( image ); + bool newRendererFlag = true; + + if( newNativeImage && !mNativeImageFlag ) { - material.SetShader( mImpl->mShader ); + SetNativeFragmentShaderCode( newNativeImage ); } - } -} - -void ImageRenderer::SetImage( const std::string& imageUrl ) -{ - SetImage( imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT ); -} -void ImageRenderer::SetImage( const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode ) -{ - if( mImageUrl != imageUrl ) - { - mImageUrl = imageUrl; - mDesiredSize = ImageDimensions( desiredWidth, desiredHeight ); - mFittingMode = fittingMode; - mSamplingMode = samplingMode; + if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) ) + { + newRendererFlag = false; + } - if( !mImageUrl.empty() && mImpl->mIsOnStage ) + if( newNativeImage ) { - mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode ); - ApplyImageToSampler(); + mNativeImageFlag = true; } else { - mImage.Reset(); + mNativeFragmentShaderCode.clear(); + mNativeImageFlag = false; + } + + mImage = image; + + if( mImpl->mRenderer ) + { + // if renderer is from cache, remove the old one, and create new renderer + if( GetIsFromCache() ) + { + //remove old renderer + if( actor ) + { + actor.RemoveRenderer( mImpl->mRenderer ); + } + + //clean the cache + if( !mImageUrl.empty() ) + { + CleanCache(mImageUrl); + } + mImageUrl.clear(); + + if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor + { + SetOnStage(actor); + } + } + // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer + else if( newRendererFlag ) + { + //remove old renderer + if( actor ) + { + actor.RemoveRenderer( mImpl->mRenderer ); + } + + if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor + { + SetOnStage(actor); + } + } + else // if renderer is not from cache, reuse the same renderer and only change the texture + { + ApplyImageToSampler( image ); + } } - } -} -void ImageRenderer::SetImage( Image image ) -{ - if( mImage != image ) - { mImageUrl.clear(); mDesiredSize = ImageDimensions(); mFittingMode = FittingMode::DEFAULT; mSamplingMode = SamplingMode::DEFAULT; - mImage = image; + } +} - if( mImage && mImpl->mIsOnStage ) +void ImageRenderer::ApplyImageToSampler( const Image& image ) +{ + if( image ) + { + TextureSet textureSet = mImpl->mRenderer.GetTextures(); + if( !textureSet ) { - ApplyImageToSampler(); + textureSet = TextureSet::New(); + mImpl->mRenderer.SetTextures( textureSet ); } + TextureSetImage( textureSet, 0u, image ); } } -Image ImageRenderer::GetImage() const +void ImageRenderer::OnImageLoaded( ResourceImage image ) { - return mImage; + if( image.GetLoadingState() == Dali::ResourceLoadingFailed ) + { + Image brokenImage = RendererFactory::GetBrokenRendererImage(); + if( mImpl->mRenderer ) + { + ApplyImageToSampler( brokenImage ); + } + } } -void ImageRenderer::ApplyImageToSampler() +void ImageRenderer::CleanCache(const std::string& url) { - if( mImage ) + TextureSet textureSet = mImpl->mRenderer.GetTextures(); + + Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f ); + Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME ); + if( index != Property::INVALID_INDEX ) { - Material material = mImpl->mRenderer.GetMaterial(); - if( material ) - { - for( std::size_t i = 0; i < material.GetNumberOfSamplers(); ++i ) - { - Sampler sampler = material.GetSamplerAt( i ); - if( sampler.GetUniformName() == TEXTURE_UNIFORM_NAME ) - { - sampler.SetImage( mImage ); - return; - } - } + Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index ); + atlasRectValue.Get( atlasRect ); + } - Sampler sampler = Sampler::New( mImage, TEXTURE_UNIFORM_NAME ); - material.AddSampler( sampler ); - } + mImpl->mRenderer.Reset(); + if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX ) + { + mAtlasManager.Remove( textureSet, atlasRect ); + } +} + +void ImageRenderer::SetNativeFragmentShaderCode( Dali::NativeImage& nativeImage ) +{ + const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix(); + const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename(); + + if( fragmentPreFix ) + { + mNativeFragmentShaderCode = fragmentPreFix; + mNativeFragmentShaderCode += "\n"; + } + + if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() ) + { + mNativeFragmentShaderCode += mImpl->mCustomShader->mFragmentShader; + } + else + { + mNativeFragmentShaderCode += FRAGMENT_SHADER; + } + + if( customSamplerTypename ) + { + mNativeFragmentShaderCode.replace( mNativeFragmentShaderCode.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename ); } }