2 * Copyright (c) 2014 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 <dali/internal/event/actors/image-actor-impl.h>
22 #include <cstring> // for strcmp
25 #include <dali/public-api/animation/constraints.h> // for EqualToConstraint
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/devel-api/scripting/scripting.h>
28 #include <dali/internal/event/animation/constraint-impl.h>
29 #include <dali/internal/event/common/property-helper.h>
30 #include <dali/internal/event/effects/shader-effect-impl.h>
31 #include <dali/internal/event/images/image-connector.h>
32 #include <dali/internal/event/images/nine-patch-image-impl.h>
45 // Name Type writable animatable constraint-input enum for index-checking
46 DALI_PROPERTY_TABLE_BEGIN
47 DALI_PROPERTY( "pixelArea", RECTANGLE, true, false, true, Dali::ImageActor::Property::PIXEL_AREA )
48 DALI_PROPERTY( "style", STRING, true, false, true, Dali::ImageActor::Property::STYLE )
49 DALI_PROPERTY( "border", VECTOR4, true, false, true, Dali::ImageActor::Property::BORDER )
50 DALI_PROPERTY( "image", MAP, true, false, false, Dali::ImageActor::Property::IMAGE )
51 DALI_PROPERTY_TABLE_END( DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX )
55 return Dali::ImageActor::New();
58 TypeRegistration mType( typeid( Dali::ImageActor ), typeid( Dali::Actor ), Create );
63 Vector2 mTextureCoord;
66 GeometryPtr CreateGeometry( unsigned int gridWidth, unsigned int gridHeight )
69 std::vector< Vector2 > vertices;
70 vertices.reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
72 for( unsigned int y = 0u; y < gridHeight + 1; ++y )
74 float yPos = (float)y / gridHeight;
75 for( unsigned int x = 0u; x < gridWidth + 1; ++x )
77 float xPos = (float)x / gridWidth;
78 vertices.push_back( Vector2( xPos - 0.5f, yPos - 0.5f ) );
83 Vector< unsigned int > indices;
84 indices.Reserve( ( gridWidth + 2 ) * gridHeight * 2 - 2);
86 for( unsigned int row = 0u; row < gridHeight; ++row )
88 unsigned int rowStartIndex = row*(gridWidth+1u);
89 unsigned int nextRowStartIndex = rowStartIndex + gridWidth +1u;
91 if( row != 0u ) // degenerate index on non-first row
93 indices.PushBack( rowStartIndex );
96 for( unsigned int column = 0u; column < gridWidth+1u; column++) // main strip
98 indices.PushBack( rowStartIndex + column);
99 indices.PushBack( nextRowStartIndex + column);
102 if( row != gridHeight-1u ) // degenerate index on non-last row
104 indices.PushBack( nextRowStartIndex + gridWidth );
109 Property::Map vertexFormat;
110 vertexFormat[ "aPosition" ] = Property::VECTOR2;
111 PropertyBufferPtr vertexPropertyBuffer = PropertyBuffer::New();
112 vertexPropertyBuffer->SetFormat( vertexFormat );
113 vertexPropertyBuffer->SetSize( vertices.size() );
114 if( vertices.size() > 0 )
116 vertexPropertyBuffer->SetData( &vertices[ 0 ] );
119 Property::Map indexFormat;
120 indexFormat[ "indices" ] = Property::INTEGER;
121 PropertyBufferPtr indexPropertyBuffer = PropertyBuffer::New();
122 indexPropertyBuffer->SetFormat( indexFormat );
123 indexPropertyBuffer->SetSize( indices.Size() );
124 if( indices.Size() > 0 )
126 indexPropertyBuffer->SetData( &indices[ 0 ] );
129 // Create the geometry object
130 GeometryPtr geometry = Geometry::New();
131 geometry->AddVertexBuffer( *vertexPropertyBuffer );
132 geometry->SetIndexBuffer( *indexPropertyBuffer );
133 geometry->SetGeometryType( Dali::Geometry::TRIANGLE_STRIP );
139 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
140 attribute mediump vec2 aPosition;\n
141 varying mediump vec2 vTexCoord;\n
142 uniform mediump mat4 uMvpMatrix;\n
143 uniform mediump vec3 uSize;\n
144 uniform mediump vec4 uTextureRect;\n
148 gl_Position = uMvpMatrix * vec4(aPosition*uSize.xy, 0.0, 1.0);\n
149 vTexCoord = mix( uTextureRect.xy, uTextureRect.zw, aPosition + vec2(0.5));\n
153 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
154 varying mediump vec2 vTexCoord;\n
155 uniform sampler2D sTexture;\n
156 uniform lowp vec4 uColor;\n
160 gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
164 const size_t INVALID_TEXTURE_ID = (size_t)-1;
165 const int INVALID_RENDERER_ID = -1;
166 const uint16_t MAXIMUM_GRID_SIZE = 2048;
169 ImageActorPtr ImageActor::New()
171 ImageActorPtr actor( new ImageActor );
173 // Second-phase construction of base class
176 //Create the renderer
177 actor->mRenderer = Renderer::New();
179 GeometryPtr quad = CreateGeometry( 1u, 1u );
180 actor->mRenderer->SetGeometry( *quad );
182 ShaderPtr shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER, Dali::Shader::HINT_NONE );
183 MaterialPtr material = Material::New();
184 material->SetShader( *shader );
185 actor->mRenderer->SetMaterial( *material );
190 void ImageActor::OnInitialize()
192 // TODO: Remove this, at the moment its needed for size negotiation to work
193 SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
196 void ImageActor::SetImage( ImagePtr& image )
200 if( mRendererIndex != INVALID_RENDERER_ID )
202 RemoveRenderer( mRendererIndex );
203 mRendererIndex = INVALID_RENDERER_ID;
208 SamplerPtr sampler = Sampler::New();
209 sampler->SetFilterMode( mMinFilter, mMagFilter );
211 mTextureIndex = mRenderer->GetMaterial()->AddTexture( image, "sTexture", sampler );
213 if( mRendererIndex == INVALID_RENDERER_ID )
215 mRendererIndex = AddRenderer( *mRenderer );
218 if( !mIsPixelAreaSet )
220 mPixelArea = PixelArea( 0, 0, image->GetWidth(), image->GetHeight() );
228 ImagePtr ImageActor::GetImage() const
230 return mRenderer->GetMaterial()->GetTexture( mTextureIndex );
233 void ImageActor::SetPixelArea( const PixelArea& pixelArea )
235 mPixelArea = pixelArea;
236 mIsPixelAreaSet = true;
242 const ImageActor::PixelArea& ImageActor::GetPixelArea() const
247 bool ImageActor::IsPixelAreaSet() const
249 return mIsPixelAreaSet;
252 void ImageActor::ClearPixelArea()
254 mIsPixelAreaSet = false;
258 ImagePtr image = GetImage();
261 imageWidth = image->GetWidth();
262 imageHeight = image->GetHeight();
265 mPixelArea = PixelArea( 0, 0, imageWidth, imageHeight );
271 ImageActor::ImageActor()
272 : Actor( Actor::BASIC ),
274 mRendererIndex( INVALID_RENDERER_ID ),
275 mTextureIndex( INVALID_TEXTURE_ID ),
276 mEffectTextureIndex( INVALID_TEXTURE_ID ),
277 mMinFilter( FilterMode::DEFAULT ),
278 mMagFilter( FilterMode::DEFAULT ),
279 mIsPixelAreaSet( false )
283 ImageActor::~ImageActor()
287 Vector3 ImageActor::GetNaturalSize() const
289 Vector2 naturalSize( CalculateNaturalSize() );
290 return Vector3( naturalSize.width, naturalSize.height, 0.f );
293 Vector2 ImageActor::CalculateNaturalSize() const
295 // if no image then natural size is 0
296 Vector2 size( 0.0f, 0.0f );
298 ImagePtr image = GetImage();
301 if( IsPixelAreaSet() )
303 PixelArea area(GetPixelArea());
304 size.width = area.width;
305 size.height = area.height;
309 size = image->GetNaturalSize();
316 void ImageActor::UpdateGeometry()
318 uint16_t gridWidth = 1u;
319 uint16_t gridHeight = 1u;
323 Vector2 gridSize = mShaderEffect->GetGridSize( Vector2(mPixelArea.width, mPixelArea.height) );
325 //limit the grid size
326 gridWidth = std::min( MAXIMUM_GRID_SIZE, static_cast<uint16_t>(gridSize.width) );
327 gridHeight = std::min( MAXIMUM_GRID_SIZE, static_cast<uint16_t>(gridSize.height) );
330 if( gridWidth != mGridSize.GetWidth() || gridHeight != mGridSize.GetHeight() )
332 mGridSize.SetWidth( gridWidth );
333 mGridSize.SetHeight( gridHeight );
335 GeometryPtr geometry = CreateGeometry( gridWidth, gridHeight );
336 mRenderer->SetGeometry( *geometry );
339 void ImageActor::UpdateTexureRect()
341 Vector4 textureRect( 0.f, 0.f, 1.f, 1.f );
343 ImagePtr image = GetImage();
344 if( mIsPixelAreaSet && image )
346 const float uScale = 1.0f / float(image->GetWidth());
347 const float vScale = 1.0f / float(image->GetHeight());
349 textureRect.x = uScale * float(mPixelArea.x);
350 textureRect.y = vScale * float(mPixelArea.y);
352 textureRect.z = uScale * float(mPixelArea.x + mPixelArea.width);
353 textureRect.w = vScale * float(mPixelArea.y + mPixelArea.height);
356 Material* material = mRenderer->GetMaterial();
357 material->RegisterProperty( "uTextureRect", textureRect );
360 unsigned int ImageActor::GetDefaultPropertyCount() const
362 return Actor::GetDefaultPropertyCount() + DEFAULT_PROPERTY_COUNT;
365 void ImageActor::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
367 Actor::GetDefaultPropertyIndices( indices ); // Actor class properties
369 indices.Reserve( indices.Size() + DEFAULT_PROPERTY_COUNT );
371 int index = DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
372 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i, ++index )
374 indices.PushBack( index );
378 bool ImageActor::IsDefaultPropertyWritable( Property::Index index ) const
380 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
382 return Actor::IsDefaultPropertyWritable(index);
385 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
386 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
388 return DEFAULT_PROPERTY_DETAILS[ index ].writable;
394 bool ImageActor::IsDefaultPropertyAnimatable( Property::Index index ) const
396 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
398 return Actor::IsDefaultPropertyAnimatable( index );
401 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
402 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
404 return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
410 bool ImageActor::IsDefaultPropertyAConstraintInput( Property::Index index ) const
412 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
414 return Actor::IsDefaultPropertyAConstraintInput( index );
417 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
418 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
420 return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
426 Property::Type ImageActor::GetDefaultPropertyType( Property::Index index ) const
428 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
430 return Actor::GetDefaultPropertyType( index );
433 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
434 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
436 return DEFAULT_PROPERTY_DETAILS[index].type;
439 // index out-of-bounds
440 return Property::NONE;
443 const char* ImageActor::GetDefaultPropertyName( Property::Index index ) const
445 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
447 return Actor::GetDefaultPropertyName(index);
450 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
451 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
453 return DEFAULT_PROPERTY_DETAILS[index].name;
456 // index out-of-bounds
460 Property::Index ImageActor::GetDefaultPropertyIndex(const std::string& name) const
462 Property::Index index = Property::INVALID_INDEX;
464 // Look for name in default properties
465 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
467 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
468 if( 0 == strcmp( name.c_str(), property->name ) ) // Don't want to convert rhs to string
470 index = i + DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
475 // If not found, check in base class
476 if( Property::INVALID_INDEX == index )
478 index = Actor::GetDefaultPropertyIndex( name );
483 void ImageActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
485 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
487 Actor::SetDefaultProperty( index, propertyValue );
493 case Dali::ImageActor::Property::PIXEL_AREA:
495 SetPixelArea(propertyValue.Get<Rect<int> >());
498 case Dali::ImageActor::Property::STYLE:
503 case Dali::ImageActor::Property::BORDER:
508 case Dali::ImageActor::Property::IMAGE:
510 Dali::Image img = Scripting::NewImage( propertyValue );
513 ImagePtr image( &GetImplementation(img) );
518 DALI_LOG_WARNING("Cannot create image from property value\n");
524 DALI_LOG_WARNING("Unknown property (%d)\n", index);
532 Property::Value ImageActor::GetDefaultProperty( Property::Index index ) const
535 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
537 ret = Actor::GetDefaultProperty( index );
543 case Dali::ImageActor::Property::PIXEL_AREA:
545 Rect<int> r = GetPixelArea();
549 case Dali::ImageActor::Property::STYLE:
554 case Dali::ImageActor::Property::BORDER:
559 case Dali::ImageActor::Property::IMAGE:
562 Scripting::CreatePropertyMap( Dali::Image( GetImage().Get() ), map );
563 ret = Property::Value( map );
568 DALI_LOG_WARNING( "Unknown property (%d)\n", index );
577 void ImageActor::SetSortModifier(float modifier)
579 mRenderer->SetDepthIndex( modifier );
582 float ImageActor::GetSortModifier() const
584 return mRenderer->GetDepthIndex();
587 void ImageActor::SetCullFace(CullFaceMode mode)
589 mRenderer->GetMaterial()->SetFaceCullingMode( static_cast< Dali::Material::FaceCullingMode >( mode ) );
592 CullFaceMode ImageActor::GetCullFace() const
594 return static_cast< CullFaceMode >( mRenderer->GetMaterial()->GetFaceCullingMode() );
597 void ImageActor::SetBlendMode( BlendingMode::Type mode )
599 mRenderer->GetMaterial()->SetBlendMode( mode );
602 BlendingMode::Type ImageActor::GetBlendMode() const
604 return mRenderer->GetMaterial()->GetBlendMode();
607 void ImageActor::SetBlendFunc( BlendingFactor::Type srcFactorRgba, BlendingFactor::Type destFactorRgba )
609 mRenderer->GetMaterial()->SetBlendFunc( srcFactorRgba, destFactorRgba, srcFactorRgba, destFactorRgba );
612 void ImageActor::SetBlendFunc( BlendingFactor::Type srcFactorRgb, BlendingFactor::Type destFactorRgb,
613 BlendingFactor::Type srcFactorAlpha, BlendingFactor::Type destFactorAlpha )
615 mRenderer->GetMaterial()->SetBlendFunc( srcFactorRgb, destFactorRgb, srcFactorAlpha, destFactorAlpha );
618 void ImageActor::GetBlendFunc( BlendingFactor::Type& srcFactorRgb, BlendingFactor::Type& destFactorRgb,
619 BlendingFactor::Type& srcFactorAlpha, BlendingFactor::Type& destFactorAlpha ) const
621 mRenderer->GetMaterial()->GetBlendFunc( srcFactorRgb, destFactorRgb, srcFactorAlpha, destFactorAlpha );
624 void ImageActor::SetBlendEquation( BlendingEquation::Type equationRgba )
626 mRenderer->GetMaterial()->SetBlendEquation( equationRgba, equationRgba );
629 void ImageActor::SetBlendEquation( BlendingEquation::Type equationRgb, BlendingEquation::Type equationAlpha )
631 mRenderer->GetMaterial()->SetBlendEquation( equationRgb, equationAlpha );
634 void ImageActor::GetBlendEquation( BlendingEquation::Type& equationRgb, BlendingEquation::Type& equationAlpha ) const
636 mRenderer->GetMaterial()->GetBlendEquation( equationRgb, equationAlpha );
639 void ImageActor::SetBlendColor( const Vector4& color )
642 mRenderer->GetMaterial()->SetBlendColor( mBlendColor );
645 const Vector4& ImageActor::GetBlendColor() const
650 void ImageActor::SetFilterMode( FilterMode::Type minFilter, FilterMode::Type magFilter )
652 mMinFilter = minFilter;
653 mMagFilter = magFilter;
655 if( mTextureIndex != INVALID_TEXTURE_ID )
657 SamplerPtr sampler = Sampler::New();
658 sampler->SetFilterMode( minFilter, magFilter );
660 mRenderer->GetMaterial()->SetTextureSampler( mTextureIndex, sampler.Get() );
664 void ImageActor::GetFilterMode( FilterMode::Type& minFilter, FilterMode::Type& magFilter ) const
666 minFilter = mMinFilter;
667 magFilter = mMagFilter;
670 void ImageActor::SetShaderEffect( ShaderEffect& effect )
674 mShaderEffect->Disconnect( this );
677 mShaderEffect = ShaderEffectPtr( &effect );
678 effect.Connect( this );
680 ShaderPtr shader = mShaderEffect->GetShader();
681 mRenderer->GetMaterial()->SetShader( *shader );
683 EffectImageUpdated();
688 ShaderEffectPtr ImageActor::GetShaderEffect() const
690 return mShaderEffect;
693 void ImageActor::RemoveShaderEffect()
697 mShaderEffect->Disconnect( this );
698 // change to the standard shader and quad geometry
699 ShaderPtr shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER, Dali::Shader::HINT_NONE );
700 mRenderer->GetMaterial()->SetShader( *shader );
701 mShaderEffect.Reset();
707 void ImageActor::EffectImageUpdated()
711 Dali::Image effectImage = mShaderEffect->GetEffectImage();
714 Image& effectImageImpl = GetImplementation( effectImage );
716 if( mEffectTextureIndex == INVALID_TEXTURE_ID )
718 mEffectTextureIndex = mRenderer->GetMaterial()->AddTexture( &effectImageImpl, "sEffect", NULL );
722 mRenderer->GetMaterial()->SetTextureImage( mEffectTextureIndex, &effectImageImpl );
727 if( mEffectTextureIndex != INVALID_TEXTURE_ID )
729 mRenderer->GetMaterial()->RemoveTexture( mEffectTextureIndex );
731 mEffectTextureIndex = INVALID_TEXTURE_ID;
737 } // namespace Internal