2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "image-renderer.h"
22 #include <cstring> // for strncasecmp
23 #include <dali/public-api/images/resource-image.h>
24 #include <dali/public-api/images/native-image.h>
25 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/internal/controls/renderers/renderer-string-constants.h>
29 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
30 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
31 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
32 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
33 #include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
46 const char HTTP_URL[] = "http://";
47 const char HTTPS_URL[] = "https://";
50 const char * const IMAGE_FITTING_MODE( "fittingMode" );
51 const char * const IMAGE_SAMPLING_MODE( "samplingMode" );
52 const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" );
53 const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
56 const char * const SHRINK_TO_FIT("SHRINK_TO_FIT");
57 const char * const SCALE_TO_FILL("SCALE_TO_FILL");
58 const char * const FIT_WIDTH("FIT_WIDTH");
59 const char * const FIT_HEIGHT("FIT_HEIGHT");
60 const char * const DEFAULT("DEFAULT");
63 const char * const BOX("BOX");
64 const char * const NEAREST("NEAREST");
65 const char * const LINEAR("LINEAR");
66 const char * const BOX_THEN_NEAREST("BOX_THEN_NEAREST");
67 const char * const BOX_THEN_LINEAR("BOX_THEN_LINEAR");
68 const char * const NO_FILTER("NO_FILTER");
69 const char * const DONT_CARE("DONT_CARE");
71 const std::string PIXEL_AREA_UNIFORM_NAME = "pixelArea";
73 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
75 const char* DEFAULT_SAMPLER_TYPENAME = "sampler2D";
77 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
78 attribute mediump vec2 aPosition;\n
79 uniform mediump mat4 uMvpMatrix;\n
80 uniform mediump vec3 uSize;\n
81 uniform mediump vec4 uAtlasRect;\n
82 uniform mediump vec4 pixelArea;
83 varying mediump vec2 vTexCoord;\n
87 mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
88 vertexPosition.xyz *= uSize;\n
89 vertexPosition = uMvpMatrix * vertexPosition;\n
91 vTexCoord = mix( uAtlasRect.xy, uAtlasRect.zw, pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) ) );\n
92 gl_Position = vertexPosition;\n
96 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
97 varying mediump vec2 vTexCoord;\n
98 uniform sampler2D sTexture;\n
99 uniform lowp vec4 uColor;\n
103 gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
107 Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize )
111 if( gridSize == ImageDimensions( 1, 1 ) )
113 geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
116 geometry = RendererFactoryCache::CreateQuadGeometry();
117 factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
122 geometry = RendererFactoryCache::CreateGridGeometry( gridSize );
128 } //unnamed namespace
130 ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
131 : ControlRenderer( factoryCache ),
132 mAtlasManager( atlasManager ),
134 mFittingMode( FittingMode::DEFAULT ),
135 mSamplingMode( SamplingMode::DEFAULT ),
136 mNativeFragmentShaderCode( ),
137 mNativeImageFlag( false )
141 ImageRenderer::~ImageRenderer()
145 void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
147 std::string oldImageUrl = mImageUrl;
149 Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
152 imageURLValue->Get( mImageUrl );
153 if( !mImageUrl.empty() )
158 Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
162 fittingValue->Get( fitting );
164 mFittingMode = FittingMode::DEFAULT;
165 if( fitting == SHRINK_TO_FIT )
167 mFittingMode = FittingMode::SHRINK_TO_FIT;
169 else if( fitting == SCALE_TO_FILL )
171 mFittingMode = FittingMode::SCALE_TO_FILL;
173 else if( fitting == FIT_WIDTH )
175 mFittingMode = FittingMode::FIT_WIDTH;
177 else if( fitting == FIT_HEIGHT )
179 mFittingMode = FittingMode::FIT_HEIGHT;
181 else if( fitting == DEFAULT )
183 mFittingMode = FittingMode::DEFAULT;
187 DALI_ASSERT_ALWAYS("Unknown fitting mode");
191 Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
194 std::string sampling;
195 samplingValue->Get( sampling );
197 mSamplingMode = SamplingMode::DEFAULT;
198 if( sampling == BOX )
200 mSamplingMode = SamplingMode::BOX;
202 else if( sampling == NEAREST )
204 mSamplingMode = SamplingMode::NEAREST;
206 else if( sampling == LINEAR )
208 mSamplingMode = SamplingMode::LINEAR;
210 else if( sampling == BOX_THEN_NEAREST )
212 mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
214 else if( sampling == BOX_THEN_LINEAR )
216 mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
218 else if( sampling == NO_FILTER )
220 mSamplingMode = SamplingMode::NO_FILTER;
222 else if( sampling == DONT_CARE )
224 mSamplingMode = SamplingMode::DONT_CARE;
226 else if( sampling == DEFAULT )
228 mSamplingMode = SamplingMode::DEFAULT;
232 DALI_ASSERT_ALWAYS("Unknown sampling mode");
236 int desiredWidth = 0;
237 Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
238 if( desiredWidthValue )
240 desiredWidthValue->Get( desiredWidth );
243 int desiredHeight = 0;
244 Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
245 if( desiredHeightValue )
247 desiredHeightValue->Get( desiredHeight );
250 mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
253 // remove old renderer if exit
254 if( mImpl->mRenderer )
256 if( actor ) //remove old renderer from actor
258 actor.RemoveRenderer( mImpl->mRenderer );
260 if( !oldImageUrl.empty() ) //clean old renderer from cache
262 CleanCache( oldImageUrl );
266 NativeImage nativeImage = NativeImage::DownCast( mImage );
270 SetNativeFragmentShaderCode( nativeImage );
273 // if actor is on stage, create new renderer and apply to actor
274 if( actor && actor.OnStage() )
280 void ImageRenderer::SetSize( const Vector2& size )
282 ControlRenderer::SetSize( size );
285 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
289 naturalSize.x = mImage.GetWidth();
290 naturalSize.y = mImage.GetHeight();
293 else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
295 naturalSize.x = mDesiredSize.GetWidth();
296 naturalSize.y = mDesiredSize.GetHeight();
299 else if( !mImageUrl.empty() )
301 ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
302 naturalSize.x = dimentions.GetWidth();
303 naturalSize.y = dimentions.GetHeight();
307 naturalSize = Vector2::ZERO;
310 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
312 ControlRenderer::SetClipRect( clipRect );
315 void ImageRenderer::SetOffset( const Vector2& offset )
319 Renderer ImageRenderer::CreateRenderer() const
324 // If mImage is nativeImage with custom sampler or prefix, mNativeFragmentShaderCode will be applied.
325 // Renderer can't be shared between NativeImage and other image types.
326 if( !mNativeFragmentShaderCode.empty() )
328 return CreateNativeImageRenderer();
331 if( !mImpl->mCustomShader )
333 geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
335 shader = GetImageShader(mFactoryCache);
339 geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
340 if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
342 shader = GetImageShader(mFactoryCache);
346 shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
347 mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
348 mImpl->mCustomShader->mHints );
349 if( mImpl->mCustomShader->mVertexShader.empty() )
351 shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
352 shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
357 TextureSet textureSet = TextureSet::New();
359 Renderer renderer = Renderer::New( geometry, shader );
360 renderer.SetTextures( textureSet );
365 Renderer ImageRenderer::CreateNativeImageRenderer() const
370 if( !mImpl->mCustomShader )
372 geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
374 shader = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
375 shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
376 shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
380 geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
381 if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
383 shader = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
387 shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
388 mNativeFragmentShaderCode,
389 mImpl->mCustomShader->mHints );
390 if( mImpl->mCustomShader->mVertexShader.empty() )
392 shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
393 shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
398 TextureSet textureSet = TextureSet::New();
399 Renderer renderer = Renderer::New( geometry, shader );
400 renderer.SetTextures( textureSet );
405 void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
407 if( imageUrl.empty() )
412 mImageUrl = imageUrl;
413 mImpl->mRenderer.Reset();
415 if( !mImpl->mCustomShader &&
416 ( strncasecmp( imageUrl.c_str(), HTTP_URL, sizeof(HTTP_URL) -1 ) != 0 ) && // ignore remote images
417 ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
419 mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
420 if( !mImpl->mRenderer )
423 TextureSet textureSet = mAtlasManager.Add(atlasRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
426 Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
427 Shader shader( GetImageShader(mFactoryCache) );
428 mImpl->mRenderer = Renderer::New( geometry, shader );
429 mImpl->mRenderer.SetTextures( textureSet );
430 mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
432 else // big image, atlasing is not applied
434 mImpl->mRenderer = CreateRenderer();
436 ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
437 image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
438 TextureSet textureSet = mImpl->mRenderer.GetTextures();
439 textureSet.SetImage( 0u, image );
442 mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
445 mImpl->mFlags |= Impl::IS_FROM_CACHE;
449 // for custom shader or remote image, renderer is not cached and atlas is not applied
451 mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
452 mImpl->mRenderer = CreateRenderer();
453 ResourceImage resourceImage = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
454 resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
455 ApplyImageToSampler( resourceImage );
459 void ImageRenderer::InitializeRenderer( const Image& image )
461 mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
463 mImpl->mRenderer = CreateRenderer();
467 ApplyImageToSampler( image );
472 void ImageRenderer::DoSetOnStage( Actor& actor )
474 if( !mImageUrl.empty() )
476 InitializeRenderer( mImageUrl );
480 InitializeRenderer( mImage );
485 void ImageRenderer::DoSetOffStage( Actor& actor )
487 //If we own the image then make sure we release it when we go off stage
488 if( !mImageUrl.empty() )
490 actor.RemoveRenderer( mImpl->mRenderer );
491 CleanCache(mImageUrl);
496 actor.RemoveRenderer( mImpl->mRenderer );
497 mImpl->mRenderer.Reset();
501 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
504 map.Insert( RENDERER_TYPE, IMAGE_RENDERER );
505 if( !mImageUrl.empty() )
507 map.Insert( IMAGE_URL_NAME, mImageUrl );
508 map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
509 map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
513 map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
514 map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
516 ResourceImage resourceImage = ResourceImage::DownCast(mImage);
519 map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
523 switch( mFittingMode )
525 case Dali::FittingMode::FIT_HEIGHT:
527 map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
530 case Dali::FittingMode::FIT_WIDTH:
532 map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
535 case Dali::FittingMode::SCALE_TO_FILL:
537 map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
540 case Dali::FittingMode::SHRINK_TO_FIT:
542 map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
547 map.Insert( IMAGE_FITTING_MODE, DEFAULT );
552 switch( mSamplingMode )
554 case Dali::SamplingMode::BOX:
556 map.Insert( IMAGE_SAMPLING_MODE, BOX );
559 case Dali::SamplingMode::NEAREST:
561 map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
564 case Dali::SamplingMode::LINEAR:
566 map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
569 case Dali::SamplingMode::BOX_THEN_LINEAR:
571 map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
574 case Dali::SamplingMode::BOX_THEN_NEAREST:
576 map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
579 case Dali::SamplingMode::NO_FILTER:
581 map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
584 case Dali::SamplingMode::DONT_CARE:
586 map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
591 map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
597 Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
599 Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
602 shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
603 factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
604 shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
605 shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
610 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
612 if( mImageUrl != imageUrl )
614 std::string oldImageUrl = mImageUrl;
615 mImageUrl = imageUrl;
617 mFittingMode = fittingMode;
618 mSamplingMode = samplingMode;
621 if( mImpl->mRenderer )
623 if( GetIsFromCache() ) // if renderer is from cache, remove the old one
625 //remove old renderer
628 actor.RemoveRenderer( mImpl->mRenderer );
632 if( !oldImageUrl.empty() )
634 CleanCache(oldImageUrl);
637 if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
642 else // if renderer is not from cache, reuse the same renderer and only change the texture
644 ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
645 image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
646 ApplyImageToSampler( image );
652 void ImageRenderer::SetImage( Actor& actor, const Image& image )
654 if( mImage != image )
656 NativeImage newNativeImage = NativeImage::DownCast( image );
657 bool newRendererFlag = true;
659 if( newNativeImage && !mNativeImageFlag )
661 SetNativeFragmentShaderCode( newNativeImage );
664 if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) )
666 newRendererFlag = false;
671 mNativeImageFlag = true;
675 mNativeFragmentShaderCode.clear();
676 mNativeImageFlag = false;
681 if( mImpl->mRenderer )
683 // if renderer is from cache, remove the old one, and create new renderer
684 if( GetIsFromCache() )
686 //remove old renderer
689 actor.RemoveRenderer( mImpl->mRenderer );
693 if( !mImageUrl.empty() )
695 CleanCache(mImageUrl);
699 if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
704 // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer
705 else if( newRendererFlag )
707 //remove old renderer
710 actor.RemoveRenderer( mImpl->mRenderer );
713 if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
718 else // if renderer is not from cache, reuse the same renderer and only change the texture
720 ApplyImageToSampler( image );
725 mDesiredSize = ImageDimensions();
726 mFittingMode = FittingMode::DEFAULT;
727 mSamplingMode = SamplingMode::DEFAULT;
731 void ImageRenderer::ApplyImageToSampler( const Image& image )
735 TextureSet textureSet = mImpl->mRenderer.GetTextures();
738 textureSet.SetImage( 0u, image );
743 void ImageRenderer::OnImageLoaded( ResourceImage image )
745 if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
747 Image brokenImage = RendererFactory::GetBrokenRendererImage();
748 if( mImpl->mRenderer )
750 ApplyImageToSampler( brokenImage );
755 void ImageRenderer::CleanCache(const std::string& url)
757 TextureSet textureSet = mImpl->mRenderer.GetTextures();
759 Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
760 Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
761 if( index != Property::INVALID_INDEX )
763 Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
764 atlasRectValue.Get( atlasRect );
767 mImpl->mRenderer.Reset();
768 if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
770 mAtlasManager.Remove( textureSet, atlasRect );
774 void ImageRenderer::SetNativeFragmentShaderCode( Dali::NativeImage& nativeImage )
776 const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix();
777 const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename();
781 mNativeFragmentShaderCode = fragmentPreFix;
782 mNativeFragmentShaderCode += "\n";
785 if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() )
787 mNativeFragmentShaderCode += mImpl->mCustomShader->mFragmentShader;
791 mNativeFragmentShaderCode += FRAGMENT_SHADER;
794 if( customSamplerTypename )
796 mNativeFragmentShaderCode.replace( mNativeFragmentShaderCode.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename );
801 } // namespace Internal
803 } // namespace Toolkit