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 using global::System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
22 using System.Globalization;
24 namespace Tizen.NUI.BaseComponents
27 /// LottieAnimationView renders an animated vector image (Lottie file).
29 /// <since_tizen> 7 </since_tizen>
30 public partial class LottieAnimationView : ImageView
33 #region Constructor, Destructor, Dispose
35 /// LottieAnimationView constructor
37 /// <param name="scale">The factor of scaling image, default : 1.0f</param>
38 /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
40 /// If the shown parameter is false, the animation is not visible even if the LottieAnimationView instance is created.
44 /// LottieAnimationView myLottie = new LottieAnimationView();
45 /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
46 /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
49 /// <since_tizen> 7 </since_tizen>
50 public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
52 NUILog.Debug($"< constructor GetId={GetId()} >");
53 currentStates.url = "";
54 currentStates.loopCount = 1;
55 currentStates.loopMode = LoopingModeType.Restart;
56 currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
57 currentStates.framePlayRangeMin = -1;
58 currentStates.framePlayRangeMax = -1;
59 currentStates.totalFrame = -1;
60 currentStates.scale = scale;
61 currentStates.redrawInScalingDown = true;
63 // Set changed flag as true when initalized state.
64 // After some properties change, LottieAnimationView.UpdateImage will apply these inital values.
65 currentStates.changed = true;
70 /// Dispose(DisposeTypes type)
72 /// <param name="type"></param>
73 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
74 [EditorBrowsable(EditorBrowsableState.Never)]
75 protected override void Dispose(DisposeTypes type)
82 CleanCallbackDictionaries();
84 //Release your own unmanaged resources here.
85 //You should not access any managed member here except static instance.
86 //because the execution order of Finalizes is non-deterministic.
88 //disconnect event signal
89 if (finishedEventHandler != null && visualEventSignalCallback != null)
91 using VisualEventSignal visualEvent = VisualEventSignal();
92 visualEvent.Disconnect(visualEventSignalCallback);
93 finishedEventHandler = null;
94 NUILog.Debug($"disconnect event signal");
100 // This is used for internal purpose. hidden API.
101 [EditorBrowsable(EditorBrowsableState.Never)]
102 protected override void Dispose(bool disposing)
104 CleanCallbackDictionaries();
105 base.Dispose(disposing);
107 #endregion Constructor, Destructor, Dispose
112 /// Set or Get resource URL of Lottie file.
114 /// <since_tizen> 7 </since_tizen>
119 return GetValue(URLProperty) as string;
123 SetValue(URLProperty, value);
124 NotifyPropertyChanged();
128 private string InternalURL
132 // Reset cached infomations.
133 currentStates.contentInfo = null;
134 currentStates.mark1 = null;
135 currentStates.mark2 = null;
136 currentStates.framePlayRangeMin = -1;
137 currentStates.framePlayRangeMax = -1;
138 currentStates.totalFrame = -1;
140 string ret = (value == null ? "" : value);
141 currentStates.url = ret;
143 NUILog.Debug($"<[{GetId()}]SET url={currentStates.url}");
145 // TODO : Could create new Image without additional creation?
146 using PropertyMap map = new PropertyMap();
147 using PropertyValue type = new PropertyValue((int)Visual.Type.AnimatedVectorImage);
148 using PropertyValue url = new PropertyValue(currentStates.url);
149 using PropertyValue loopCnt = new PropertyValue(currentStates.loopCount);
150 using PropertyValue stopAction = new PropertyValue((int)currentStates.stopEndAction);
151 using PropertyValue loopMode = new PropertyValue((int)currentStates.loopMode);
152 using PropertyValue redrawInScalingDown = new PropertyValue(currentStates.redrawInScalingDown);
154 map.Add(Visual.Property.Type, type)
155 .Add(ImageVisualProperty.URL, url)
156 .Add(ImageVisualProperty.LoopCount, loopCnt)
157 .Add(ImageVisualProperty.StopBehavior, stopAction)
158 .Add(ImageVisualProperty.LoopingMode, loopMode)
159 .Add(ImageVisualProperty.RedrawInScalingDown, redrawInScalingDown);
162 // All states applied well.
163 currentStates.changed = false;
165 if (currentStates.scale != 1.0f)
167 Scale = new Vector3(currentStates.scale, currentStates.scale, currentStates.scale);
169 NUILog.Debug($"<[{GetId()}]>");
173 string ret = currentStates.url;
174 NUILog.Debug($"<[{GetId()}] GET");
175 NUILog.Debug($"gotten url={ret} >");
181 /// Gets the playing state
183 /// <since_tizen> 7 </since_tizen>
184 public PlayStateType PlayState
188 NUILog.Debug($"< Get!");
191 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayState, out ret);
193 currentStates.playState = (PlayStateType)ret;
194 NUILog.Debug($"gotten play state={currentStates.playState} >");
196 return currentStates.playState;
201 /// Get the number of total frames
203 /// <since_tizen> 7 </since_tizen>
204 public int TotalFrame
208 int ret = currentStates.totalFrame;
211 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.TotalFrameNumber, out ret);
213 currentStates.totalFrame = ret;
214 NUILog.Debug($"TotalFrameNumber get! ret={ret}");
221 /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
224 /// Gets the value set by a user. If the setting value is out-ranged, it is reset as a minimum frame or a maximum frame.
227 /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
229 /// LottieAnimationView myLottie = new LottieAnimationView();
230 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
231 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
232 /// myLottie.CurrentFrame = 200; //display 99 frame
233 /// myLottie.SetMinMaxFrame(10, 20);
234 /// myLottie.CurrentFrame = 15; //display 15 frame
235 /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
238 /// <since_tizen> 7 </since_tizen>
239 public int CurrentFrame
243 return (int)GetValue(CurrentFrameProperty);
247 SetValue(CurrentFrameProperty, value);
248 NotifyPropertyChanged();
252 private int InternalCurrentFrame
256 NUILog.Debug($"<[{GetId()}]SET frame={value}>");
258 Interop.View.DoActionWithSingleIntAttributes(this.SwigCPtr, ImageView.Property.IMAGE, ActionJumpTo, value);
264 Interop.View.InternalRetrievingVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.CurrentFrameNumber, out ret);
266 NUILog.Debug($"CurrentFrameNumber get! val={ret}");
272 /// Sets or gets the looping mode of Lottie animation.
274 /// <since_tizen> 7 </since_tizen>
275 public LoopingModeType LoopingMode
279 return (LoopingModeType)GetValue(LoopingModeProperty);
283 SetValue(LoopingModeProperty, value);
284 NotifyPropertyChanged();
288 private LoopingModeType InternalLoopingMode
292 if (currentStates.loopMode != (LoopingModeType)value)
294 currentStates.changed = true;
295 currentStates.loopMode = (LoopingModeType)value;
297 NUILog.Debug($"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
299 Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.LoopingMode, (int)currentStates.loopMode);
304 NUILog.Debug($"LoopMode get! {currentStates.loopMode}");
305 return currentStates.loopMode;
310 /// Sets or gets the loop count.
313 /// The minus value means the infinite loop count.
317 /// LottieAnimationView myLottie = new LottieAnimationView();
318 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
319 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
320 /// myLottie.LoopCount = -1; //infinite loop
322 /// myLottie.Stop(); //it plays continuously unless Stop() is called
323 /// myLottie.LoopCount = 2;
324 /// myLottie.Play(); //it plays only 2 times and stops automatically
327 /// <since_tizen> 7 </since_tizen>
332 return (int)GetValue(LoopCountProperty);
336 SetValue(LoopCountProperty, value);
337 NotifyPropertyChanged();
341 private int InternalLoopCount
345 if (currentStates.loopCount != value)
347 currentStates.changed = true;
348 currentStates.loopCount = value;
350 NUILog.Debug($"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
352 Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.LoopCount, currentStates.loopCount);
357 NUILog.Debug($"LoopCount get! {currentStates.loopCount}");
358 return currentStates.loopCount;
363 /// Sets or gets the stop behavior.
365 /// <since_tizen> 7 </since_tizen>
366 public StopBehaviorType StopBehavior
370 return (StopBehaviorType)GetValue(StopBehaviorProperty);
374 SetValue(StopBehaviorProperty, value);
375 NotifyPropertyChanged();
379 private StopBehaviorType InternalStopBehavior
383 if (currentStates.stopEndAction != (StopBehaviorType)value)
385 currentStates.changed = true;
386 currentStates.stopEndAction = (StopBehaviorType)value;
388 NUILog.Debug($"<[{GetId()}]SET val={currentStates.stopEndAction}>");
390 Interop.View.InternalUpdateVisualPropertyInt(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.StopBehavior, (int)currentStates.stopEndAction);
395 NUILog.Debug($"StopBehavior get! {currentStates.stopEndAction}");
396 return currentStates.stopEndAction;
401 /// Whether to redraw the image when the visual is scaled down.
405 /// It is used in the AnimatedVectorImageVisual.The default is true.
407 [EditorBrowsable(EditorBrowsableState.Never)]
408 public bool RedrawInScalingDown
412 return (bool)GetValue(RedrawInScalingDownProperty);
416 SetValue(RedrawInScalingDownProperty, value);
417 NotifyPropertyChanged();
421 private bool InternalRedrawInScalingDown
425 if (currentStates.redrawInScalingDown != value)
427 currentStates.changed = true;
428 currentStates.redrawInScalingDown = value;
430 NUILog.Debug($"<[{GetId()}]SET currentStates.redrawInScalingDown={currentStates.redrawInScalingDown}>");
432 Interop.View.InternalUpdateVisualPropertyBool(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.RedrawInScalingDown, currentStates.redrawInScalingDown);
437 NUILog.Debug($"RedrawInScalingDown get! {currentStates.redrawInScalingDown}");
438 return currentStates.redrawInScalingDown;
446 /// Set the minimum and the maximum frame.
448 /// <param name="minFrame">minimum frame</param>
449 /// <param name="maxFrame">maximum frame</param>
450 /// <since_tizen> 7 </since_tizen>
451 public void SetMinMaxFrame(int minFrame, int maxFrame)
453 if (currentStates.framePlayRangeMin != minFrame || currentStates.framePlayRangeMax != maxFrame)
455 NUILog.Debug($"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
456 currentStates.changed = true;
457 currentStates.framePlayRangeMin = minFrame;
458 currentStates.framePlayRangeMax = maxFrame;
460 Interop.View.InternalUpdateVisualPropertyIntPair(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.framePlayRangeMin, currentStates.framePlayRangeMax);
462 NUILog.Debug($" [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
469 /// <since_tizen> 7 </since_tizen>
470 public new void Play()
472 NUILog.Debug($"<[{GetId()}] Play()");
475 NUILog.Debug($"[{GetId()}]>");
481 /// <since_tizen> 7 </since_tizen>
482 public new void Pause()
484 NUILog.Debug($"<[{GetId()}] Pause()>");
487 NUILog.Debug($"[{GetId()}]>");
493 /// <since_tizen> 7 </since_tizen>
494 public new void Stop()
496 NUILog.Debug($"<[{GetId()}] Stop()");
499 NUILog.Debug($"[{GetId()}]>");
503 /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
505 /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
506 /// <since_tizen> 7 </since_tizen>
507 public List<Tuple<string, int, int>> GetContentInfo()
509 if (currentStates.contentInfo != null)
511 return currentStates.contentInfo;
516 PropertyMap imageMap = base.Image;
517 if (imageMap != null)
519 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
520 PropertyMap contentMap = new PropertyMap();
521 if (val?.Get(ref contentMap) == true)
523 currentStates.contentInfo = new List<Tuple<string, int, int>>();
524 for (uint i = 0; i < contentMap.Count(); i++)
526 using PropertyKey propertyKey = contentMap.GetKeyAt(i);
527 string key = propertyKey.StringKey;
529 using PropertyValue arrVal = contentMap.GetValue(i);
530 using PropertyArray arr = new PropertyArray();
534 using PropertyValue start = arr.GetElementAt(0);
535 start?.Get(out startFrame);
538 using PropertyValue end = arr.GetElementAt(1);
539 end?.Get(out endFrame);
541 NUILog.Debug($"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
543 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
545 currentStates.contentInfo?.Add(item);
549 contentMap.Dispose();
554 return currentStates.contentInfo;
558 /// A marker has its start frame and end frame.
559 /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
560 /// Or animation will play between the start frame of the first marker and the end frame of the second marker if two markers are specified. *
562 /// <param name="marker1">First marker</param>
563 /// <param name="marker2">Second marker</param>
564 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
565 [EditorBrowsable(EditorBrowsableState.Never)]
566 public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
568 if (currentStates.mark1 != marker1 || currentStates.mark2 != marker2)
570 NUILog.Debug($"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
572 currentStates.changed = true;
573 currentStates.mark1 = marker1;
574 currentStates.mark2 = marker2;
576 if (string.IsNullOrEmpty(currentStates.mark2))
578 Interop.View.InternalUpdateVisualPropertyString(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.mark1);
582 Interop.View.InternalUpdateVisualPropertyStringPair(this.SwigCPtr, ImageView.Property.IMAGE, ImageVisualProperty.PlayRange, currentStates.mark1, currentStates.mark2);
585 NUILog.Debug($" [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
592 /// <returns>Tuple of Min and Max frames</returns>
593 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
594 [EditorBrowsable(EditorBrowsableState.Never)]
595 public Tuple<int, int> GetMinMaxFrame()
597 NUILog.Debug($"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
599 using PropertyMap map = Image;
602 using PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
605 using PropertyArray array = new PropertyArray();
608 uint cnt = array.Count();
609 int item1 = -1, item2 = -1;
610 for (uint i = 0; i < cnt; i++)
612 using PropertyValue v = array.GetElementAt(i);
614 if (v.Get(out intRet))
616 NUILog.Debug($"Got play range of string [{i}]: {intRet}");
628 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
631 NUILog.Debug($" [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
632 return new Tuple<int, int>(item1, item2);
636 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
637 return new Tuple<int, int>(-1, -1);
640 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
641 [EditorBrowsable(EditorBrowsableState.Never)]
642 public void DoActionExtension(LottieAnimationViewDynamicProperty info)
644 dynamicPropertyCallbackId++;
646 weakReferencesOfLottie?.Add(dynamicPropertyCallbackId, new WeakReference<LottieAnimationView>(this));
647 InternalSavedDynamicPropertyCallbacks?.Add(dynamicPropertyCallbackId, info.Callback);
649 Interop.View.DoActionExtension(SwigCPtr, ImageView.Property.IMAGE, ActionSetDynamicProperty, dynamicPropertyCallbackId, info.KeyPath, (int)info.Property, Marshal.GetFunctionPointerForDelegate<System.Delegate>(rootCallback));
651 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
654 private void CleanCallbackDictionaries()
656 if (weakReferencesOfLottie?.Count > 0 && InternalSavedDynamicPropertyCallbacks != null)
658 foreach (var key in InternalSavedDynamicPropertyCallbacks?.Keys)
660 if (weakReferencesOfLottie.ContainsKey(key))
662 weakReferencesOfLottie.Remove(key);
666 InternalSavedDynamicPropertyCallbacks?.Clear();
667 InternalSavedDynamicPropertyCallbacks = null;
671 /// Update lottie-image-relative properties synchronously.
672 /// After call this API, All image properties updated.
674 [EditorBrowsable(EditorBrowsableState.Never)]
675 protected override void UpdateImage()
677 if (!imagePropertyUpdatedFlag) return;
679 // Update currentStates properties to cachedImagePropertyMap
680 if(currentStates.changed)
682 UpdateImage(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount), false);
683 UpdateImage(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction), false);
684 UpdateImage(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode), false);
685 UpdateImage(ImageVisualProperty.RedrawInScalingDown, new PropertyValue(currentStates.redrawInScalingDown), false);
687 // Do not cache PlayRange and TotalFrameNumber into cachedImagePropertyMap.
688 // (To keep legacy implements behaviour)
689 currentStates.changed = false;
696 /// Update NUI cached animated image visual property map by inputed property map.
697 /// And call base.MergeCachedImageVisualProperty()
700 /// For performance issue, we will collect only "cachedLottieAnimationPropertyKeyList" hold in this class.
702 [EditorBrowsable(EditorBrowsableState.Never)]
703 protected override void MergeCachedImageVisualProperty(PropertyMap map)
705 if (map == null) return;
706 if (cachedImagePropertyMap == null)
708 cachedImagePropertyMap = new PropertyMap();
710 foreach (var key in cachedLottieAnimationPropertyKeyList)
712 PropertyValue value = map.Find(key);
715 // Update-or-Insert new value
716 cachedImagePropertyMap[key] = value;
719 base.MergeCachedImageVisualProperty(map);
724 #region Event, Enum, Struct, ETC
726 /// Animation finished event.
728 /// <since_tizen> 7 </since_tizen>
729 public event EventHandler Finished
733 if (finishedEventHandler == null)
735 NUILog.Debug($"<[{GetId()}] Finished eventhandler added>");
736 visualEventSignalCallback = onVisualEventSignal;
737 using VisualEventSignal visualEvent = VisualEventSignal();
738 visualEvent.Connect(visualEventSignalCallback);
740 finishedEventHandler += value;
744 NUILog.Debug($"<[{GetId()}] Finished eventhandler removed>");
745 finishedEventHandler -= value;
746 if (finishedEventHandler == null && visualEventSignalCallback != null)
748 using VisualEventSignal visualEvent = VisualEventSignal();
749 visualEvent.Disconnect(visualEventSignalCallback);
750 if (visualEvent?.Empty() == true)
752 visualEventSignalCallback = null;
759 /// Enumeration for what state the vector animation is in
761 /// <since_tizen> 7 </since_tizen>
762 public enum PlayStateType
767 /// <since_tizen> 7 </since_tizen>
770 /// Vector Animation has stopped
772 /// <since_tizen> 7 </since_tizen>
775 /// The vector animation is playing
777 /// <since_tizen> 7 </since_tizen>
780 /// The vector animation is paused
782 /// <since_tizen> 7 </since_tizen>
787 /// Enumeration for what to do when the animation is stopped.
789 /// <since_tizen> 7 </since_tizen>
790 public enum StopBehaviorType
793 /// When the animation is stopped, the current frame is shown.
795 /// <since_tizen> 7 </since_tizen>
798 /// When the animation is stopped, the min frame (first frame) is shown.
800 /// <since_tizen> 7 </since_tizen>
803 /// When the animation is stopped, the max frame (last frame) is shown.
805 /// <since_tizen> 7 </since_tizen>
810 /// Enumeration for what looping mode is in.
812 /// <since_tizen> 7 </since_tizen>
813 public enum LoopingModeType
816 /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
818 /// <since_tizen> 7 </since_tizen>
821 /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
823 /// <since_tizen> 7 </since_tizen>
830 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
831 [EditorBrowsable(EditorBrowsableState.Never)]
832 public enum VectorProperty
835 /// Fill color of the object, Type of <see cref="Vector3"/>
837 [EditorBrowsable(EditorBrowsableState.Never)]
841 /// Fill opacity of the object, Type of float
843 [EditorBrowsable(EditorBrowsableState.Never)]
847 /// Stroke color of the object, Type of <see cref="Vector3"/>
849 [EditorBrowsable(EditorBrowsableState.Never)]
853 /// Stroke opacity of the object, Type of float
855 [EditorBrowsable(EditorBrowsableState.Never)]
859 /// Stroke width of the object, Type of float
861 [EditorBrowsable(EditorBrowsableState.Never)]
865 /// Transform anchor of the Layer and Group object, Type of <see cref="Vector2"/>
867 [EditorBrowsable(EditorBrowsableState.Never)]
871 /// Transform position of the Layer and Group object, Type of <see cref="Vector2"/>
873 [EditorBrowsable(EditorBrowsableState.Never)]
877 /// Transform scale of the Layer and Group object, Type of <see cref="Vector2"/>, Value range of [0..100]
879 [EditorBrowsable(EditorBrowsableState.Never)]
883 /// Transform rotation of the Layer and Group object, Type of float, Value range of [0..360] in degrees
885 [EditorBrowsable(EditorBrowsableState.Never)]
889 /// Transform opacity of the Layer and Group object, Type of float
891 [EditorBrowsable(EditorBrowsableState.Never)]
895 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
896 [EditorBrowsable(EditorBrowsableState.Never)]
897 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
898 public delegate PropertyValue DynamicPropertyCallbackType(int returnType, uint frameNumber);
899 #endregion Event, Enum, Struct, ETC
904 /// Actions property value to Jump to the specified frame.
906 internal static readonly int ActionJumpTo = Interop.LottieAnimationView.AnimatedVectorImageVisualActionJumpToGet();
908 // This is used for internal purpose.
909 internal static readonly int ActionSetDynamicProperty = Interop.LottieAnimationView.AnimatedVectorImageVisualActionSetDynamicProperty();
911 internal class VisualEventSignalArgs : EventArgs
913 public int VisualIndex
925 internal event EventHandler<VisualEventSignalArgs> VisualEvent
929 if (visualEventSignalHandler == null)
931 visualEventSignalCallback = onVisualEventSignal;
932 using VisualEventSignal visualEvent = VisualEventSignal();
933 visualEvent?.Connect(visualEventSignalCallback);
935 visualEventSignalHandler += value;
939 visualEventSignalHandler -= value;
940 if (visualEventSignalHandler == null && visualEventSignalCallback != null)
942 using VisualEventSignal visualEvent = VisualEventSignal();
943 visualEvent?.Disconnect(visualEventSignalCallback);
944 if (visualEvent?.Empty() == true)
946 visualEventSignalCallback = null;
952 internal void EmitVisualEventSignal(int visualIndex, int signalId)
954 using VisualEventSignal visualEvent = VisualEventSignal();
955 visualEvent?.Emit(this, visualIndex, signalId);
958 internal VisualEventSignal VisualEventSignal()
960 VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
961 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
965 internal Dictionary<int, DynamicPropertyCallbackType> InternalSavedDynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
967 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
968 internal delegate void RootCallbackType(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3);
970 internal RootCallbackType rootCallback = RootCallback;
972 static internal void RootCallback(int id, int returnType, uint frameNumber, ref float val1, ref float val2, ref float val3)
974 WeakReference<LottieAnimationView> current = null;
975 LottieAnimationView currentView = null;
976 DynamicPropertyCallbackType currentCallback = null;
977 PropertyValue ret = null;
979 if (weakReferencesOfLottie.TryGetValue(id, out current))
981 if (current.TryGetTarget(out currentView))
983 if (currentView != null && currentView.InternalSavedDynamicPropertyCallbacks != null &&
984 currentView.InternalSavedDynamicPropertyCallbacks.TryGetValue(id, out currentCallback))
986 ret = currentCallback?.Invoke(returnType, frameNumber);
990 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
996 Tizen.Log.Error("NUI", "can't find the callback in LottieAnimationView, just return here!");
1002 Tizen.Log.Error("NUI", "can't find LottieAnimationView by id, just return here!");
1008 case (int)(VectorProperty.FillColor):
1009 case (int)(VectorProperty.StrokeColor):
1010 Vector3 tmpVector3 = new Vector3(-1, -1, -1);
1011 if ((ret != null) && ret.Get(tmpVector3))
1013 val1 = tmpVector3.X;
1014 val2 = tmpVector3.Y;
1015 val3 = tmpVector3.Z;
1017 tmpVector3.Dispose();
1020 case (int)(VectorProperty.TransformAnchor):
1021 case (int)(VectorProperty.TransformPosition):
1022 case (int)(VectorProperty.TransformScale):
1023 Vector2 tmpVector2 = new Vector2(-1, -1);
1024 if ((ret != null) && ret.Get(tmpVector2))
1026 val1 = tmpVector2.X;
1027 val2 = tmpVector2.Y;
1029 tmpVector2.Dispose();
1032 case (int)(VectorProperty.FillOpacity):
1033 case (int)(VectorProperty.StrokeOpacity):
1034 case (int)(VectorProperty.StrokeWidth):
1035 case (int)(VectorProperty.TransformRotation):
1036 case (int)(VectorProperty.TransformOpacity):
1037 float tmpFloat = -1;
1038 if ((ret != null) && ret.Get(out tmpFloat))
1054 // Collection of lottie-image-sensitive properties.
1055 private static readonly List<int> cachedLottieAnimationPropertyKeyList = new List<int> {
1056 ImageVisualProperty.LoopCount,
1057 ImageVisualProperty.StopBehavior,
1058 ImageVisualProperty.LoopingMode,
1059 ImageVisualProperty.RedrawInScalingDown,
1062 private struct states
1064 internal string url;
1065 internal int loopCount;
1066 internal LoopingModeType loopMode;
1067 internal StopBehaviorType stopEndAction;
1068 internal int framePlayRangeMin;
1069 internal int framePlayRangeMax;
1070 internal int totalFrame;
1071 internal float scale;
1072 internal PlayStateType playState;
1073 internal List<Tuple<string, int, int>> contentInfo;
1074 internal string mark1, mark2;
1075 internal bool redrawInScalingDown;
1076 internal bool changed;
1078 private states currentStates;
1080 private const string tag = "NUITEST";
1081 private event EventHandler finishedEventHandler;
1083 private void OnFinished()
1085 NUILog.Debug($"<[{GetId()}] OnFinished()>");
1086 finishedEventHandler?.Invoke(this, null);
1089 private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
1093 if (targetView != IntPtr.Zero)
1095 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
1098 NUILog.Debug($"targetView is not null! name={v.Name}");
1102 NUILog.Debug($"target is something created from dali");
1105 VisualEventSignalArgs e = new VisualEventSignalArgs();
1106 e.VisualIndex = visualIndex;
1107 e.SignalId = signalId;
1108 visualEventSignalHandler?.Invoke(this, e);
1110 NUILog.Debug($"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
1113 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
1114 private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
1116 private VisualEventSignalCallbackType visualEventSignalCallback;
1117 private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
1119 static private int dynamicPropertyCallbackId = 0;
1120 //static private Dictionary<int, DynamicPropertyCallbackType> dynamicPropertyCallbacks = new Dictionary<int, DynamicPropertyCallbackType>();
1121 static private Dictionary<int, WeakReference<LottieAnimationView>> weakReferencesOfLottie = new Dictionary<int, WeakReference<LottieAnimationView>>();
1123 private void debugPrint()
1125 NUILog.Debug($"===================================");
1126 NUILog.Debug($"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, \nframePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
1127 NUILog.Debug($" get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState}");
1128 NUILog.Debug($" RedrawInScalingDown={RedrawInScalingDown} >");
1129 NUILog.Debug($"===================================");
1136 /// A class containing frame informations for a LottieAnimationView.
1138 [EditorBrowsable(EditorBrowsableState.Never)]
1139 public class LottieFrameInfo : ICloneable
1142 /// Creates a new instance with a playing range.
1144 [EditorBrowsable(EditorBrowsableState.Never)]
1145 public LottieFrameInfo(int startFrame, int endFrame)
1147 StartFrame = startFrame;
1148 EndFrame = endFrame;
1152 /// Creates a new instance with a still image frame.
1154 [EditorBrowsable(EditorBrowsableState.Never)]
1155 public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
1160 /// Create a new instance from a pair notation.
1162 [EditorBrowsable(EditorBrowsableState.Never)]
1163 public static implicit operator LottieFrameInfo((int, int) pair)
1165 return new LottieFrameInfo(pair.Item1, pair.Item2);
1169 /// Create a new instance from an int value.
1171 [EditorBrowsable(EditorBrowsableState.Never)]
1172 public static implicit operator LottieFrameInfo(int stillImageFrame)
1174 return new LottieFrameInfo(stillImageFrame);
1178 /// Create a new instance from string.
1179 /// Possible input : "0, 10", "10"
1181 [EditorBrowsable(EditorBrowsableState.Never)]
1182 public static implicit operator LottieFrameInfo(string pair)
1189 string[] parts = pair.Split(',');
1190 if (parts.Length == 1)
1192 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture));
1194 else if (parts.Length == 2)
1196 return new LottieFrameInfo(Int32.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), Int32.Parse(parts[1].Trim(), CultureInfo.InvariantCulture));
1199 Tizen.Log.Error("NUI", $"Can not convert string {pair} to LottieFrameInfo");
1204 /// The start frame of the lottie animation.
1206 [EditorBrowsable(EditorBrowsableState.Never)]
1207 public int StartFrame { get; }
1210 /// The end frame of the lottie animation.
1212 [EditorBrowsable(EditorBrowsableState.Never)]
1213 public int EndFrame { get; }
1216 /// Create LottieFrameInfo struct with animation range information
1218 [EditorBrowsable(EditorBrowsableState.Never)]
1219 public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
1221 return new LottieFrameInfo(startFrame, endFrame);
1225 /// Create LottieFrameInfo struct with still image information
1227 [EditorBrowsable(EditorBrowsableState.Never)]
1228 public static LottieFrameInfo CreateStillImage(int stillImageFrame)
1230 return new LottieFrameInfo(stillImageFrame, stillImageFrame);
1235 /// Whether this LottieFrameInfo represents one frame or more.
1237 [EditorBrowsable(EditorBrowsableState.Never)]
1238 public bool IsStillImage()
1240 return StartFrame == EndFrame;
1245 /// Play specified LottieAnimationView with this frame information.
1247 /// <param name="lottieView">The target LottieAnimationView to play.</param>
1248 /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
1249 [EditorBrowsable(EditorBrowsableState.Never)]
1250 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062: Validate arguments of public methods", Justification = "The null checking is done by BeReadyToShow()")]
1251 public void Show(LottieAnimationView lottieView, bool noPlay = false)
1253 if (!BeReadyToShow(lottieView))
1258 lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
1260 if (IsStillImage() || noPlay)
1262 lottieView.CurrentFrame = EndFrame;
1266 lottieView.CurrentFrame = StartFrame;
1272 [EditorBrowsable(EditorBrowsableState.Never)]
1273 public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
1275 private bool BeReadyToShow(LottieAnimationView lottieView)
1277 // Validate input lottieView
1278 if (null == lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
1283 // Stop if it was playing
1284 if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)
1293 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
1294 [EditorBrowsable(EditorBrowsableState.Never)]
1295 public struct LottieAnimationViewDynamicProperty : IEquatable<LottieAnimationViewDynamicProperty>
1297 [EditorBrowsable(EditorBrowsableState.Never)]
1298 public string KeyPath { get; set; }
1300 [EditorBrowsable(EditorBrowsableState.Never)]
1301 public LottieAnimationView.VectorProperty Property { get; set; }
1303 [EditorBrowsable(EditorBrowsableState.Never)]
1304 public LottieAnimationView.DynamicPropertyCallbackType Callback { get; set; }
1306 public override bool Equals(object obj)
1308 if (obj is LottieAnimationViewDynamicProperty target)
1310 if (KeyPath == target.KeyPath && Property == target.Property && Callback == target.Callback)
1318 public override int GetHashCode()
1320 return base.GetHashCode();
1323 public static bool operator ==(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1325 return left.Equals(right);
1328 public static bool operator !=(LottieAnimationViewDynamicProperty left, LottieAnimationViewDynamicProperty right)
1330 return !(left == right);
1333 public bool Equals(LottieAnimationViewDynamicProperty other)
1337 if (KeyPath == other.KeyPath && Property == other.Property && Callback == other.Callback)