Updated demos to use DALi clang-format
[platform/core/uifw/dali-demo.git] / examples / dissolve-effect / dissolve-effect-example.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 // EXTERNAL INCLUDES
19 #include <math.h>
20
21 // INTERNAL INCLUDES
22 #include "shared/view.h"
23
24 #include <dali-toolkit/dali-toolkit.h>
25 #include <dali-toolkit/devel-api/shader-effects/dissolve-effect.h>
26 #include <dali/dali.h>
27 #include <dali/devel-api/actors/actor-devel.h>
28
29 using namespace Dali;
30
31 using Dali::Toolkit::TextLabel;
32
33 // LOCAL STUFF
34 namespace
35 {
36 const char* const TOOLBAR_IMAGE(DEMO_IMAGE_DIR "top-bar.png");
37 const char* const APPLICATION_TITLE_HIGHP("Dissolve Effect(highp)");
38 const char* const APPLICATION_TITLE_MEDIUMP("Dissolve Effect(mediump)");
39 const char* const EFFECT_HIGHP_IMAGE(DEMO_IMAGE_DIR "icon-highp.png");
40 const char* const EFFECT_HIGHP_IMAGE_SELECTED(DEMO_IMAGE_DIR "icon-highp-selected.png");
41 const char* const EFFECT_MEDIUMP_IMAGE(DEMO_IMAGE_DIR "icon-mediump.png");
42 const char* const EFFECT_MEDIUMP_IMAGE_SELECTED(DEMO_IMAGE_DIR "icon-mediump-selected.png");
43 const char* const PLAY_ICON(DEMO_IMAGE_DIR "icon-play.png");
44 const char* const PLAY_ICON_SELECTED(DEMO_IMAGE_DIR "icon-play-selected.png");
45 const char* const STOP_ICON(DEMO_IMAGE_DIR "icon-stop.png");
46 const char* const STOP_ICON_SELECTED(DEMO_IMAGE_DIR "icon-stop-selected.png");
47
48 const char* IMAGES[] =
49   {
50     DEMO_IMAGE_DIR "gallery-large-1.jpg",
51     DEMO_IMAGE_DIR "gallery-large-2.jpg",
52     DEMO_IMAGE_DIR "gallery-large-3.jpg",
53     DEMO_IMAGE_DIR "gallery-large-4.jpg",
54     DEMO_IMAGE_DIR "gallery-large-5.jpg",
55     DEMO_IMAGE_DIR "gallery-large-6.jpg",
56     DEMO_IMAGE_DIR "gallery-large-7.jpg",
57     DEMO_IMAGE_DIR "gallery-large-8.jpg",
58     DEMO_IMAGE_DIR "gallery-large-9.jpg",
59     DEMO_IMAGE_DIR "gallery-large-10.jpg",
60     DEMO_IMAGE_DIR "gallery-large-11.jpg",
61     DEMO_IMAGE_DIR "gallery-large-12.jpg",
62     DEMO_IMAGE_DIR "gallery-large-13.jpg",
63     DEMO_IMAGE_DIR "gallery-large-14.jpg",
64     DEMO_IMAGE_DIR "gallery-large-15.jpg",
65     DEMO_IMAGE_DIR "gallery-large-16.jpg",
66     DEMO_IMAGE_DIR "gallery-large-17.jpg",
67     DEMO_IMAGE_DIR "gallery-large-18.jpg",
68     DEMO_IMAGE_DIR "gallery-large-19.jpg",
69     DEMO_IMAGE_DIR "gallery-large-20.jpg",
70     DEMO_IMAGE_DIR "gallery-large-21.jpg",
71 };
72
73 const int NUM_IMAGES(sizeof(IMAGES) / sizeof(IMAGES[0]));
74
75 // The duration of the current image staying on screen when slideshow is on
76 const int VIEWINGTIME = 2000; // 2 seconds
77
78 const float TRANSITION_DURATION = 2.5f; //2.5 second
79
80 const float INITIAL_DEPTH = 10.0f;
81
82 /**
83  * @brief Create an image view with an image which would be scaled-down to no more than the window dimensions.
84  *
85  * Uses image scaling mode SCALE_TO_FILL to resize the image at
86  * load time to cover the entire window with pixels with no borders,
87  * and filter mode BOX_THEN_LINEAR to sample the image with maximum quality.
88  */
89 Toolkit::ImageView CreateWindowFillingImageView(const Vector2& windowSize, const char* const imagePath)
90 {
91   Toolkit::ImageView imageView = Toolkit::ImageView::New();
92   Property::Map      map;
93   map[Toolkit::Visual::Property::TYPE]                     = Toolkit::Visual::IMAGE;
94   map[Toolkit::ImageVisual::Property::URL]                 = imagePath;
95   map[Toolkit::ImageVisual::Property::DESIRED_WIDTH]       = windowSize.x;
96   map[Toolkit::ImageVisual::Property::DESIRED_HEIGHT]      = windowSize.y;
97   map[Toolkit::ImageVisual::Property::FITTING_MODE]        = FittingMode::SCALE_TO_FILL;
98   map[Toolkit::ImageVisual::Property::SAMPLING_MODE]       = SamplingMode::BOX_THEN_LINEAR;
99   map[Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING] = true;
100   imageView.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
101
102   return imageView;
103 }
104
105 } // namespace
106
107 class DissolveEffectApp : public ConnectionTracker
108 {
109 public:
110   /**
111    * Constructor
112    * @param application class, stored as reference
113    */
114   DissolveEffectApp(Application& application);
115
116   ~DissolveEffectApp();
117
118 private:
119   /**
120    * This method gets called once the main loop of application is up and running
121    */
122   void OnInit(Application& application);
123   /**
124    * PanGesture callback. This method gets called when the pan gesture is detected.
125    * @param[in] actor The actor receiving the pan gesture.
126    * @param[in] gesture The detected pan gesture.
127    */
128   void OnPanGesture(Actor actor, const PanGesture& gesture);
129
130   /**
131    * Set up the animations for transition
132    * @param[in] position The point ( locates within rectange {(0,0),(0,1),(1,0),(1,1)} ) passing through the central line of the dissolve effect
133    * @param[in] displacement The direction of the central line of the dissolve effect
134    */
135   void StartTransition(Vector2 position, Vector2 displacement);
136   /**
137    * Callback function of effect-switch button
138    * Change the precision of the effect shader when the effect button is clicked
139    * @param[in] button The handle of the clicked button
140    */
141   bool OnEffectButtonClicked(Toolkit::Button button);
142   /**
143    * Callback function of slideshow button
144    * Start or stop the automatical image display when the slideshow button is clicked
145    * @param[in] button The handle of the clicked button
146    */
147   bool OnSildeshowButtonClicked(Toolkit::Button button);
148   /**
149    * Callback function of cube transition completed signal
150    * @param[in] effect The cube effect used for the transition
151    * @param[in] imageActor The target imageActor of the completed transition
152    */
153   void OnTransitionCompleted(Animation& source);
154   /**
155    * Callback function of timer tick
156    * The timer is used to count the image display duration after cube transition in slideshow,
157    */
158   bool OnTimerTick();
159
160   /**
161    * Main key event handler
162    */
163   void OnKeyEvent(const KeyEvent& event);
164
165 private:
166   Application&       mApplication;
167   Toolkit::Control   mView;
168   Toolkit::ToolBar   mToolBar;
169   Layer              mContent;
170   Toolkit::TextLabel mTitleActor;
171   Actor              mParent;
172
173   Toolkit::ImageView mCurrentImage;
174   Toolkit::ImageView mNextImage;
175   unsigned int       mIndex;
176
177   Property::Map mDissolveEffect;
178   Property::Map mEmptyEffect;
179
180   bool      mUseHighPrecision;
181   Animation mAnimation;
182
183   PanGestureDetector mPanGestureDetector;
184   bool               mIsTransiting;
185
186   bool         mSlideshow;
187   Timer        mViewTimer;
188   bool         mTimerReady;
189   unsigned int mCentralLineIndex;
190
191   Toolkit::PushButton mPlayStopButton;
192   Toolkit::PushButton mEffectChangeButton;
193 };
194
195 DissolveEffectApp::DissolveEffectApp(Application& application)
196 : mApplication(application),
197   mIndex(0),
198   mUseHighPrecision(true),
199   mIsTransiting(false),
200   mSlideshow(false),
201   mTimerReady(false),
202   mCentralLineIndex(0)
203 {
204   mApplication.InitSignal().Connect(this, &DissolveEffectApp::OnInit);
205 }
206
207 DissolveEffectApp::~DissolveEffectApp()
208 {
209   //Nothing to do
210 }
211
212 void DissolveEffectApp::OnInit(Application& application)
213 {
214   auto    window     = application.GetWindow();
215   Vector2 windowSize = window.GetSize();
216   window.KeyEventSignal().Connect(this, &DissolveEffectApp::OnKeyEvent);
217
218   // Creates a default view with a default tool bar, the view is added to the window.
219   mContent = DemoHelper::CreateView(application, mView, mToolBar, "", TOOLBAR_IMAGE, "");
220
221   // Add an effect-changing button on the right of the tool bar.
222   mEffectChangeButton = Toolkit::PushButton::New();
223   mEffectChangeButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, EFFECT_HIGHP_IMAGE);
224   mEffectChangeButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, EFFECT_HIGHP_IMAGE_SELECTED);
225   mEffectChangeButton.ClickedSignal().Connect(this, &DissolveEffectApp::OnEffectButtonClicked);
226   mToolBar.AddControl(mEffectChangeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HORIZONTAL_RIGHT, DemoHelper::DEFAULT_MODE_SWITCH_PADDING);
227
228   // Add title to the tool bar.
229   mTitleActor = DemoHelper::CreateToolBarLabel(APPLICATION_TITLE_HIGHP);
230   mToolBar.AddControl(mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Toolkit::Alignment::HORIZONTAL_CENTER);
231
232   // Add an slide-show button on the right of the title
233   mPlayStopButton = Toolkit::PushButton::New();
234   mPlayStopButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, PLAY_ICON);
235   mPlayStopButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, PLAY_ICON_SELECTED);
236   mPlayStopButton.ClickedSignal().Connect(this, &DissolveEffectApp::OnSildeshowButtonClicked);
237   mToolBar.AddControl(mPlayStopButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HORIZONTAL_CENTER, DemoHelper::DEFAULT_PLAY_PADDING);
238
239   // use pan gesture to detect the cursor or finger movement
240   mPanGestureDetector = PanGestureDetector::New();
241   mPanGestureDetector.DetectedSignal().Connect(this, &DissolveEffectApp::OnPanGesture);
242
243   mViewTimer = Timer::New(VIEWINGTIME);
244   mViewTimer.TickSignal().Connect(this, &DissolveEffectApp::OnTimerTick);
245   mTimerReady = true;
246
247   // Set size to window size to avoid seeing a black border on transition
248   mParent = Actor::New();
249   mParent.SetProperty(Actor::Property::SIZE, windowSize);
250   mParent.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
251   mContent.Add(mParent);
252
253   // show the first image
254   mCurrentImage = CreateWindowFillingImageView(windowSize, IMAGES[mIndex]);
255   mCurrentImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
256   mCurrentImage.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
257   mCurrentImage.SetProperty(Actor::Property::SIZE_SCALE_POLICY, SizeScalePolicy::FIT_WITH_ASPECT_RATIO);
258   mParent.Add(mCurrentImage);
259
260   mPanGestureDetector.Attach(mCurrentImage);
261
262   mDissolveEffect = Dali::Toolkit::CreateDissolveEffect(mUseHighPrecision);
263   Property::Map emptyShaderMap;
264   mEmptyEffect.Insert("shader", emptyShaderMap);
265 }
266
267 // signal handler, called when the pan gesture is detected
268 void DissolveEffectApp::OnPanGesture(Actor actor, const PanGesture& gesture)
269 {
270   // does not response when the animation has not finished
271   if(mIsTransiting || mSlideshow)
272   {
273     return;
274   }
275
276   if(gesture.GetState() == GestureState::CONTINUING)
277   {
278     const Vector2& displacement = gesture.GetDisplacement();
279     if(displacement.x < 0)
280     {
281       mIndex = (mIndex + 1) % NUM_IMAGES;
282     }
283     else
284     {
285       mIndex = (mIndex + NUM_IMAGES - 1) % NUM_IMAGES;
286     }
287
288     mNextImage = CreateWindowFillingImageView(mApplication.GetWindow().GetSize(), IMAGES[mIndex]);
289     mNextImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
290     mNextImage.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
291     mNextImage.SetProperty(Actor::Property::SIZE_SCALE_POLICY, SizeScalePolicy::FIT_WITH_ASPECT_RATIO);
292     mNextImage.SetProperty(Actor::Property::POSITION_Z, INITIAL_DEPTH);
293     mParent.Add(mNextImage);
294     Vector2 size = Vector2(mCurrentImage.GetCurrentProperty<Vector3>(Actor::Property::SIZE));
295     StartTransition(gesture.GetPosition() / size, displacement * Vector2(1.0, size.x / size.y));
296   }
297 }
298
299 void DissolveEffectApp::StartTransition(Vector2 position, Vector2 displacement)
300 {
301   mAnimation = Animation::New(TRANSITION_DURATION);
302
303   Dali::Toolkit::DissolveEffectSetCentralLine(mCurrentImage, position, displacement, 0.0f);
304   mCurrentImage.SetProperty(Toolkit::ImageView::Property::IMAGE, mDissolveEffect);
305   mAnimation.AnimateTo(Property(mCurrentImage, "uPercentage"), 1.0f, AlphaFunction::LINEAR);
306
307   mNextImage.SetProperty(Actor::Property::OPACITY, 0.0f);
308   mAnimation.AnimateTo(Property(mNextImage, Actor::Property::COLOR_ALPHA), 1.0f, AlphaFunction::LINEAR);
309
310   if(mUseHighPrecision)
311   {
312     Dali::Toolkit::DissolveEffectSetCentralLine(mNextImage, position, displacement, 1.0f);
313     mNextImage.SetProperty(Toolkit::ImageView::Property::IMAGE, mDissolveEffect);
314     mAnimation.AnimateTo(Property(mNextImage, "uPercentage"), 0.0f, AlphaFunction::LINEAR);
315   }
316   else
317   {
318     mAnimation.AnimateTo(Property(mNextImage, Actor::Property::POSITION), Vector3(0.0f, 0.0f, 0.0f), AlphaFunction::LINEAR);
319   }
320
321   mAnimation.FinishedSignal().Connect(this, &DissolveEffectApp::OnTransitionCompleted);
322   mAnimation.Play();
323   mIsTransiting = true;
324 }
325
326 void DissolveEffectApp::OnKeyEvent(const KeyEvent& event)
327 {
328   if(event.GetState() == KeyEvent::DOWN)
329   {
330     if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
331     {
332       mApplication.Quit();
333     }
334   }
335 }
336
337 bool DissolveEffectApp::OnEffectButtonClicked(Toolkit::Button button)
338 {
339   mUseHighPrecision = !mUseHighPrecision;
340   mDissolveEffect   = Dali::Toolkit::CreateDissolveEffect(mUseHighPrecision);
341   if(mUseHighPrecision)
342   {
343     mTitleActor.SetProperty(TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_HIGHP));
344     mEffectChangeButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, EFFECT_HIGHP_IMAGE);
345     mEffectChangeButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, EFFECT_HIGHP_IMAGE_SELECTED);
346   }
347   else
348   {
349     mTitleActor.SetProperty(TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_MEDIUMP));
350     mEffectChangeButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, EFFECT_MEDIUMP_IMAGE);
351     mEffectChangeButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, EFFECT_MEDIUMP_IMAGE_SELECTED);
352   }
353
354   return true;
355 }
356
357 bool DissolveEffectApp::OnSildeshowButtonClicked(Toolkit::Button button)
358 {
359   mSlideshow = !mSlideshow;
360   if(mSlideshow)
361   {
362     mPlayStopButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, STOP_ICON);
363     mPlayStopButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, STOP_ICON_SELECTED);
364     mPanGestureDetector.Detach(mParent);
365     mViewTimer.Start();
366     mTimerReady = false;
367   }
368   else
369   {
370     mPlayStopButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, PLAY_ICON);
371     mPlayStopButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, PLAY_ICON_SELECTED);
372     mTimerReady = true;
373     mPanGestureDetector.Attach(mParent);
374   }
375   return true;
376 }
377
378 void DissolveEffectApp::OnTransitionCompleted(Animation& source)
379 {
380   if(mUseHighPrecision)
381   {
382     mNextImage.SetProperty(Toolkit::ImageView::Property::IMAGE, mEmptyEffect);
383   }
384   mParent.Remove(mCurrentImage);
385   mPanGestureDetector.Detach(mCurrentImage);
386   mCurrentImage = mNextImage;
387   mPanGestureDetector.Attach(mCurrentImage);
388   mIsTransiting = false;
389
390   if(mSlideshow)
391   {
392     mViewTimer.Start();
393     mTimerReady = false;
394   }
395 }
396
397 bool DissolveEffectApp::OnTimerTick()
398 {
399   mTimerReady = true;
400   if(mSlideshow)
401   {
402     mIndex     = (mIndex + 1) % NUM_IMAGES;
403     mNextImage = CreateWindowFillingImageView(mApplication.GetWindow().GetSize(), IMAGES[mIndex]);
404     mNextImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
405     mNextImage.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
406     mNextImage.SetProperty(Actor::Property::SIZE_SCALE_POLICY, SizeScalePolicy::FIT_WITH_ASPECT_RATIO);
407     mNextImage.SetProperty(Actor::Property::POSITION_Z, INITIAL_DEPTH);
408     mParent.Add(mNextImage);
409     switch(mCentralLineIndex % 4)
410     {
411       case 0:
412       {
413         StartTransition(Vector2(1.0f, 0.5f), Vector2(-1.0f, 0.0f));
414         break;
415       }
416       case 1:
417       {
418         StartTransition(Vector2(0.5f, 0.0f), Vector2(0.0f, 1.0f));
419         break;
420       }
421       case 2:
422       {
423         StartTransition(Vector2(0.0f, 0.5f), Vector2(1.0f, 0.0f));
424         break;
425       }
426       default:
427       {
428         StartTransition(Vector2(0.5f, 1.0f), Vector2(0.0f, -1.0f));
429         break;
430       }
431     }
432     mCentralLineIndex++;
433   }
434   return false; //return false to stop the timer
435 }
436
437 int DALI_EXPORT_API main(int argc, char** argv)
438 {
439   Application       application = Application::New(&argc, &argv, DEMO_THEME_PATH);
440   DissolveEffectApp test(application);
441   application.MainLoop();
442
443   return 0;
444 }