2 * Copyright (c) 2023 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 <dali-extension/vector-animation-renderer/tizen-vector-animation-renderer.h>
22 #include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
23 #include <dali/devel-api/common/hash.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/object/property-array.h>
26 #include <tbm_surface_internal.h>
27 #include <cstring> // for strlen()
30 #include <dali-extension/vector-animation-renderer/tizen-vector-animation-manager.h>
32 // The plugin factories
33 extern "C" DALI_EXPORT_API Dali::VectorAnimationRendererPlugin* CreateVectorAnimationRendererPlugin(void)
35 return new Dali::Plugin::TizenVectorAnimationRenderer;
44 const char* const PIXEL_AREA_UNIFORM_NAME("pixelArea");
45 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
50 } // unnamed namespace
52 TizenVectorAnimationRenderer::TizenVectorAnimationRenderer()
62 mUploadCompletedSignal(),
71 mResourceReady(false),
72 mShaderChanged(false),
73 mResourceReadyTriggered(false)
75 TizenVectorAnimationManager::Get().AddEventHandler(*this);
78 TizenVectorAnimationRenderer::~TizenVectorAnimationRenderer()
80 Dali::Mutex::ScopedLock lock(mMutex);
83 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "this = %p\n", this);
86 void TizenVectorAnimationRenderer::Finalize()
88 Dali::Mutex::ScopedLock lock(mMutex);
90 TizenVectorAnimationManager::Get().RemoveEventHandler(*this);
94 mRenderedTexture.Reset();
95 mPreviousTexture.Reset();
96 mVectorRenderer.reset();
98 mTargetSurface = nullptr;
101 mPropertyCallbacks.clear();
103 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "[%p]\n", this);
106 bool TizenVectorAnimationRenderer::Load(const std::string& url)
108 Dali::Mutex::ScopedLock lock(mMutex);
112 mVectorRenderer = rlottie::Animation::loadFromFile(mUrl);
115 DALI_LOG_ERROR("Failed to load a Lottie file [%s] [%p]\n", mUrl.c_str(), this);
120 mTotalFrameNumber = static_cast<uint32_t>(mVectorRenderer->totalFrame());
121 mFrameRate = static_cast<float>(mVectorRenderer->frameRate());
124 mVectorRenderer->size(w, h);
125 mDefaultWidth = static_cast<uint32_t>(w);
126 mDefaultHeight = static_cast<uint32_t>(h);
128 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "file [%s] [%p]\n", url.c_str(), this);
133 bool TizenVectorAnimationRenderer::Load(const Dali::Vector<uint8_t>& data)
135 Dali::Mutex::ScopedLock lock(mMutex);
137 std::string jsonData(data.Begin(), data.End()); ///< Convert from raw buffer to string.
138 auto hashValue = Dali::CalculateHash(data); ///< Will be used for rlottie internal cache system.
140 mVectorRenderer = rlottie::Animation::loadFromData(jsonData, std::to_string(hashValue));
143 DALI_LOG_ERROR("Failed to load a Lottie data [data size : %zu byte] [%p]\n", data.Size(), this);
148 mTotalFrameNumber = static_cast<uint32_t>(mVectorRenderer->totalFrame());
149 mFrameRate = static_cast<float>(mVectorRenderer->frameRate());
152 mVectorRenderer->size(w, h);
153 mDefaultWidth = static_cast<uint32_t>(w);
154 mDefaultHeight = static_cast<uint32_t>(h);
156 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "data [data size : %zu byte] [%p]\n", data.Size(), this);
161 void TizenVectorAnimationRenderer::SetRenderer(Renderer renderer)
163 mRenderer = renderer;
164 mShaderChanged = false;
168 Dali::Mutex::ScopedLock lock(mMutex);
170 if(mResourceReady && mRenderedTexture)
172 TextureSet textureSet = renderer.GetTextures();
174 textureSet.SetTexture(0, mRenderedTexture);
176 mUploadCompletedSignal.Emit();
183 void TizenVectorAnimationRenderer::SetSize(uint32_t width, uint32_t height)
185 Dali::Mutex::ScopedLock lock(mMutex);
187 if(mWidth == width && mHeight == height)
189 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Same size (%d, %d) [%p]\n", mWidth, mHeight, this);
195 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Load is failed. Do not make texture [%p]\n", this);
199 mTargetSurface = NativeImageSourceQueue::New(width, height, NativeImageSourceQueue::ColorFormat::RGBA8888);
201 mTexture = Texture::New(*mTargetSurface);
208 mTbmQueue = AnyCast<tbm_surface_queue_h>(mTargetSurface->GetNativeImageSourceQueue());
213 mResourceReady = false;
215 // Reset the previous texture to destroy it in the main thread
216 mPreviousTexture.Reset();
218 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "width = %d, height = %d [%p]\n", mWidth, mHeight, this);
221 bool TizenVectorAnimationRenderer::Render(uint32_t frameNumber)
223 Dali::Mutex::ScopedLock lock(mMutex);
225 if(!mTbmQueue || !mVectorRenderer || !mTargetSurface)
230 int canDequeue = tbm_surface_queue_can_dequeue(mTbmQueue, 0);
233 // Ignore the previous image which is inserted to the queue.
234 mTargetSurface->IgnoreSourceImage();
237 canDequeue = tbm_surface_queue_can_dequeue(mTbmQueue, 0);
244 tbm_surface_h tbmSurface;
246 if(tbm_surface_queue_dequeue(mTbmQueue, &tbmSurface) != TBM_SURFACE_QUEUE_ERROR_NONE)
248 DALI_LOG_ERROR("Failed to dequeue a tbm_surface [%p]\n", this);
252 tbm_surface_info_s info;
253 int ret = tbm_surface_map(tbmSurface, TBM_OPTION_WRITE, &info);
254 if(ret != TBM_SURFACE_ERROR_NONE)
256 DALI_LOG_ERROR("TizenVectorAnimationRenderer::Render: tbm_surface_map is failed! [%d] [%p]\n", ret, this);
257 tbm_surface_queue_cancel_dequeue(mTbmQueue, tbmSurface);
261 unsigned char* buffer = info.planes[0].ptr;
262 if(info.width != mWidth || info.height != mHeight || !buffer)
264 DALI_LOG_ERROR("TizenVectorAnimationRenderer::Render: Invalid tbm surface! [%d, %d, %p] [%p]\n", info.width, info.height, buffer, this);
265 tbm_surface_unmap(tbmSurface);
266 tbm_surface_queue_cancel_dequeue(mTbmQueue, tbmSurface);
270 rlottie::Surface surface;
271 bool existing = false;
275 // Need to reset buffer list
280 for(auto&& iter : mBuffers)
282 if(iter.first == tbmSurface)
284 // Find the buffer in the existing list
286 surface = iter.second;
294 tbm_surface_internal_ref(tbmSurface);
296 // Create Surface object
297 surface = rlottie::Surface(reinterpret_cast<uint32_t*>(buffer), mWidth, mHeight, static_cast<size_t>(info.planes[0].stride));
300 mBuffers.push_back(SurfacePair(tbmSurface, surface));
304 mVectorRenderer->renderSync(frameNumber, surface);
306 tbm_surface_unmap(tbmSurface);
308 tbm_surface_queue_enqueue(mTbmQueue, tbmSurface);
312 mPreviousTexture = mRenderedTexture; // It is used to destroy the object in the main thread.
313 mRenderedTexture = mTexture;
314 mResourceReady = true;
315 mResourceReadyTriggered = true;
317 TizenVectorAnimationManager::Get().TriggerEvent(*this);
319 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Resource ready [current = %d] [%p]\n", frameNumber, this);
325 void TizenVectorAnimationRenderer::RenderStopped()
329 // Animation is stopped. Free empty buffers
330 mTargetSurface->FreeReleasedBuffers();
333 Dali::Mutex::ScopedLock lock(mMutex);
339 uint32_t TizenVectorAnimationRenderer::GetTotalFrameNumber() const
341 return mTotalFrameNumber;
344 float TizenVectorAnimationRenderer::GetFrameRate() const
349 void TizenVectorAnimationRenderer::GetDefaultSize(uint32_t& width, uint32_t& height) const
351 width = mDefaultWidth;
352 height = mDefaultHeight;
355 void TizenVectorAnimationRenderer::GetLayerInfo(Property::Map& map) const
357 Dali::Mutex::ScopedLock lock(mMutex);
361 auto layerInfo = mVectorRenderer->layers();
363 for(auto&& iter : layerInfo)
365 Property::Array frames;
366 frames.PushBack(std::get<1>(iter));
367 frames.PushBack(std::get<2>(iter));
368 map.Add(std::get<0>(iter), frames);
373 bool TizenVectorAnimationRenderer::GetMarkerInfo(const std::string& marker, uint32_t& startFrame, uint32_t& endFrame) const
375 Dali::Mutex::ScopedLock lock(mMutex);
379 auto markerList = mVectorRenderer->markers();
380 for(auto&& iter : markerList)
382 if(std::get<0>(iter).compare(marker) == 0)
384 startFrame = static_cast<uint32_t>(std::get<1>(iter));
385 endFrame = static_cast<uint32_t>(std::get<2>(iter));
393 void TizenVectorAnimationRenderer::InvalidateBuffer()
395 Dali::Mutex::ScopedLock lock(mMutex);
396 mResourceReady = false;
399 void TizenVectorAnimationRenderer::AddPropertyValueCallback(const std::string& keyPath, VectorProperty property, CallbackBase* callback, int32_t id)
401 Dali::Mutex::ScopedLock lock(mMutex);
403 mPropertyCallbacks.push_back(std::unique_ptr<CallbackBase>(callback));
407 case VectorProperty::FILL_COLOR:
409 mVectorRenderer->setValue<rlottie::Property::FillColor>(keyPath,
410 [property, callback, id](const rlottie::FrameInfo& info) {
411 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
415 return rlottie::Color(color.r, color.g, color.b);
417 return rlottie::Color(1.0f, 1.0f, 1.0f);
421 case VectorProperty::FILL_OPACITY:
423 mVectorRenderer->setValue<rlottie::Property::FillOpacity>(keyPath,
424 [property, callback, id](const rlottie::FrameInfo& info) {
425 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
427 if(value.Get(opacity))
429 return opacity * 100;
435 case VectorProperty::STROKE_COLOR:
437 mVectorRenderer->setValue<rlottie::Property::StrokeColor>(keyPath,
438 [property, callback, id](const rlottie::FrameInfo& info) {
439 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
443 return rlottie::Color(color.r, color.g, color.b);
445 return rlottie::Color(1.0f, 1.0f, 1.0f);
449 case VectorProperty::STROKE_OPACITY:
451 mVectorRenderer->setValue<rlottie::Property::StrokeOpacity>(keyPath,
452 [property, callback, id](const rlottie::FrameInfo& info) {
453 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
455 if(value.Get(opacity))
457 return opacity * 100;
463 case VectorProperty::STROKE_WIDTH:
465 mVectorRenderer->setValue<rlottie::Property::StrokeWidth>(keyPath,
466 [property, callback, id](const rlottie::FrameInfo& info) {
467 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
477 case VectorProperty::TRANSFORM_ANCHOR:
479 mVectorRenderer->setValue<rlottie::Property::TrAnchor>(keyPath,
480 [property, callback, id](const rlottie::FrameInfo& info) {
481 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
485 return rlottie::Point(point.x, point.y);
487 return rlottie::Point(0.0f, 0.0f);
491 case VectorProperty::TRANSFORM_POSITION:
493 mVectorRenderer->setValue<rlottie::Property::TrPosition>(keyPath,
494 [property, callback, id](const rlottie::FrameInfo& info) {
495 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
497 if(value.Get(position))
499 return rlottie::Point(position.x, position.y);
501 return rlottie::Point(0.0f, 0.0f);
505 case VectorProperty::TRANSFORM_SCALE:
507 mVectorRenderer->setValue<rlottie::Property::TrScale>(keyPath,
508 [property, callback, id](const rlottie::FrameInfo& info) {
509 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
513 return rlottie::Size(scale.x, scale.y);
515 return rlottie::Size(100.0f, 100.0f);
519 case VectorProperty::TRANSFORM_ROTATION:
521 mVectorRenderer->setValue<rlottie::Property::TrRotation>(keyPath,
522 [property, callback, id](const rlottie::FrameInfo& info) {
523 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
525 if(value.Get(rotation))
533 case VectorProperty::TRANSFORM_OPACITY:
535 mVectorRenderer->setValue<rlottie::Property::TrOpacity>(keyPath,
536 [property, callback, id](const rlottie::FrameInfo& info) {
537 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
539 if(value.Get(opacity))
541 return opacity * 100;
550 VectorAnimationRendererPlugin::UploadCompletedSignalType& TizenVectorAnimationRenderer::UploadCompletedSignal()
552 return mUploadCompletedSignal;
555 void TizenVectorAnimationRenderer::NotifyEvent()
557 bool emitSignal = false;
559 Dali::Mutex::ScopedLock lock(mMutex);
561 if(mResourceReadyTriggered)
563 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Set Texture [%p]\n", this);
566 if(mRenderer && mRenderedTexture)
568 TextureSet textureSet = mRenderer.GetTextures();
569 textureSet.SetTexture(0, mRenderedTexture);
572 mResourceReadyTriggered = false;
576 mPreviousTexture.Reset();
580 mUploadCompletedSignal.Emit();
584 void TizenVectorAnimationRenderer::SetShader()
591 Shader shader = mRenderer.GetShader();
593 std::string fragmentShader;
594 std::string vertexShader;
596 // Get the current fragment shader source
597 Property::Value program = shader.GetProperty(Shader::Property::PROGRAM);
598 Property::Map* map = program.GetMap();
601 Property::Value* fragment = map->Find("fragment");
604 fragmentShader = fragment->Get<std::string>();
607 Property::Value* vertex = map->Find("vertex");
610 vertexShader = vertex->Get<std::string>();
614 // Get custom fragment shader prefix
615 mTargetSurface->ApplyNativeFragmentShader(fragmentShader);
617 // Set the modified shader again
618 Shader newShader = Shader::New(vertexShader, fragmentShader);
619 newShader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
621 mRenderer.SetShader(newShader);
623 mShaderChanged = true;
626 void TizenVectorAnimationRenderer::ResetBuffers()
628 for(auto&& iter : mBuffers)
630 tbm_surface_internal_unref(iter.first);
635 } // namespace Plugin