2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "svg-visual.h"
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>
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>
50 const char * const UNITS("px");
52 const Dali::Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
56 SvgVisualPtr SvgVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties )
58 SvgVisualPtr svgVisual( new SvgVisual( factoryCache, shaderFactory, imageUrl ) );
60 svgVisual->ParseFromUrl( imageUrl );
61 svgVisual->SetProperties( properties );
64 svgVisual->SetProperties( properties );
65 #endif /* NO_THORVG */
70 SvgVisualPtr SvgVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl )
72 SvgVisualPtr svgVisual( new SvgVisual( factoryCache, shaderFactory, imageUrl ) );
74 svgVisual->ParseFromUrl( imageUrl );
77 #endif /* NO_THORVG */
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 ),
90 mVectorRenderer( VectorImageRenderer::New() ),
94 mLocalResource( true ),
95 #endif /* NO_THORVG */
97 mVisualSize(Vector2::ZERO),
98 mAttemptAtlasing( false )
100 // the rasterized image is with pre-multiplied alpha format
101 mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
104 SvgVisual::~SvgVisual()
109 nsvgDelete( mParsedImage );
111 #endif /* NO_THORVG */
114 void SvgVisual::DoSetProperties( const Property::Map& propertyMap )
116 // url already passed in from constructor
117 for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
119 KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
120 if( keyValue.first.type == Property::Key::INDEX )
122 DoSetProperty( keyValue.first.indexKey, keyValue.second );
124 else if( keyValue.first == IMAGE_ATLASING )
126 DoSetProperty( Toolkit::ImageVisual::Property::ATLASING, keyValue.second );
128 else if( keyValue.first == SYNCHRONOUS_LOADING )
130 DoSetProperty( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second );
135 void SvgVisual::DoSetProperty( Property::Index index, const Property::Value& value )
139 case Toolkit::ImageVisual::Property::ATLASING:
141 value.Get( mAttemptAtlasing );
144 case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
147 if( value.Get( sync ) )
151 mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
155 mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
160 DALI_LOG_ERROR("ImageVisual: synchronousLoading property has incorrect type\n");
167 void SvgVisual::DoSetOnScene( Actor& actor )
170 if( !mImpl->mCustomShader )
172 shader = mImageVisualShaderFactory.GetShader( mFactoryCache, mAttemptAtlasing, true, false );
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 );
180 shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
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 );
188 // Register transform properties
189 mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
191 // Defer the rasterisation task until we get given a size (by Size Negotiation algorithm)
193 // Hold the weak handle of the placement actor and delay the adding of renderer until the svg rasterization is finished.
194 mPlacementActor = actor;
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 );
200 void SvgVisual::DoSetOffScene( Actor& actor )
202 mFactoryCache.GetSVGRasterizationThread()->RemoveTask( this );
204 actor.RemoveRenderer( mImpl->mRenderer );
205 mImpl->mRenderer.Reset();
206 mPlacementActor.Reset();
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;
212 void SvgVisual::GetNaturalSize( Vector2& naturalSize )
217 naturalSize.x = mParsedImage->width;
218 naturalSize.y = mParsedImage->height;
220 #else /* NO_THORVG */
223 naturalSize.x = mDefaultWidth;
224 naturalSize.y = mDefaultHeight;
226 #endif /* NO_THORVG */
229 naturalSize = Vector2::ZERO;
233 void SvgVisual::DoCreatePropertyMap( Property::Map& map ) const
236 map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::SVG );
237 if( mImageUrl.IsValid() )
239 map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
240 map.Insert( Toolkit::ImageVisual::Property::ATLASING, mAttemptAtlasing );
242 map.Insert( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, IsSynchronousLoadingRequired() );
245 void SvgVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
251 void SvgVisual::ParseFromUrl( const VisualUrl& imageUrl )
253 mImageUrl = imageUrl;
254 if( mImageUrl.IsLocalResource() )
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 ) )
261 buffer.PushBack( '\0' );
262 mParsedImage = nsvgParse( buffer.Begin(), UNITS, meanDpi );
266 #else /* NO_THORVG */
267 void SvgVisual::Load()
269 if( mLoaded || !mLocalResource )
274 mLocalResource = mImageUrl.IsLocalResource();
276 if( !mLocalResource )
278 // load remote resource on svg rasterize thread.
282 if( !mVectorRenderer.Load( mImageUrl.GetUrl() ) )
284 DALI_LOG_ERROR( "Failed to load file!\n" );
288 mVectorRenderer.GetDefaultSize(mDefaultWidth, mDefaultHeight);
291 #endif /* NO_THORVG */
294 void SvgVisual::AddRasterizationTask( const Vector2& size )
296 if( mImpl->mRenderer )
298 unsigned int width = static_cast<unsigned int>(size.width);
299 unsigned int height = static_cast<unsigned int>( size.height );
301 Vector2 dpi = Stage::GetCurrent().GetDpi();
302 float meanDpi = ( dpi.height + dpi.width ) * 0.5f;
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() )
312 newTask->Rasterize();
313 ApplyRasterizedImage( newTask->GetParsedImage(), newTask->GetPixelData() );
314 #else /* NO_THORVG */
316 newTask->Rasterize();
317 ApplyRasterizedImage( newTask->GetVectorRenderer(), newTask->GetPixelData(), newTask->IsLoaded() );
318 #endif /* NO_THORVG */
322 mFactoryCache.GetSVGRasterizationThread()->AddTask( newTask );
328 void SvgVisual::ApplyRasterizedImage( NSVGimage* parsedSvg, PixelData rasterizedPixelData )
330 if( mParsedImage == NULL)
332 mParsedImage = parsedSvg;
335 if( mParsedImage && IsOnScene() )
336 #else /* NO_THORVG */
337 void SvgVisual::ApplyRasterizedImage( VectorImageRenderer vectorRenderer, PixelData rasterizedPixelData, bool isLoaded )
341 if( isLoaded && rasterizedPixelData && IsOnScene() )
342 #endif /* NO_THORVG */
344 TextureSet currentTextureSet = mImpl->mRenderer.GetTextures();
345 if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED )
347 mFactoryCache.GetAtlasManager()->Remove( currentTextureSet, mAtlasRect );
350 TextureSet textureSet;
352 if( mAttemptAtlasing && !mImpl->mCustomShader )
355 textureSet = mFactoryCache.GetAtlasManager()->Add(atlasRect, rasterizedPixelData );
356 if( textureSet ) // atlasing
358 if( textureSet != currentTextureSet )
360 mImpl->mRenderer.SetTextures( textureSet );
362 mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
363 mAtlasRect = atlasRect;
364 mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
368 if( !textureSet ) // no atlasing - mAttemptAtlasing is false or adding to atlas is failed
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;
375 if( mAtlasRect == FULL_TEXTURE_RECT )
377 textureSet = currentTextureSet;
381 textureSet = TextureSet::New();
382 mImpl->mRenderer.SetTextures( textureSet );
384 mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
385 mAtlasRect = FULL_TEXTURE_RECT;
390 textureSet.SetTexture( 0, texture );
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();
398 actor.AddRenderer( mImpl->mRenderer );
399 // reset the weak handle so that the renderer only get added to actor once
400 mPlacementActor.Reset();
403 // Svg loaded and ready to display
404 ResourceReady( Toolkit::Visual::ResourceStatus::READY );
407 else if( !mParsedImage )
408 #else /* NO_THORVG */
409 else if( !isLoaded || !rasterizedPixelData )
410 #endif /* NO_THORVG */
412 ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
416 void SvgVisual::OnSetTransform()
418 Vector2 visualSize = mImpl->mTransform.GetVisualSize( mImpl->mControlSize );
422 if( visualSize != mVisualSize )
424 AddRasterizationTask( visualSize );
425 mVisualSize = visualSize;
431 mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
435 bool SvgVisual::IsResourceReady() const
437 return ( mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
438 mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED );
441 } // namespace Internal
443 } // namespace Toolkit