#include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
#include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
#include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
namespace Dali
{
namespace
{
-const char * const RENDERER_TYPE("renderer-type");
-const char * const RENDERER_TYPE_VALUE("image-renderer");
+const char * const RENDERER_TYPE("rendererType");
+const char * const RENDERER_TYPE_VALUE("imageRenderer");
// 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_URL_NAME( "imageUrl" );
+const char * const IMAGE_FITTING_MODE( "imageFittingMode" );
+const char * const IMAGE_SAMPLING_MODE( "imageSamplingMode" );
+const char * const IMAGE_DESIRED_WIDTH( "imageDesiredWidth" );
+const char * const IMAGE_DESIRED_HEIGHT( "imageDesiredHeight" );
// 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 SHRINK_TO_FIT("shrinkToFit");
+const char * const SCALE_TO_FILL("scaleToFill");
+const char * const FIT_WIDTH("fitWidth");
+const char * const FIT_HEIGHT("fitHeight");
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_THEN_NEAREST("boxThenNearest");
+const char * const BOX_THEN_LINEAR("boxThenLinear");
+const char * const NO_FILTER("noFilter");
+const char * const DONT_CARE("dontCare");
-std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string TEXTURE_RECT_UNIFORM_NAME = "uTextureRect";
+const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
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 uTextureRect;\n
\n
void main()\n
{\n
vertexPosition.xyz *= uSize;\n
vertexPosition = uMvpMatrix * vertexPosition;\n
\n
- vTexCoord = aPosition + vec2(0.5);\n
+ vTexCoord = mix( uTextureRect.xy, uTextureRect.zw, aPosition + vec2(0.5));\n
gl_Position = vertexPosition;\n
}\n
);
}\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;
Geometry geometry = Geometry::New();
geometry.AddVertexBuffer( vertexPropertyBuffer );
geometry.SetIndexBuffer( indexPropertyBuffer );
+ geometry.SetGeometryType( Geometry::TRIANGLE_STRIP );
return geometry;
}
// Create indices
Vector< unsigned int > indices;
- indices.Reserve( gridWidth * gridHeight * 6 );
+ indices.Reserve( (gridWidth+2)*gridHeight*2 - 2);
- unsigned int rowIdx = 0;
- unsigned int nextRowIdx = gridWidth + 1;
- for( int y = 0; y < gridHeight; ++y, ++nextRowIdx, ++rowIdx )
+ for( unsigned int row = 0u; row < gridHeight; ++row )
{
- for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
+ unsigned int rowStartIndex = row*(gridWidth+1u);
+ unsigned int nextRowStartIndex = rowStartIndex + gridWidth +1u;
+
+ if( row != 0u ) // degenerate index on non-first row
+ {
+ indices.PushBack( rowStartIndex );
+ }
+
+ for( unsigned int column = 0u; column < gridWidth+1u; column++) // main strip
+ {
+ indices.PushBack( rowStartIndex + column);
+ indices.PushBack( nextRowStartIndex + column);
+ }
+
+ if( row != gridHeight-1u ) // degenerate index on non-last row
{
- AddQuadIndices( indices, rowIdx, nextRowIdx );
+ indices.PushBack( nextRowStartIndex + gridWidth );
}
}
} //unnamed namespace
-ImageRenderer::ImageRenderer()
-: ControlRenderer(),
+ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
+: ControlRenderer( factoryCache ),
+ mAtlasManager( atlasManager ),
+ mTextureRect( FULL_TEXTURE_RECT ),
mDesiredSize(),
mFittingMode( FittingMode::DEFAULT ),
mSamplingMode( SamplingMode::DEFAULT )
{
}
-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 )
mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
}
+
+ if( mImpl->mRenderer )
+ {
+ //remove old renderer
+ if( actor )
+ {
+ actor.RemoveRenderer( mImpl->mRenderer );
+ }
+
+ //clean the cache
+ if( !oldImageUrl.empty() )
+ {
+ CleanCache( oldImageUrl );
+ }
+
+ //Initialize the renderer
+ if( !mImageUrl.empty() )
+ {
+ InitializeRenderer( mImageUrl );
+ }
+ else if( mImage )
+ {
+ InitializeRenderer( mImage );
+ }
+
+ //add the new renderer to the actor
+ if( actor && mImpl->mRenderer )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ }
+ }
}
void ImageRenderer::SetSize( const Vector2& size )
{
}
+Renderer ImageRenderer::CreateRenderer() const
+{
+ Geometry geometry;
+ Shader shader;
+
+ 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 );
+ }
+ }
+
+ Material material = Material::New( shader );
+ return Renderer::New( geometry, material );
+}
+
+void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
+{
+ if( mImageUrl.empty() )
+ {
+ mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
+ return;
+ }
+
+ mImpl->mRenderer.Reset();
+ if( !mImpl->mCustomShader )
+ {
+ mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
+ if( !mImpl->mRenderer )
+ {
+ Material material = mAtlasManager.Add(mTextureRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ if( material )
+ {
+ Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
+ mImpl->mRenderer = Renderer::New( geometry, material );
+ SetTextureRectUniform(mTextureRect);
+ }
+ else // big image, atlasing is not applied
+ {
+ mImpl->mRenderer = CreateRenderer();
+ SetTextureRectUniform(FULL_TEXTURE_RECT);
+
+ ResourceImage image = Dali::ResourceImage::New( imageUrl );
+ image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+ Material material = mImpl->mRenderer.GetMaterial();
+ material.AddTexture( image, TEXTURE_UNIFORM_NAME );
+ }
+
+ mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
+ }
+ else
+ {
+ Property::Value textureRect = mImpl->mRenderer.GetProperty( mImpl->mRenderer.GetPropertyIndex(TEXTURE_RECT_UNIFORM_NAME) );
+ textureRect.Get( mTextureRect );
+ }
+ mImpl->mFlags |= Impl::IS_FROM_CACHE;
+ }
+ else
+ {
+ mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
+ mImpl->mRenderer = CreateRenderer();
+ ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+ ApplyImageToSampler( image );
+ }
+}
+
+void ImageRenderer::InitializeRenderer( const Image& image )
+{
+ mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
+
+ if( !image )
+ {
+ return;
+ }
+
+ mImpl->mRenderer = CreateRenderer();
+ ApplyImageToSampler( image );
+ SetTextureRectUniform( FULL_TEXTURE_RECT );
+}
+
+
void ImageRenderer::DoSetOnStage( Actor& actor )
{
- if( !mImageUrl.empty() && !mImage )
+ if( !mImageUrl.empty() )
{
- mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ InitializeRenderer( mImageUrl );
}
+ else if( mImage )
+ {
+ InitializeRenderer( mImage );
+ }
+
+ if( !GetIsFromCache() )
+ {
+ Image image = mImage;
+ if( !mImageUrl.empty() )
+ {
+ ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+ image = resourceImage;
- ApplyImageToSampler();
+ // Set value to the uTextureRect uniform
+ SetTextureRectUniform( FULL_TEXTURE_RECT );
+ }
+
+ ApplyImageToSampler( image );
+ }
}
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
}
}
-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 );
+ }
+ 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 )
+{
+ mDesiredSize = size;
+ mFittingMode = fittingMode;
+ mSamplingMode = samplingMode;
- if( !mImpl->mShader )
+ if( mImageUrl != imageUrl )
+ {
+ if( mImpl->mRenderer )
{
- mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
- factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader );
+ if( GetIsFromCache() )
+ {
+ //remove old renderer
+ if( actor )
+ {
+ actor.RemoveRenderer( mImpl->mRenderer );
+ }
+
+ //clean the cache
+ if( !mImageUrl.empty() )
+ {
+ CleanCache(mImageUrl);
+ }
+
+ //Initialize the renderer
+ InitializeRenderer( imageUrl );
+
+ //add the new renderer to the actor
+ if( actor && mImpl->mRenderer )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ }
+ }
+ else
+ {
+ ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+ ApplyImageToSampler( image );
+ }
}
+
+ mImageUrl = imageUrl;
+
+ mImage.Reset();
}
- else
- {
- mImpl->mGeometry = CreateGeometry( factoryCache, mImpl->mCustomShader->mGridSize );
+}
- if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
+void ImageRenderer::SetImage( Actor& actor, const Image& image )
+{
+ if( mImage != image )
+ {
+ if( mImpl->mRenderer )
{
- mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
+ if( GetIsFromCache() )
+ {
+ //remove old renderer
+ if( actor )
+ {
+ actor.RemoveRenderer( mImpl->mRenderer );
+ }
- if( !mImpl->mShader )
+ //clean the cache
+ if( !mImageUrl.empty() )
+ {
+ CleanCache(mImageUrl);
+ }
+
+ //Initialize the renderer
+ InitializeRenderer( image );
+
+ //add the new renderer to the actor
+ if( actor && mImpl->mRenderer )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ }
+ }
+ else
{
- mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
- factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader );
+ 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 );
- }
+ SetTextureRectUniform( FULL_TEXTURE_RECT );
+
+ mImage = image;
+ mImageUrl.clear();
+ mDesiredSize = ImageDimensions();
+ mFittingMode = FittingMode::DEFAULT;
+ mSamplingMode = SamplingMode::DEFAULT;
}
+}
- if( mImpl->mRenderer )
+void ImageRenderer::ApplyImageToSampler( const Image& image )
+{
+ if( image )
{
- mImpl->mRenderer.SetGeometry( mImpl->mGeometry );
Material material = mImpl->mRenderer.GetMaterial();
if( material )
{
- material.SetShader( mImpl->mShader );
+ int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
+ if( index != -1 )
+ {
+ material.SetTextureImage( index, image );
+ return;
+ }
+
+ material.AddTexture( image, TEXTURE_UNIFORM_NAME );
}
}
}
-void ImageRenderer::SetImage( const std::string& imageUrl )
+void ImageRenderer::OnImageLoaded( ResourceImage image )
{
- 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 )
+ if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
{
- mImageUrl = imageUrl;
- mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
- mFittingMode = fittingMode;
- mSamplingMode = samplingMode;
-
- if( !mImageUrl.empty() && mImpl->mIsOnStage )
+ Image image = RendererFactory::GetBrokenRendererImage();
+ if( mImpl->mRenderer )
{
- mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
- ApplyImageToSampler();
- }
- else
- {
- mImage.Reset();
+ ApplyImageToSampler( image );
}
}
}
-void ImageRenderer::SetImage( Image image )
+void ImageRenderer::SetTextureRectUniform( const Vector4& textureRect )
{
- if( mImage != image )
+ if( mImpl->mRenderer )
{
- mImageUrl.clear();
- mDesiredSize = ImageDimensions();
- mFittingMode = FittingMode::DEFAULT;
- mSamplingMode = SamplingMode::DEFAULT;
- mImage = image;
-
- if( mImage && mImpl->mIsOnStage )
+ Property::Index index = mImpl->mRenderer.GetPropertyIndex( TEXTURE_RECT_UNIFORM_NAME );
+ if( index == Property::INVALID_INDEX )
+ {
+ index = mImpl->mRenderer.RegisterProperty( TEXTURE_RECT_UNIFORM_NAME, textureRect );
+ }
+ else
{
- ApplyImageToSampler();
+ mImpl->mRenderer.SetProperty( index, textureRect );
}
}
}
-Image ImageRenderer::GetImage() const
+void ImageRenderer::CleanCache(const std::string& url)
{
- return mImage;
-}
-
-void ImageRenderer::ApplyImageToSampler()
-{
- if( mImage )
+ Material material = mImpl->mRenderer.GetMaterial();
+ mImpl->mRenderer.Reset();
+ if( mFactoryCache.CleanRendererCache( url ) )
{
- 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;
- }
- }
-
- Sampler sampler = Sampler::New( mImage, TEXTURE_UNIFORM_NAME );
- material.AddSampler( sampler );
- }
+ mAtlasManager.Remove( material, mTextureRect );
}
}