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