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