c32f81cd8dd30c03fbc37c70d67518bcd5521e24
[platform/core/uifw/dali-extension.git] / dali-extension / vector-animation-renderer / tizen-vector-animation-renderer.cpp
1 /*
2  * Copyright (c) 2022 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 <dali-extension/vector-animation-renderer/tizen-vector-animation-renderer.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/object/property-array.h>
25 #include <tbm_surface_internal.h>
26 #include <cstring> // for strlen()
27
28 // INTERNAL INCLUDES
29 #include <dali-extension/vector-animation-renderer/tizen-vector-animation-manager.h>
30
31 // The plugin factories
32 extern "C" DALI_EXPORT_API Dali::VectorAnimationRendererPlugin* CreateVectorAnimationRendererPlugin(void)
33 {
34   return new Dali::Plugin::TizenVectorAnimationRenderer;
35 }
36
37 namespace Dali
38 {
39 namespace Plugin
40 {
41 namespace
42 {
43 const char* const PIXEL_AREA_UNIFORM_NAME("pixelArea");
44 const Vector4     FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
45
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
48 #endif
49 } // unnamed namespace
50
51 TizenVectorAnimationRenderer::TizenVectorAnimationRenderer()
52 : mUrl(),
53   mBuffers(),
54   mMutex(),
55   mRenderer(),
56   mTexture(),
57   mRenderedTexture(),
58   mPreviousTexture(),
59   mTargetSurface(),
60   mVectorRenderer(),
61   mUploadCompletedSignal(),
62   mTbmQueue(NULL),
63   mTotalFrameNumber(0),
64   mWidth(0),
65   mHeight(0),
66   mDefaultWidth(0),
67   mDefaultHeight(0),
68   mFrameRate(60.0f),
69   mLoadFailed(false),
70   mResourceReady(false),
71   mShaderChanged(false),
72   mResourceReadyTriggered(false)
73 {
74   TizenVectorAnimationManager::Get().AddEventHandler(*this);
75 }
76
77 TizenVectorAnimationRenderer::~TizenVectorAnimationRenderer()
78 {
79   Dali::Mutex::ScopedLock lock(mMutex);
80
81   ResetBuffers();
82   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "this = %p\n", this);
83 }
84
85 void TizenVectorAnimationRenderer::Finalize()
86 {
87   Dali::Mutex::ScopedLock lock(mMutex);
88
89   TizenVectorAnimationManager::Get().RemoveEventHandler(*this);
90
91   mRenderer.Reset();
92   mTexture.Reset();
93   mRenderedTexture.Reset();
94   mPreviousTexture.Reset();
95   mVectorRenderer.reset();
96
97   mTargetSurface = nullptr;
98   mTbmQueue      = NULL;
99
100   mPropertyCallbacks.clear();
101
102   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "[%p]\n", this);
103 }
104
105 bool TizenVectorAnimationRenderer::Load(const std::string& url)
106 {
107   Dali::Mutex::ScopedLock lock(mMutex);
108
109   mUrl = url;
110
111   mVectorRenderer = rlottie::Animation::loadFromFile(mUrl);
112   if(!mVectorRenderer)
113   {
114     DALI_LOG_ERROR("Failed to load a Lottie file [%s] [%p]\n", mUrl.c_str(), this);
115     mLoadFailed = true;
116     return false;
117   }
118
119   mTotalFrameNumber = static_cast<uint32_t>(mVectorRenderer->totalFrame());
120   mFrameRate        = static_cast<float>(mVectorRenderer->frameRate());
121
122   size_t w, h;
123   mVectorRenderer->size(w, h);
124   mDefaultWidth  = static_cast<uint32_t>(w);
125   mDefaultHeight = static_cast<uint32_t>(h);
126
127   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "file [%s] [%p]\n", url.c_str(), this);
128
129   return true;
130 }
131
132 void TizenVectorAnimationRenderer::SetRenderer(Renderer renderer)
133 {
134   mRenderer      = renderer;
135   mShaderChanged = false;
136
137   if(mTargetSurface)
138   {
139     Dali::Mutex::ScopedLock lock(mMutex);
140
141     if(mResourceReady && mRenderedTexture)
142     {
143       TextureSet textureSet = renderer.GetTextures();
144
145       textureSet.SetTexture(0, mRenderedTexture);
146
147       mUploadCompletedSignal.Emit();
148     }
149
150     SetShader();
151   }
152 }
153
154 void TizenVectorAnimationRenderer::SetSize(uint32_t width, uint32_t height)
155 {
156   Dali::Mutex::ScopedLock lock(mMutex);
157
158   if(mWidth == width && mHeight == height)
159   {
160     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Same size (%d, %d) [%p]\n", mWidth, mHeight, this);
161     return;
162   }
163
164   if(mLoadFailed)
165   {
166     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Load is failed. Do not make texture [%p]\n", this);
167     return;
168   }
169
170   mTargetSurface = NativeImageSourceQueue::New(width, height, NativeImageSourceQueue::ColorFormat::RGBA8888);
171
172   mTexture = Texture::New(*mTargetSurface);
173
174   if(mRenderer)
175   {
176     SetShader();
177   }
178
179   mTbmQueue = AnyCast<tbm_surface_queue_h>(mTargetSurface->GetNativeImageSourceQueue());
180
181   mWidth  = width;
182   mHeight = height;
183
184   mResourceReady = false;
185
186   // Reset the previous texture to destroy it in the main thread
187   mPreviousTexture.Reset();
188
189   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "width = %d, height = %d [%p]\n", mWidth, mHeight, this);
190 }
191
192 bool TizenVectorAnimationRenderer::Render(uint32_t frameNumber)
193 {
194   Dali::Mutex::ScopedLock lock(mMutex);
195
196   if(!mTbmQueue || !mVectorRenderer || !mTargetSurface)
197   {
198     return false;
199   }
200
201   int canDequeue = tbm_surface_queue_can_dequeue(mTbmQueue, 0);
202   if(!canDequeue)
203   {
204     // Ignore the previous image which is inserted to the queue.
205     mTargetSurface->IgnoreSourceImage();
206
207     // Check again
208     canDequeue = tbm_surface_queue_can_dequeue(mTbmQueue, 0);
209     if(!canDequeue)
210     {
211       return false;
212     }
213   }
214
215   tbm_surface_h tbmSurface;
216
217   if(tbm_surface_queue_dequeue(mTbmQueue, &tbmSurface) != TBM_SURFACE_QUEUE_ERROR_NONE)
218   {
219     DALI_LOG_ERROR("Failed to dequeue a tbm_surface [%p]\n", this);
220     return false;
221   }
222
223   tbm_surface_info_s info;
224   int                ret = tbm_surface_map(tbmSurface, TBM_OPTION_WRITE, &info);
225   if(ret != TBM_SURFACE_ERROR_NONE)
226   {
227     DALI_LOG_ERROR("TizenVectorAnimationRenderer::Render: tbm_surface_map is failed! [%d] [%p]\n", ret, this);
228     tbm_surface_queue_cancel_dequeue(mTbmQueue, tbmSurface);
229     return false;
230   }
231
232   unsigned char* buffer = info.planes[0].ptr;
233   if(info.width != mWidth || info.height != mHeight || !buffer)
234   {
235     DALI_LOG_ERROR("TizenVectorAnimationRenderer::Render: Invalid tbm surface! [%d, %d, %p] [%p]\n", info.width, info.height, buffer, this);
236     tbm_surface_unmap(tbmSurface);
237     tbm_surface_queue_cancel_dequeue(mTbmQueue, tbmSurface);
238     return false;
239   }
240
241   rlottie::Surface surface;
242   bool             existing = false;
243
244   if(!mResourceReady)
245   {
246     // Need to reset buffer list
247     ResetBuffers();
248   }
249   else
250   {
251     for(auto&& iter : mBuffers)
252     {
253       if(iter.first == tbmSurface)
254       {
255         // Find the buffer in the existing list
256         existing = true;
257         surface  = iter.second;
258         break;
259       }
260     }
261   }
262
263   if(!existing)
264   {
265     tbm_surface_internal_ref(tbmSurface);
266
267     // Create Surface object
268     surface = rlottie::Surface(reinterpret_cast<uint32_t*>(buffer), mWidth, mHeight, static_cast<size_t>(info.planes[0].stride));
269
270     // Push the buffer
271     mBuffers.push_back(SurfacePair(tbmSurface, surface));
272   }
273
274   // Render the frame
275   mVectorRenderer->renderSync(frameNumber, surface);
276
277   tbm_surface_unmap(tbmSurface);
278
279   tbm_surface_queue_enqueue(mTbmQueue, tbmSurface);
280
281   if(!mResourceReady)
282   {
283     mPreviousTexture        = mRenderedTexture; // It is used to destroy the object in the main thread.
284     mRenderedTexture        = mTexture;
285     mResourceReady          = true;
286     mResourceReadyTriggered = true;
287
288     TizenVectorAnimationManager::Get().TriggerEvent(*this);
289
290     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Resource ready [current = %d] [%p]\n", frameNumber, this);
291   }
292
293   return true;
294 }
295
296 uint32_t TizenVectorAnimationRenderer::GetTotalFrameNumber() const
297 {
298   return mTotalFrameNumber;
299 }
300
301 float TizenVectorAnimationRenderer::GetFrameRate() const
302 {
303   return mFrameRate;
304 }
305
306 void TizenVectorAnimationRenderer::GetDefaultSize(uint32_t& width, uint32_t& height) const
307 {
308   width  = mDefaultWidth;
309   height = mDefaultHeight;
310 }
311
312 void TizenVectorAnimationRenderer::GetLayerInfo(Property::Map& map) const
313 {
314   Dali::Mutex::ScopedLock lock(mMutex);
315
316   if(mVectorRenderer)
317   {
318     auto layerInfo = mVectorRenderer->layers();
319
320     for(auto&& iter : layerInfo)
321     {
322       Property::Array frames;
323       frames.PushBack(std::get<1>(iter));
324       frames.PushBack(std::get<2>(iter));
325       map.Add(std::get<0>(iter), frames);
326     }
327   }
328 }
329
330 bool TizenVectorAnimationRenderer::GetMarkerInfo(const std::string& marker, uint32_t& startFrame, uint32_t& endFrame) const
331 {
332   Dali::Mutex::ScopedLock lock(mMutex);
333
334   if(mVectorRenderer)
335   {
336     auto markerList = mVectorRenderer->markers();
337     for(auto&& iter : markerList)
338     {
339       if(std::get<0>(iter).compare(marker) == 0)
340       {
341         startFrame = static_cast<uint32_t>(std::get<1>(iter));
342         endFrame   = static_cast<uint32_t>(std::get<2>(iter));
343         return true;
344       }
345     }
346   }
347   return false;
348 }
349
350 void TizenVectorAnimationRenderer::InvalidateBuffer()
351 {
352   Dali::Mutex::ScopedLock lock(mMutex);
353   mResourceReady = false;
354 }
355
356 void TizenVectorAnimationRenderer::AddPropertyValueCallback(const std::string& keyPath, VectorProperty property, CallbackBase* callback, int32_t id)
357 {
358   Dali::Mutex::ScopedLock lock(mMutex);
359
360   mPropertyCallbacks.push_back(std::unique_ptr<CallbackBase>(callback));
361
362   switch(property)
363   {
364     case VectorProperty::FILL_COLOR:
365     {
366       mVectorRenderer->setValue<rlottie::Property::FillColor>(keyPath,
367                                                               [property, callback, id](const rlottie::FrameInfo& info) {
368                                                                 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
369                                                                 Vector3         color;
370                                                                 if(value.Get(color))
371                                                                 {
372                                                                   return rlottie::Color(color.r, color.g, color.b);
373                                                                 }
374                                                                 return rlottie::Color(1.0f, 1.0f, 1.0f);
375                                                               });
376       break;
377     }
378     case VectorProperty::FILL_OPACITY:
379     {
380       mVectorRenderer->setValue<rlottie::Property::FillOpacity>(keyPath,
381                                                                 [property, callback, id](const rlottie::FrameInfo& info) {
382                                                                   Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
383                                                                   float           opacity;
384                                                                   if(value.Get(opacity))
385                                                                   {
386                                                                     return opacity * 100;
387                                                                   }
388                                                                   return 100.0f;
389                                                                 });
390       break;
391     }
392     case VectorProperty::STROKE_COLOR:
393     {
394       mVectorRenderer->setValue<rlottie::Property::StrokeColor>(keyPath,
395                                                                 [property, callback, id](const rlottie::FrameInfo& info) {
396                                                                   Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
397                                                                   Vector3         color;
398                                                                   if(value.Get(color))
399                                                                   {
400                                                                     return rlottie::Color(color.r, color.g, color.b);
401                                                                   }
402                                                                   return rlottie::Color(1.0f, 1.0f, 1.0f);
403                                                                 });
404       break;
405     }
406     case VectorProperty::STROKE_OPACITY:
407     {
408       mVectorRenderer->setValue<rlottie::Property::StrokeOpacity>(keyPath,
409                                                                   [property, callback, id](const rlottie::FrameInfo& info) {
410                                                                     Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
411                                                                     float           opacity;
412                                                                     if(value.Get(opacity))
413                                                                     {
414                                                                       return opacity * 100;
415                                                                     }
416                                                                     return 100.0f;
417                                                                   });
418       break;
419     }
420     case VectorProperty::STROKE_WIDTH:
421     {
422       mVectorRenderer->setValue<rlottie::Property::StrokeWidth>(keyPath,
423                                                                 [property, callback, id](const rlottie::FrameInfo& info) {
424                                                                   Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
425                                                                   float           width;
426                                                                   if(value.Get(width))
427                                                                   {
428                                                                     return width;
429                                                                   }
430                                                                   return 1.0f;
431                                                                 });
432       break;
433     }
434     case VectorProperty::TRANSFORM_ANCHOR:
435     {
436       mVectorRenderer->setValue<rlottie::Property::TrAnchor>(keyPath,
437                                                              [property, callback, id](const rlottie::FrameInfo& info) {
438                                                                Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
439                                                                Vector2         point;
440                                                                if(value.Get(point))
441                                                                {
442                                                                  return rlottie::Point(point.x, point.y);
443                                                                }
444                                                                return rlottie::Point(0.0f, 0.0f);
445                                                              });
446       break;
447     }
448     case VectorProperty::TRANSFORM_POSITION:
449     {
450       mVectorRenderer->setValue<rlottie::Property::TrPosition>(keyPath,
451                                                                [property, callback, id](const rlottie::FrameInfo& info) {
452                                                                  Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
453                                                                  Vector2         position;
454                                                                  if(value.Get(position))
455                                                                  {
456                                                                    return rlottie::Point(position.x, position.y);
457                                                                  }
458                                                                  return rlottie::Point(0.0f, 0.0f);
459                                                                });
460       break;
461     }
462     case VectorProperty::TRANSFORM_SCALE:
463     {
464       mVectorRenderer->setValue<rlottie::Property::TrScale>(keyPath,
465                                                             [property, callback, id](const rlottie::FrameInfo& info) {
466                                                               Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
467                                                               Vector2         scale;
468                                                               if(value.Get(scale))
469                                                               {
470                                                                 return rlottie::Size(scale.x, scale.y);
471                                                               }
472                                                               return rlottie::Size(100.0f, 100.0f);
473                                                             });
474       break;
475     }
476     case VectorProperty::TRANSFORM_ROTATION:
477     {
478       mVectorRenderer->setValue<rlottie::Property::TrRotation>(keyPath,
479                                                                [property, callback, id](const rlottie::FrameInfo& info) {
480                                                                  Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
481                                                                  float           rotation;
482                                                                  if(value.Get(rotation))
483                                                                  {
484                                                                    return rotation;
485                                                                  }
486                                                                  return 0.0f;
487                                                                });
488       break;
489     }
490     case VectorProperty::TRANSFORM_OPACITY:
491     {
492       mVectorRenderer->setValue<rlottie::Property::TrOpacity>(keyPath,
493                                                               [property, callback, id](const rlottie::FrameInfo& info) {
494                                                                 Property::Value value = CallbackBase::ExecuteReturn<Property::Value>(*callback, id, property, info.curFrame());
495                                                                 float           opacity;
496                                                                 if(value.Get(opacity))
497                                                                 {
498                                                                   return opacity * 100;
499                                                                 }
500                                                                 return 100.0f;
501                                                               });
502       break;
503     }
504   }
505 }
506
507 VectorAnimationRendererPlugin::UploadCompletedSignalType& TizenVectorAnimationRenderer::UploadCompletedSignal()
508 {
509   return mUploadCompletedSignal;
510 }
511
512 void TizenVectorAnimationRenderer::NotifyEvent()
513 {
514   Dali::Mutex::ScopedLock lock(mMutex);
515
516   if(mResourceReadyTriggered)
517   {
518     DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "Set Texture [%p]\n", this);
519
520     // Set texture
521     if(mRenderer && mRenderedTexture)
522     {
523       TextureSet textureSet = mRenderer.GetTextures();
524       textureSet.SetTexture(0, mRenderedTexture);
525     }
526
527     mResourceReadyTriggered = false;
528
529     mUploadCompletedSignal.Emit();
530   }
531
532   mPreviousTexture.Reset();
533 }
534
535 void TizenVectorAnimationRenderer::SetShader()
536 {
537   if(mShaderChanged)
538   {
539     return;
540   }
541
542   Shader shader = mRenderer.GetShader();
543
544   std::string fragmentShader;
545   std::string vertexShader;
546
547   // Get the current fragment shader source
548   Property::Value program = shader.GetProperty(Shader::Property::PROGRAM);
549   Property::Map*  map     = program.GetMap();
550   if(map)
551   {
552     Property::Value* fragment = map->Find("fragment");
553     if(fragment)
554     {
555       fragmentShader = fragment->Get<std::string>();
556     }
557
558     Property::Value* vertex = map->Find("vertex");
559     if(vertex)
560     {
561       vertexShader = vertex->Get<std::string>();
562     }
563   }
564
565   // Get custom fragment shader prefix
566   mTargetSurface->ApplyNativeFragmentShader(fragmentShader);
567
568   // Set the modified shader again
569   Shader newShader = Shader::New(vertexShader, fragmentShader);
570   newShader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
571
572   mRenderer.SetShader(newShader);
573
574   mShaderChanged = true;
575 }
576
577 void TizenVectorAnimationRenderer::ResetBuffers()
578 {
579   for(auto&& iter : mBuffers)
580   {
581     tbm_surface_internal_unref(iter.first);
582   }
583   mBuffers.clear();
584 }
585
586 } // namespace Plugin
587
588 } // namespace Dali