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