Changed Toolkit shader effects to be a static function returning a
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 /*
2  * Copyright (c) 2014 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-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/animation/animation.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/devel-api/events/hit-test-algorithm.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/devel-api/object/type-registry-helper.h>
27 #include <dali/public-api/render-tasks/render-task-list.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
31 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
32 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-book-spine-effect.h>
33
34 using namespace Dali;
35
36 namespace //Unnamed namespace
37 {
38 // To register type
39
40 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, NULL )
41 DALI_TYPE_REGISTRATION_END()
42
43 // default grid density for page turn effect, 10 pixels by 10 pixels
44 const float DEFAULT_GRID_DENSITY(10.0f);
45
46 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
47 const float MINIMUM_START_POSITION_RATIO(0.6f);
48
49 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
50 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
51
52 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
53 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
54
55 // duration of animation, shorter for faster speed
56 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
57 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
58
59 // the major&minor radius (in pixels) to form an ellipse shape
60 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
61 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
62
63 // constants for shadow casting
64 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
65 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
66
67 // constraints ////////////////////////////////////////////////////////////////
68 /**
69  * Original Center Constraint
70  *
71  * This constraint adjusts the original center property of the page turn shader effect
72  * based on the X-direction displacement of the pan gesture
73  */
74 struct OriginalCenterConstraint
75 {
76   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
77   : mOldCenter( originalCenter )
78   {
79     mNewCenter = originalCenter + offset;
80     mDistance = offset.Length() * 0.5f;
81     mDirection = offset  / mDistance;
82   }
83
84   void operator()( Vector2& current, const PropertyInputContainer& inputs )
85   {
86     float displacement = inputs[0]->GetFloat();
87
88     if( displacement < mDistance )
89     {
90       current = mOldCenter + mDirection * displacement;
91     }
92     else
93     {
94       current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
95     }
96   }
97
98   Vector2 mOldCenter;
99   Vector2 mNewCenter;
100   float mDistance;
101   Vector2 mDirection;
102 };
103
104 /**
105  * Rotation Constraint
106  *
107  * This constraint adjusts the rotation property of the page actor
108  * based on the X-direction displacement of the pan gesture
109  */
110 struct RotationConstraint
111 {
112   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
113   : mDistance( distance*0.5f )
114   {
115     mStep = 1.f / pageWidth;
116     mSign = isTurnBack ? -1.0f : 1.0f;
117     mConst = isTurnBack ? -1.0f : 0.f;
118     mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
119   }
120
121   void operator()( Quaternion& current, const PropertyInputContainer& inputs )
122   {
123     float displacement = inputs[0]->GetFloat();
124     if( displacement < mDistance)
125     {
126       current = mRotation;
127     }
128     else
129     {
130       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
131       float angle = Math::PI * ( mConst + mSign * coef );
132       current = Quaternion( Radian( angle ), Vector3::YAXIS );
133     }
134   }
135
136   float mDistance;
137   float mStep;
138   float mConst;
139   float mSign;
140   Quaternion mRotation;
141 };
142
143 /**
144  * Current Center Constraint
145  *
146  * This constraint adjusts the current center property of the page turn shader effect
147  * based on the pan position and the original center position
148  */
149 struct CurrentCenterConstraint
150 {
151   CurrentCenterConstraint( float pageWidth )
152   : mPageWidth( pageWidth )
153   {
154     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
155   }
156
157   void operator()( Vector2& current, const PropertyInputContainer& inputs )
158   {
159     const Vector2& centerPosition = inputs[0]->GetVector2();
160     if( centerPosition.x > 0.f )
161     {
162       current.x = mThres+centerPosition.x * 0.5f;
163       current.y = centerPosition.y;
164     }
165     else
166     {
167       const Vector2& centerOrigin = inputs[1]->GetVector2();
168       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
169       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
170       // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
171       if(coef < 0.025f)
172       {
173         coef = (coef+0.225f)/10.0f;
174       }
175       current = centerOrigin - direction * coef;
176     }
177   }
178
179   float mPageWidth;
180   float mThres;
181 };
182
183 struct ShadowBlurStrengthConstraint
184 {
185   ShadowBlurStrengthConstraint( float thres )
186   : mThres( thres )
187   {}
188
189   void operator()( float& blurStrength,  const PropertyInputContainer& inputs )
190   {
191     float displacement = inputs[2]->GetFloat();
192     if( EqualsZero(displacement))
193     {
194       const Vector2& cur = inputs[0]->GetVector2();
195       const Vector2& ori = inputs[1]->GetVector2();
196       blurStrength =  5.f*(ori-cur).Length() / mThres;
197     }
198     else
199     {
200       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
201     }
202
203     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
204   }
205
206   float mThres;
207 };
208
209 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
210 {
211   bool hittable = false;
212
213   switch (type)
214   {
215     case Dali::HitTestAlgorithm::CHECK_ACTOR:
216     {
217       // Check whether the actor is visible and not fully transparent.
218       Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
219       if( actor.IsSensitive()
220        && actor.IsVisible()
221        && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
222        && ( propertyActorHittable != Property::INVALID_INDEX &&
223             actor.GetProperty<bool>( propertyActorHittable ) ) )
224       {
225          hittable = true;
226       }
227       break;
228     }
229     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
230     {
231       if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
232       {
233         hittable = true;
234       }
235       break;
236     }
237     default:
238     {
239       break;
240     }
241   }
242
243   return hittable;
244 }
245
246 } //unnamed namespace
247
248 namespace Dali
249 {
250
251 namespace Toolkit
252 {
253
254 namespace Internal
255 {
256
257 // these several constants are also used in the derived classes
258 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
259 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
260 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
261 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
262
263 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
264 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
265   mPageFactory( pageFactory ),
266   mPageSize( pageSize ),
267   mTotalPageCount( 0 ),
268   mIsEditMode( false ),
269   mNeedOffscreenRendering( false ),
270   mPanning( false ),
271   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
272   mCurrentPageIndex( 0 ),
273   mIndex( 0 ),
274   mPress( false ),
275   mPageUpdated( true ),
276   mDistanceUpCorner( 0.f ),
277   mDistanceBottomCorner( 0.f ),
278   mPanDisplacement( 0.f ),
279   mConstraints( false ),
280   mPageTurnStartedSignal(),
281   mPageTurnFinishedSignal(),
282   mPagePanStartedSignal(),
283   mPagePanFinishedSignal()
284 {
285   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
286   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
287   mIsSliding.resize( MAXIMUM_TURNING_NUM );
288   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
289   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
290   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
291 }
292
293 PageTurnView::~PageTurnView()
294 {
295 }
296
297 void PageTurnView::OnInitialize()
298 {
299    // create the two book spine effect for static images, left and right side pages respectively
300   mSpineEffectFront = CreatePageTurnBookSpineEffect();
301   mSpineEffectFront.SetUniform("uIsBackImageVisible", false );
302   mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
303   mSpineEffectFront.SetUniform("uShadowWidth", 0.f );
304   mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
305
306   mSpineEffectBack = CreatePageTurnBookSpineEffect();
307   mSpineEffectBack.SetUniform("uIsBackImageVisible", true );
308   mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
309   mSpineEffectBack.SetUniform("uShadowWidth", 0.f );
310   mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
311
312   // create the page turn effect objects
313   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
314   {
315     mTurnEffect[i] = CreatePageTurnEffect( false );
316     mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
317     mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
318     mTurnEffect[i].SetUniform( "uShadowWidth", 0.f);
319     mTurnEffect[i].SetUniform( "uSpineShadowParameter", mSpineShadowParameter );
320     mIsAnimating[i] = false;
321     mIsSliding[i] = false;
322     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
323     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
324   }
325
326   mTurningPageLayer = Layer::New();
327   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
328   // Set control size and the parent origin of turningPageLayer
329   OnPageTurnViewInitialize();
330
331   mRootOnScreen = Actor::New();
332   mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
333   mRootOnScreen.SetSize( mControlSize );
334   Self().Add( mRootOnScreen );
335   mRootOnScreen.Add(mTurningPageLayer);
336
337   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
338   mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
339   if( mNeedOffscreenRendering )
340   {
341     SetupRenderTasks();
342   }
343
344   // add pages to the scene, and set depth for the stacked pages
345   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
346   {
347     AddPage( i );
348     if(mPageActors[i])
349     {
350       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
351     }
352   }
353
354   // enable the pan gesture which is attached to the control
355   EnableGestureDetection(Gesture::Type(Gesture::Pan));
356
357   mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
358 }
359
360 void PageTurnView::SetupRenderTasks()
361 {
362   mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
363   mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
364   mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
365
366   mCameraActor = CameraActor::New(mControlSize);
367   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
368   mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
369   mCameraActor.SetInheritScale( false );
370   Self().Add(mCameraActor);
371
372   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
373   for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
374   {
375     mPageSourceActor[i] = Actor::New();
376     mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
377     mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
378     mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
379     mPageSourceActor[i].SetInheritScale( false );
380     Self().Add( mPageSourceActor[i] );
381     mPageSourceActor[i].SetSensitive( false );
382
383     mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
384     mOffscreenTask[i] = taskList.CreateTask();
385     mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
386     mOffscreenTask[i].SetCameraActor(mCameraActor);
387     mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
388     mOffscreenTask[i].SetExclusive(true);
389     mOffscreenTask[i].SetInputEnabled( false );
390     mOffscreenTask[i].SetClearEnabled( true );
391     mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
392     mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
393     mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
394    }
395 }
396
397 void PageTurnView::SetupShadowView()
398 {
399   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
400   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
401   mShadowView.SetParentOrigin( origin );
402   mShadowView.SetAnchorPoint( origin );
403   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
404   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
405
406   mShadowLayer = Layer::New();
407   mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
408   mRootOnScreen.Add(mShadowLayer);
409   mShadowLayer.Raise();
410
411   mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
412   mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
413   mShadowPlane.SetSize( mControlSize );
414   mShadowLayer.Add( mShadowPlane );
415   mShadowView.SetShadowPlane( mShadowPlane );
416
417   mPointLight = Actor::New();
418   mPointLight.SetAnchorPoint( origin );
419   mPointLight.SetParentOrigin( origin );
420   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
421   mRootOnScreen.Add( mPointLight );
422   mShadowView.SetPointLight( mPointLight );
423
424   mTurningPageLayer.Add( mShadowView );
425   mShadowView.Activate();
426 }
427
428 void PageTurnView::OnControlStageConnection()
429 {
430   SetupShadowView();
431   mTurningPageLayer.RaiseToTop();
432 }
433
434 void PageTurnView::OnControlStageDisconnection()
435 {
436   if(mShadowView)
437   {
438     Self().Remove(mPointLight);
439     Self().Remove(mShadowLayer);
440     mTurningPageLayer.Remove( mShadowView );
441   }
442
443   // make sure the status of the control is updated correctly when the pan gesture is interrupted
444   if(mPanning)
445   {
446     mPanning = false;
447
448     mRootOnScreen.Add(mPanActor);
449     mIsAnimating[mIndex] = false;
450     mPanActor.RemoveConstraints();
451     mTurnEffect[mIndex].RemoveConstraints();
452     mPageUpdated = true;
453
454     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
455   }
456 }
457
458 void PageTurnView::OnControlSizeSet( const Vector3& size )
459 {
460   // disable the SetSize of the control from the application
461   Self().SetSize( mControlSize );
462 }
463
464 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
465 {
466   mSpineShadowParameter = spineShadowParameter;
467
468   // set spine shadow parameter to all the shader effects
469   mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
470   mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
471   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
472   {
473     mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
474   }
475 }
476
477 Vector2 PageTurnView::GetSpineShadowParameter()
478 {
479   return mSpineShadowParameter;
480 }
481
482 void PageTurnView::GoToPage( unsigned int pageId )
483 {
484   int pageIdx = static_cast<int>(pageId);
485   // record the new current page index
486   mCurrentPageIndex = pageIdx;
487
488   // clear the old pages
489   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
490   {
491     if( mPageActors[i] )
492     {
493       mPageActors[i].Unparent();
494       mPageActors[i].Reset();
495     }
496   }
497
498   // add the current page and the pages right before and after it
499   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
500   {
501     AddPage( i );
502   }
503   // set ordered depth to the stacked pages
504   OrganizePageDepth();
505 }
506
507 unsigned int PageTurnView::GetCurrentPage()
508 {
509   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
510   return static_cast< unsigned int >( mCurrentPageIndex );
511 }
512
513 Actor PageTurnView::EnterEditMode()
514 {
515   if( mNeedOffscreenRendering )
516   {
517     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
518
519     mIsEditMode = true;
520
521     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
522     mOffscreenTask[index].SetInputEnabled( true );
523     mPageSourceActor[index].SetSensitive( true );
524     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
525
526     mRootOnScreen.SetSensitive(false);
527
528     return mPageSourceActor[index].GetChildAt( 0 );
529   }
530   else
531   {
532     return Actor();
533   }
534 }
535
536 void PageTurnView::LeaveEditMode()
537 {
538   if( mNeedOffscreenRendering )
539   {
540     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
541
542     mIsEditMode = false;
543
544     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
545     mOffscreenTask[index].SetInputEnabled( false );
546     mPageSourceActor[index].SetSensitive( false );
547     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
548
549     mRootOnScreen.SetSensitive(true);
550   }
551 }
552
553 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
554 {
555   if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
556   {
557     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
558
559     Dali::HitTestAlgorithm::Results results;
560     if( !mOffscreenTask[index].GetInputEnabled() )
561     {
562       mOffscreenTask[index].SetInputEnabled( true );
563       mPageSourceActor[index].SetSensitive( true );
564       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
565       mOffscreenTask[index].SetInputEnabled( false );
566       mPageSourceActor[index].SetSensitive( false );
567     }
568     else
569     {
570       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
571     }
572     actorCoordinates = results.actorCoordinates;
573     return results.actor;
574   }
575   else
576   {
577     return Actor();
578   }
579 }
580
581 void PageTurnView::AddPage( int pageIndex )
582 {
583   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
584   {
585     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
586     ImageActor newPage;
587     if( mNeedOffscreenRendering )
588     {
589       Actor source = mPageFactory.NewPage( pageIndex );
590       if( mPageSourceActor[index].GetChildCount() > 0 )
591       {
592         mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
593       }
594       mPageSourceActor[index].Add( source );
595       mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
596       newPage = NewPageFromRenderBuffer( pageIndex );
597     }
598     else
599     {
600       newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
601       DALI_ASSERT_ALWAYS( newPage );
602     }
603     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
604     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
605     newPage.SetSize( mPageSize );
606     mRootOnScreen.Add( newPage );
607     mPageActors[index] = newPage;
608
609     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
610     mIsTurnBack[ newPage ] = isLeftSide;
611     if( isLeftSide )
612     {
613       // new page is added to the left side, so need to rotate it 180 degrees
614       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
615     }
616     else
617     {
618       SetShaderEffect( newPage, mSpineEffectFront);
619     }
620
621     // For Portrait, nothing to do
622     // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists
623     OnAddPage( newPage, isLeftSide );
624   }
625 }
626
627 void PageTurnView::RemovePage( int pageIndex )
628 {
629   if( pageIndex > -1 && pageIndex < mTotalPageCount)
630   {
631     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
632     mPageActors[index].Unparent();
633     mIsTurnBack.erase( mPageActors[index] );
634     mPageActors[index].Reset();
635     if( mNeedOffscreenRendering )
636     {
637       mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
638     }
639   }
640 }
641
642 void PageTurnView::RenderPage( int pageIndex )
643 {
644   if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
645    && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
646   {
647     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
648     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
649   }
650 }
651
652 void PageTurnView::RefreshAll()
653 {
654   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
655   if( mTotalPageCount > 0 )
656   {
657     if(mCurrentPageIndex < mTotalPageCount)
658     {
659       GoToPage( mCurrentPageIndex );
660     }
661     else
662     {
663       GoToPage( mCurrentPageIndex-- );
664     }
665   }
666 }
667
668 void PageTurnView::RefreshCurrentPage()
669 {
670   RenderPage( mCurrentPageIndex );
671 }
672
673 void PageTurnView::OnPan( const PanGesture& gesture )
674 {
675   if( mIsEditMode )
676   {
677     // when interrupted by the call of DisplayCurrentPageSourceActor(),
678     // make sure the panFinished is always called before stopping to responding the gesture
679     // so the status of the control is updated correctly
680     if(mPanning)
681     {
682       mPanning = false;
683       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
684     }
685
686     return;
687   }
688   // the pan gesture is attached to control itself instead of each page
689   switch( gesture.state )
690   {
691     case Gesture::Started:
692     {
693       mPanning = true;
694       // to find out whether the undergoing turning page number already reaches the maximum allowed
695       // and get one idle index when it is animatable
696       bool animatable = false;
697       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
698       {
699         if( !mIsAnimating[mIndex] )
700         {
701           animatable = true;
702           break;
703         }
704         if( mIsSliding[mIndex] )
705         {
706           animatable = false;
707           break;
708         }
709         mIndex++;
710         mIndex = mIndex % MAXIMUM_TURNING_NUM;
711       }
712
713       if( mPageUpdated && animatable )
714       {
715         SetPanActor( gesture.position ); // determine which page actor is panned
716         if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
717         {
718           mPanActor.Reset();
719         }
720         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
721       }
722       else
723       {
724         mPanActor.Reset();
725       }
726       break;
727     }
728     case Gesture::Continuing:
729     {
730       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
731       break;
732     }
733     case Gesture::Finished:
734     case Gesture::Cancelled:
735     {
736       mPanning = false;
737       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
738       break;
739     }
740     case Gesture::Clear:
741     case Gesture::Possible:
742     default:
743     {
744       break;
745     }
746   }
747 }
748
749 void PageTurnView::PanStarted( const Vector2& gesturePosition )
750 {
751   mPressDownPosition = gesturePosition;
752
753   if( !mPanActor )
754   {
755     return;
756   }
757
758   mOriginalCenter = gesturePosition;
759   mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] );
760   mPress = false;
761   mPageUpdated = false;
762
763   // Guard against destruction during signal emission
764   Toolkit::PageTurnView handle( GetOwner() );
765   mPagePanStartedSignal.Emit( handle );
766 }
767
768 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
769 {
770   if( !mPanActor )
771   {
772     return;
773   }
774
775   // Guard against destruction during signal emission
776   Toolkit::PageTurnView handle( GetOwner() );
777
778   if(!mPress)
779   {
780     // when the touch down position is near the spine
781     // or when the panning goes outwards or some other position which would tear the paper in real situation
782     // we change the start position into the current panning position and update the shader parameters
783     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
784         || gesturePosition.x > mOriginalCenter.x-1.0f
785         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
786              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
787     {
788       mOriginalCenter = gesturePosition;
789     }
790     else
791     {
792       mDistanceUpCorner = mOriginalCenter.Length();
793       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
794       mShadowView.Add( mPanActor );
795       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
796       mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
797       mCurrentCenter = mOriginalCenter;
798       mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
799       mPanDisplacement = 0.f;
800       mConstraints = true;
801       mPress = true;
802       mIsAnimating[mIndex] = true;
803
804       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
805
806       mShadowView.RemoveConstraints();
807       Actor self = Self();
808       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
809
810       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
811       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
812       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
813       shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
814       shadowBlurStrengthConstraint.Apply();
815     }
816   }
817   else
818   {
819     Vector2 currentCenter = gesturePosition;
820
821     // Test whether the new current center would tear the paper from the top pine in real situation
822     // we do not forbid this totally, which would restrict the panning gesture too much
823     // instead, set it to the nearest allowable position
824     float distanceUpCorner = currentCenter.Length();
825     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
826     if( distanceUpCorner > mDistanceUpCorner )
827     {
828       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
829     }
830     // would tear the paper from the bottom spine in real situation
831     if( distanceBottomCorner > mDistanceBottomCorner )
832     {
833       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
834     }
835     // If direction has a very high y component, reduce it.
836     Vector2 curveDirection = currentCenter - mOriginalCenter;
837     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
838     {
839       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
840     }
841     // If the vertical distance is high, reduce it
842     float yShift = currentCenter.y - mOriginalCenter.y;
843     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
844     {
845       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
846     }
847
848     // use contraints to control the page shape and rotation when the pan position is near the spine
849     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
850     {
851       // set the property values used by the constraints
852       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
853       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
854       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
855
856       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
857       // also set up the RotationConstraint to the page actor
858       if( mConstraints )
859       {
860         Vector2 corner;
861         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
862         if( currentCenter.y >= mOriginalCenter.y )
863         {
864           corner = Vector2( 1.1f*mPageSize.width, 0.f );
865         }
866         else
867         {
868           corner = mPageSize*1.1f;
869         }
870
871         Vector2 offset( currentCenter-mOriginalCenter );
872         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
873                    /( offset.x*offset.x + offset.y*offset.y );
874         offset *= k;
875         Actor self = Self();
876
877         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
878         Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
879         originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
880         originalCenterConstraint.Apply();
881
882         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
883         Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
884         currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
885         currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
886         currentCenterConstraint.Apply();
887
888         PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
889
890         float distance = offset.Length();
891         Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
892         rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
893         rotationConstraint.Apply();
894
895         mConstraints = false;
896       }
897     }
898     else
899     {
900       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
901       {
902         mPanActor.RemoveConstraints();
903         mTurnEffect[mIndex].RemoveConstraints();
904         mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
905         mConstraints = true;
906         mPanDisplacement = 0.f;
907       }
908
909       mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
910       mCurrentCenter = currentCenter;
911       PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
912     }
913   }
914 }
915
916 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
917 {
918   // Guard against destruction during signal emission
919   Toolkit::PageTurnView handle( GetOwner() );
920
921   if( !mPanActor )
922   {
923     if(!mIsAnimating[mIndex])
924     {
925       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
926     }
927     return;
928   }
929
930   mPagePanFinishedSignal.Emit( handle );
931
932   ImageActor actor = mPanActor;
933   if(mPress)
934   {
935     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
936     {
937       // update the pages here instead of in the TurnedOver callback function
938       // as new page is allowed to respond to the pan gesture before other pages finishing animation
939       if(mIsTurnBack[actor])
940       {
941         mCurrentPageIndex--;
942         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
943         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
944       }
945       else
946       {
947         mCurrentPageIndex++;
948         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
949         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
950       }
951       OrganizePageDepth();
952
953       // set up an animation to turn the page over
954       Actor self = Self();
955       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
956       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
957       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
958                            width,AlphaFunction::EASE_OUT_SINE);
959       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
960                            Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
961       mAnimationActorPair[animation] = actor;
962       mAnimationIndexPair[animation] = mIndex;
963       animation.Play();
964       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
965     }
966     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
967     {
968       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
969       animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
970                            mOriginalCenter, AlphaFunction::LINEAR );
971       mAnimationActorPair[animation] = actor;
972       mAnimationIndexPair[animation] = mIndex;
973       animation.Play();
974       mIsSliding[mIndex] = true;
975       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
976
977       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
978     }
979   }
980   else
981   {
982     // In portrait view, an outwards flick should turn the previous page back
983     // In landscape view, nothing to do
984     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
985   }
986
987   mPageUpdated = true;
988 }
989
990 void PageTurnView::TurnedOver( Animation& animation )
991 {
992   ImageActor actor = mAnimationActorPair[animation];
993   mIsTurnBack[actor] = !mIsTurnBack[actor];
994   actor.RemoveConstraints();
995   mRootOnScreen.Add(actor);
996   int index = mAnimationIndexPair[animation];
997   mIsAnimating[index] = false;
998   mTurnEffect[index].RemoveConstraints();
999   mAnimationIndexPair.erase( animation );
1000   mAnimationActorPair.erase( animation );
1001
1002   SetSpineEffect( actor, mIsTurnBack[actor] );
1003
1004   // Guard against destruction during signal emission
1005   Toolkit::PageTurnView handle( GetOwner() );
1006   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1007 }
1008
1009 void PageTurnView::SliddenBack( Animation& animation )
1010 {
1011   ImageActor actor = mAnimationActorPair[animation];
1012   mRootOnScreen.Add(actor);
1013   int index = mAnimationIndexPair[animation];
1014   mIsSliding[index] = false;
1015   mIsAnimating[index] = false;
1016   mAnimationIndexPair.erase( animation );
1017   mAnimationActorPair.erase( animation );
1018
1019   SetSpineEffect( actor, mIsTurnBack[actor] );
1020
1021   // Guard against destruction during signal emission
1022   Toolkit::PageTurnView handle( GetOwner() );
1023   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1024 }
1025
1026 void PageTurnView::OrganizePageDepth()
1027 {
1028   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1029   {
1030     if(mCurrentPageIndex+i < mTotalPageCount)
1031     {
1032       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1033     }
1034     if( mCurrentPageIndex >= i + 1 )
1035     {
1036       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1037     }
1038   }
1039 }
1040
1041 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1042 {
1043   SetShaderEffectRecursively( actor, shaderEffect );
1044 }
1045
1046 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1047 {
1048   return mPageTurnStartedSignal;
1049 }
1050
1051 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1052 {
1053   return mPageTurnFinishedSignal;
1054 }
1055
1056 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1057 {
1058   return mPagePanStartedSignal;
1059 }
1060
1061 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1062 {
1063   return mPagePanFinishedSignal;
1064 }
1065
1066 } // namespace Internal
1067
1068 } // namespace Toolkit
1069
1070 } // namespace Dali