[Tizen] Move constants string to visual-string-constants file
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / svg / svg-visual.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "svg-visual.h"
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/third-party/nanosvg/nanosvg.h>
23 #include <dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h>
24 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
25 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
26 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
27 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
28 #include <dali/devel-api/adaptor-framework/file-loader.h>
29
30 // EXTERNAL INCLUDES
31 #include <dali/public-api/common/stage.h>
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Internal
40 {
41
42 namespace
43 {
44 // property name
45 const char * const UNITS("px");
46
47 const Dali::Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
48 }
49
50 SvgVisualPtr SvgVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties )
51 {
52   SvgVisualPtr svgVisual( new SvgVisual( factoryCache, shaderFactory, imageUrl ) );
53   svgVisual->ParseFromUrl( imageUrl );
54   svgVisual->SetProperties( properties );
55
56   return svgVisual;
57 }
58
59 SvgVisualPtr SvgVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl )
60 {
61   SvgVisualPtr svgVisual( new SvgVisual( factoryCache, shaderFactory, imageUrl ) );
62   svgVisual->ParseFromUrl( imageUrl );
63
64   return svgVisual;
65 }
66
67 SvgVisual::SvgVisual( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl )
68 : Visual::Base( factoryCache, Visual::FittingMode::FILL ),
69   mImageVisualShaderFactory( shaderFactory ),
70   mAtlasRect( FULL_TEXTURE_RECT ),
71   mImageUrl( imageUrl ),
72   mParsedImage( NULL ),
73   mPlacementActor(),
74   mVisualSize(Vector2::ZERO),
75   mAttemptAtlasing( false )
76 {
77   // the rasterized image is with pre-multiplied alpha format
78   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
79 }
80
81 SvgVisual::~SvgVisual()
82 {
83   if( mParsedImage )
84   {
85     nsvgDelete( mParsedImage );
86   }
87 }
88
89 void SvgVisual::DoSetProperties( const Property::Map& propertyMap )
90 {
91   // url already passed in from constructor
92   for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
93   {
94     KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
95     if( keyValue.first.type == Property::Key::INDEX )
96     {
97       DoSetProperty( keyValue.first.indexKey, keyValue.second );
98     }
99     else if( keyValue.first == IMAGE_ATLASING )
100     {
101       DoSetProperty( Toolkit::ImageVisual::Property::ATLASING, keyValue.second );
102     }
103   }
104 }
105
106 void SvgVisual::DoSetProperty( Property::Index index, const Property::Value& value )
107 {
108   switch( index )
109   {
110     case Toolkit::ImageVisual::Property::ATLASING:
111     {
112       value.Get( mAttemptAtlasing );
113       break;
114     }
115   }
116 }
117
118 void SvgVisual::DoSetOnStage( Actor& actor )
119 {
120   Shader shader;
121   if( !mImpl->mCustomShader )
122   {
123     shader = mImageVisualShaderFactory.GetShader( mFactoryCache, mAttemptAtlasing, true, false );
124   }
125   else
126   {
127     shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? mImageVisualShaderFactory.GetVertexShaderSource() : mImpl->mCustomShader->mVertexShader,
128                           mImpl->mCustomShader->mFragmentShader.empty() ? mImageVisualShaderFactory.GetFragmentShaderSource() : mImpl->mCustomShader->mFragmentShader,
129                           mImpl->mCustomShader->mHints );
130
131     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
132   }
133
134   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
135   TextureSet textureSet = TextureSet::New();
136   mImpl->mRenderer = Renderer::New( geometry, shader );
137   mImpl->mRenderer.SetTextures( textureSet );
138
139   // Register transform properties
140   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
141
142   // Defer the rasterisation task until we get given a size (by Size Negotiation algorithm)
143
144   // Hold the weak handle of the placement actor and delay the adding of renderer until the svg rasterization is finished.
145   mPlacementActor = actor;
146
147   // SVG visual needs it's size set before it can be rasterized hence set ResourceReady once on stage
148   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
149 }
150
151 void SvgVisual::DoSetOffStage( Actor& actor )
152 {
153   mFactoryCache.GetSVGRasterizationThread()->RemoveTask( this );
154
155   actor.RemoveRenderer( mImpl->mRenderer );
156   mImpl->mRenderer.Reset();
157   mPlacementActor.Reset();
158
159   // Reset the visual size to zero so that when adding the actor back to stage the SVG rasterization is forced
160   mVisualSize = Vector2::ZERO;
161 }
162
163 void SvgVisual::GetNaturalSize( Vector2& naturalSize )
164 {
165   if( mParsedImage )
166   {
167     naturalSize.x = mParsedImage->width;
168     naturalSize.y = mParsedImage->height;
169   }
170   else
171   {
172     naturalSize = Vector2::ZERO;
173   }
174 }
175
176 void SvgVisual::DoCreatePropertyMap( Property::Map& map ) const
177 {
178   map.Clear();
179   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::SVG );
180   if( mImageUrl.IsValid() )
181   {
182     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
183     map.Insert( Toolkit::ImageVisual::Property::ATLASING, mAttemptAtlasing );
184   }
185 }
186
187 void SvgVisual::ParseFromUrl( const VisualUrl& imageUrl )
188 {
189   mImageUrl = imageUrl;
190   if( mImageUrl.IsLocalResource() )
191   {
192     Vector2 dpi = Stage::GetCurrent().GetDpi();
193     float meanDpi = ( dpi.height + dpi.width ) * 0.5f;
194     Dali::Vector<char> buffer;
195
196     mParsedImage = nsvgParseFromFile( mImageUrl.GetUrl().c_str(), UNITS, meanDpi );
197   }
198 }
199
200 void SvgVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
201 {
202   // Do nothing
203 }
204
205 void SvgVisual::AddRasterizationTask( const Vector2& size )
206 {
207   if( mImpl->mRenderer )
208   {
209     unsigned int width = static_cast<unsigned int>(size.width);
210     unsigned int height = static_cast<unsigned int>( size.height );
211
212     Vector2 dpi = Stage::GetCurrent().GetDpi();
213     float meanDpi = ( dpi.height + dpi.width ) * 0.5f;
214
215     RasterizingTaskPtr newTask = new RasterizingTask( this, mParsedImage, mImageUrl, meanDpi, width, height );
216     mFactoryCache.GetSVGRasterizationThread()->AddTask( newTask );
217   }
218 }
219
220 void SvgVisual::ApplyRasterizedImage( NSVGimage* parsedSvg, PixelData rasterizedPixelData )
221 {
222   if( mParsedImage == NULL)
223   {
224     mParsedImage = parsedSvg;
225   }
226
227   if( mParsedImage && IsOnStage() )
228   {
229     TextureSet currentTextureSet = mImpl->mRenderer.GetTextures();
230     if( mImpl->mFlags |= Impl::IS_ATLASING_APPLIED )
231     {
232       mFactoryCache.GetAtlasManager()->Remove( currentTextureSet, mAtlasRect );
233     }
234
235     TextureSet textureSet;
236
237     if( mAttemptAtlasing && !mImpl->mCustomShader )
238     {
239       Vector4 atlasRect;
240       textureSet = mFactoryCache.GetAtlasManager()->Add(atlasRect, rasterizedPixelData );
241       if( textureSet ) // atlasing
242       {
243         if( textureSet != currentTextureSet )
244         {
245           mImpl->mRenderer.SetTextures( textureSet );
246         }
247         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
248         mAtlasRect = atlasRect;
249         mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
250       }
251     }
252
253     if( !textureSet ) // no atlasing - mAttemptAtlasing is false or adding to atlas is failed
254     {
255       Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888,
256                                       rasterizedPixelData.GetWidth(), rasterizedPixelData.GetHeight() );
257       texture.Upload( rasterizedPixelData );
258       mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
259
260       if( mAtlasRect == FULL_TEXTURE_RECT )
261       {
262         textureSet = currentTextureSet;
263       }
264       else
265       {
266         textureSet = TextureSet::New();
267         mImpl->mRenderer.SetTextures( textureSet );
268
269         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
270         mAtlasRect = FULL_TEXTURE_RECT;
271       }
272
273       if( textureSet )
274       {
275         textureSet.SetTexture( 0, texture );
276       }
277     }
278
279     // Rasterized pixels are uploaded to texture. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
280     Actor actor = mPlacementActor.GetHandle();
281     if( actor )
282     {
283       actor.AddRenderer( mImpl->mRenderer );
284       // reset the weak handle so that the renderer only get added to actor once
285       mPlacementActor.Reset();
286     }
287
288    // Svg loaded and ready to display
289    ResourceReady( Toolkit::Visual::ResourceStatus::READY );
290   }
291   else if( !mParsedImage )
292   {
293     ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
294   }
295 }
296
297 void SvgVisual::OnSetTransform()
298 {
299   Vector2 visualSize = mImpl->mTransform.GetVisualSize( mImpl->mControlSize );
300
301   if( IsOnStage() )
302   {
303     if( visualSize != mVisualSize )
304     {
305       AddRasterizationTask( visualSize );
306       mVisualSize = visualSize;
307     }
308   }
309
310   if(mImpl->mRenderer)
311   {
312     mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
313   }
314 }
315
316 bool SvgVisual::IsResourceReady() const
317 {
318   return ( mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
319            mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED );
320 }
321
322 } // namespace Internal
323
324 } // namespace Toolkit
325
326 } // namespace Dali