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