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