Replace some Dali::Actor public APIs with new properties
[platform/core/uifw/dali-demo.git] / examples / image-scaling-and-filtering / image-scaling-and-filtering-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 #include <dali/dali.h>
19 #include <dali/devel-api/actors/actor-devel.h>
20 #include <dali-toolkit/dali-toolkit.h>
21 #include <dali-toolkit/devel-api/controls/popup/popup.h>
22 #include "shared/view.h"
23 #include <iostream>
24
25 using namespace Dali;
26 using Toolkit::TextLabel;
27
28 namespace
29 {
30
31 const char* BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-gradient.jpg" );
32 const Vector4 BACKGROUND_COLOUR( 1.0f, 1.0f, 1.0f, 0.15f );
33
34 const char* BORDER_IMAGE( DEMO_IMAGE_DIR "border-4px.9.png" );
35 const int BORDER_WIDTH = ( 11.0f + 4.0f ); // Shadow size = 11, border size = 4.
36 const char* RESIZE_HANDLE_IMAGE( DEMO_IMAGE_DIR "resize-handle.png" );
37
38 const int MARGIN_SIZE = 10;
39
40 const char* const NEXT_BUTTON_ID = "NEXT_BUTTON";
41 const char* const PREVIOUS_BUTTON_ID = "PREVIOUS_BUTTON";
42 const char * const DALI_ICON_PLAY = DEMO_IMAGE_DIR "icon-play.png";
43
44 const char* const FITTING_BUTTON_ID = "FITTING_BUTTON";
45 const char* const SAMPLING_BUTTON_ID = "SAMPLING_BUTTON";
46 const char* const FITTING_BUTTON_TEXT = "Fitting";
47 const char* const SAMPLING_BUTTON_TEXT = "Sampling";
48
49 const char* const STYLE_LABEL_TEXT  = "ImageScalingGroupLabel";
50 const char* const STYLE_BUTTON_TEXT = "ImageScalingButton";
51
52 const char* IMAGE_PATHS[] =
53 {
54   // Variety of sizes, shapes and formats:
55   DEMO_IMAGE_DIR "dali-logo.png",
56   DEMO_IMAGE_DIR "layer1.png",
57   DEMO_IMAGE_DIR "layer2.png",
58   DEMO_IMAGE_DIR "animation-list.png",
59   DEMO_IMAGE_DIR "music-libray-main-screen.png",
60   DEMO_IMAGE_DIR "music-libray-record-cover.png",
61   DEMO_IMAGE_DIR "contacts-background.png",
62   DEMO_IMAGE_DIR "portrait_screen_primitive_shapes.gif",
63   DEMO_IMAGE_DIR "landscape_screen_primitive_shapes.gif",
64   DEMO_IMAGE_DIR "square_primitive_shapes.bmp",
65   DEMO_IMAGE_DIR "gallery-large-14.jpg",
66   DEMO_IMAGE_DIR "book-landscape-cover.jpg",
67   DEMO_IMAGE_DIR "book-portrait-p1.jpg",
68   DEMO_IMAGE_DIR "book-landscape-cover-back.jpg",
69
70   // Worst case for aliasing in downscaling, 2k x 2k 1 bit per pixel dithered
71   // black and white image:
72   DEMO_IMAGE_DIR "gallery-large-14.wbmp",
73
74   DEMO_IMAGE_DIR "background-1.jpg",
75   DEMO_IMAGE_DIR "background-blocks.jpg",
76   DEMO_IMAGE_DIR "background-magnifier.jpg",
77   DEMO_IMAGE_DIR "gallery-large-14.jpg",
78   NULL
79 };
80 const int NUM_IMAGE_PATHS = sizeof(IMAGE_PATHS) / sizeof(IMAGE_PATHS[0]) - 1u;
81
82 /** Cycle the scaling mode options. */
83 FittingMode::Type NextScalingMode( FittingMode::Type oldMode )
84 {
85   FittingMode::Type newMode = FittingMode::SHRINK_TO_FIT;
86   switch ( oldMode )
87   {
88     case FittingMode::SHRINK_TO_FIT:
89       newMode = FittingMode::SCALE_TO_FILL;
90       break;
91     case FittingMode::SCALE_TO_FILL:
92       newMode = FittingMode::FIT_WIDTH;
93       break;
94     case FittingMode::FIT_WIDTH:
95       newMode = FittingMode::FIT_HEIGHT;
96       break;
97     case FittingMode::FIT_HEIGHT:
98       newMode = FittingMode::SHRINK_TO_FIT;
99       break;
100   }
101   return newMode;
102 }
103
104 /** Cycle through filter mode options. */
105 SamplingMode::Type NextFilterMode( SamplingMode::Type oldMode )
106 {
107   SamplingMode::Type newMode = SamplingMode::BOX;
108
109   switch ( oldMode )
110   {
111     case SamplingMode::BOX:
112       newMode = SamplingMode::NEAREST;
113       break;
114     case SamplingMode::NEAREST:
115       newMode = SamplingMode::LINEAR;
116       break;
117     case SamplingMode::LINEAR:
118       newMode = SamplingMode::BOX_THEN_NEAREST;
119       break;
120     case SamplingMode::BOX_THEN_NEAREST:
121       newMode = SamplingMode::BOX_THEN_LINEAR;
122       break;
123     case SamplingMode::BOX_THEN_LINEAR:
124       newMode = SamplingMode::NO_FILTER;
125       break;
126     case SamplingMode::NO_FILTER:
127       newMode = SamplingMode::BOX;
128       break;
129     case SamplingMode::DONT_CARE:
130       newMode = SamplingMode::BOX;
131       break;
132   }
133   return newMode;
134 }
135
136 const char* StringFromScalingMode( FittingMode::Type scalingMode )
137 {
138   return scalingMode == FittingMode::SCALE_TO_FILL ? "SCALE_TO_FILL" : scalingMode == FittingMode::SHRINK_TO_FIT ? "SHRINK_TO_FIT" : scalingMode == FittingMode::FIT_WIDTH ? "FIT_WIDTH" : scalingMode == FittingMode::FIT_HEIGHT ? "FIT_HEIGHT" : "UnknownScalingMode";
139 }
140
141 const char* StringFromFilterMode( SamplingMode::Type filterMode )
142 {
143   return filterMode == SamplingMode::BOX ? "BOX" : filterMode == SamplingMode::BOX_THEN_NEAREST ? "BOX_THEN_NEAREST" : filterMode == SamplingMode::BOX_THEN_LINEAR ? "BOX_THEN_LINEAR" : filterMode == SamplingMode::NEAREST ? "NEAREST" : filterMode == SamplingMode::LINEAR ? "LINEAR" : filterMode == SamplingMode::NO_FILTER ? "NO_FILTER" : filterMode == SamplingMode::DONT_CARE ? "DONT_CARE" : "UnknownFilterMode";
144 }
145
146 }
147
148 // This example shows the load-time image scaling and filtering features.
149 //
150 class ImageScalingAndFilteringController : public ConnectionTracker
151 {
152 public:
153
154   ImageScalingAndFilteringController( Application& application )
155   : mApplication( application ),
156     mLastPinchScale( 1.0f ),
157     mImageStageScale( 0.5f, 0.5f ),
158     mCurrentPath( 0 ),
159     mFittingMode( FittingMode::FIT_WIDTH ),
160     mSamplingMode( SamplingMode::BOX_THEN_LINEAR),
161     mImageLoading( false ),
162     mQueuedImageLoad( false )
163   {
164     // Connect to the Application's Init signal
165     mApplication.InitSignal().Connect( this, &ImageScalingAndFilteringController::Create );
166   }
167
168   ~ImageScalingAndFilteringController()
169   {
170     // Nothing to do here;
171   }
172
173   // The Init signal is received once (only) during the Application lifetime
174   void Create( Application& application )
175   {
176     // Get a handle to the stage
177     Stage stage = Stage::GetCurrent();
178
179     // Hide the indicator bar
180     application.GetWindow().ShowIndicator( Dali::Window::INVISIBLE );
181
182     // Background image:
183     Dali::Property::Map backgroundImage;
184     backgroundImage.Insert( Toolkit::Visual::Property::TYPE,  Toolkit::Visual::IMAGE );
185     backgroundImage.Insert( Toolkit::ImageVisual::Property::URL,  BACKGROUND_IMAGE );
186     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, stage.GetSize().width );
187     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, stage.GetSize().height );
188     backgroundImage.Insert( Toolkit::ImageVisual::Property::FITTING_MODE,   FittingMode::SCALE_TO_FILL );
189     backgroundImage.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE,   SamplingMode::BOX_THEN_NEAREST );
190
191     Toolkit::ImageView background = Toolkit::ImageView::New();
192     background.SetProperty( Toolkit::ImageView::Property::IMAGE, backgroundImage );
193     background.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
194     background.SetProperty( Actor::Property::SIZE, stage.GetSize() );
195     stage.Add( background );
196
197     mDesiredBox = Toolkit::ImageView::New( BORDER_IMAGE );
198     background.Add( mDesiredBox );
199
200     mDesiredBox.SetProperty( Actor::Property::SIZE, stage.GetSize() * mImageStageScale );
201     mDesiredBox.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
202     mDesiredBox.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
203
204     // Initialize the actor
205     mImageView = Toolkit::ImageView::New( IMAGE_PATHS[ 0 ] );
206
207     // Reposition the actor
208     mImageView.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
209     mImageView.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
210
211     // Display the actor on the stage
212     mDesiredBox.Add( mImageView );
213
214     mImageView.SetProperty( Actor::Property::SIZE, stage.GetSize() * mImageStageScale );
215
216     // Setup the pinch detector for scaling the desired image load dimensions:
217     mPinchDetector = PinchGestureDetector::New();
218     mPinchDetector.Attach( mImageView );
219     mPinchDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPinch );
220
221     mGrabCorner = Toolkit::ImageView::New( RESIZE_HANDLE_IMAGE );
222     mGrabCorner.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
223     mGrabCorner.SetProperty( Dali::Actor::Property::NAME, "GrabCorner" );
224     mGrabCorner.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
225     mGrabCorner.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT );
226     mGrabCorner.SetProperty( Actor::Property::POSITION, Vector2( -BORDER_WIDTH, -BORDER_WIDTH ));
227     mGrabCorner.SetProperty( Actor::Property::OPACITY, 0.6f );
228
229     Layer grabCornerLayer = Layer::New();
230     grabCornerLayer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
231     grabCornerLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT );
232     grabCornerLayer.Add( mGrabCorner );
233     mDesiredBox.Add( grabCornerLayer );
234
235     mPanGestureDetector = PanGestureDetector::New();
236     mPanGestureDetector.Attach( mGrabCorner );
237     mPanGestureDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPan );
238
239     // Tie-in input event handlers:
240     stage.KeyEventSignal().Connect( this, &ImageScalingAndFilteringController::OnKeyEvent );
241
242     CreateControls();
243
244     ResizeImage();
245   }
246
247   /**
248    * Create the GUI controls which float above the scene
249    */
250   void CreateControls()
251   {
252     Stage stage = Stage::GetCurrent();
253
254     Dali::Layer controlsLayer = Dali::Layer::New();
255     controlsLayer.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
256     controlsLayer.SetProperty( Actor::Property::SIZE_MODE_FACTOR, Vector3( 1.0f, 1.0f, 1.0f ) );
257     controlsLayer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
258     controlsLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
259     stage.Add( controlsLayer );
260
261     // Back and next image buttons in corners of stage:
262     unsigned int playWidth = std::min( stage.GetSize().x * (1 / 5.0f), 58.0f );
263     Toolkit::ImageView imagePrevious = Toolkit::ImageView::New( DALI_ICON_PLAY, ImageDimensions( playWidth, playWidth ) );
264
265     // Last image button:
266     imagePrevious.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
267     imagePrevious.RotateBy( Radian(3.14159265358979323846f), Vector3( 0, 1.0f, 0 ) );
268     imagePrevious.SetProperty( Actor::Property::POSITION_Y,  playWidth * 0.5f );
269     imagePrevious.SetProperty( Actor::Property::POSITION_X,  playWidth + playWidth * 0.5f );
270     imagePrevious.SetProperty( Actor::Property::OPACITY, 0.6f );
271     controlsLayer.Add( imagePrevious );
272     imagePrevious.SetProperty( Dali::Actor::Property::NAME, PREVIOUS_BUTTON_ID );
273     imagePrevious.TouchSignal().Connect( this, &ImageScalingAndFilteringController::OnControlTouched );
274
275     // Next image button:
276     Toolkit::ImageView imageNext = Toolkit::ImageView::New( DALI_ICON_PLAY, ImageDimensions( playWidth, playWidth ) );
277     imageNext.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT );
278     imageNext.SetProperty( Actor::Property::POSITION_Y,  playWidth * 0.5f );
279     imageNext.SetProperty( Actor::Property::POSITION_X,  stage.GetSize().x - playWidth * 0.5f );
280     imageNext.SetProperty( Actor::Property::OPACITY, 0.6f );
281     controlsLayer.Add( imageNext );
282     imageNext.SetProperty( Dali::Actor::Property::NAME, NEXT_BUTTON_ID );
283     imageNext.TouchSignal().Connect( this, &ImageScalingAndFilteringController::OnControlTouched );
284
285     // Buttons to popup selectors for fitting and sampling modes:
286
287     // Wrapper table to hold two buttons side by side:
288     Toolkit::TableView modesGroupBackground = Toolkit::TableView::New( 1, 2 );
289     modesGroupBackground.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
290     modesGroupBackground.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
291     modesGroupBackground.SetBackgroundColor( BACKGROUND_COLOUR );
292     modesGroupBackground.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE ) );
293     modesGroupBackground.SetFitHeight( 0 );
294
295     modesGroupBackground.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
296     modesGroupBackground.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT );
297     modesGroupBackground.SetProperty( Actor::Property::POSITION, Vector2( 0.0f, 0.0f ));
298
299     controlsLayer.Add( modesGroupBackground );
300
301     {
302       // Vertical table to hold label and button:
303       Toolkit::TableView fittingModeGroup = Toolkit::TableView::New( 2, 1 );
304       fittingModeGroup.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
305       fittingModeGroup.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
306       fittingModeGroup.SetBackgroundColor( BACKGROUND_COLOUR );
307       fittingModeGroup.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f ) );
308       fittingModeGroup.SetFitHeight( 0 );
309       fittingModeGroup.SetFitHeight( 1 );
310
311       TextLabel label = TextLabel::New( "Image fitting mode:" );
312       label.SetStyleName( STYLE_LABEL_TEXT );
313       fittingModeGroup.Add( label );
314
315       Toolkit::PushButton button = CreateButton( FITTING_BUTTON_ID, StringFromScalingMode( mFittingMode ) );
316       fittingModeGroup.Add( button );
317       mFittingModeButton = button;
318
319       modesGroupBackground.Add( fittingModeGroup );
320     }
321
322     {
323       // Vertical table to hold label and button:
324       Toolkit::TableView samplingModeGroup = Toolkit::TableView::New( 2, 1 );
325       samplingModeGroup.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
326       samplingModeGroup.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
327       samplingModeGroup.SetBackgroundColor( BACKGROUND_COLOUR );
328       samplingModeGroup.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f ) );
329       samplingModeGroup.SetFitHeight( 0 );
330       samplingModeGroup.SetFitHeight( 1 );
331
332       TextLabel label = TextLabel::New( "Image sampling mode:" );
333       label.SetStyleName( STYLE_LABEL_TEXT );
334       samplingModeGroup.Add( label );
335
336       Toolkit::PushButton button = CreateButton( SAMPLING_BUTTON_ID, StringFromFilterMode( mSamplingMode ) );
337       samplingModeGroup.Add( button );
338       mSamplingModeButton = button;
339
340       modesGroupBackground.Add( samplingModeGroup );
341     }
342   }
343
344   Toolkit::PushButton CreateButton( const char * id, const char * label )
345   {
346     Toolkit::PushButton button = Toolkit::PushButton::New();
347     button.SetStyleName( STYLE_BUTTON_TEXT );
348     button.SetProperty( Dali::Actor::Property::NAME, id );
349     button.SetProperty( Toolkit::Button::Property::LABEL, label );
350     button.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
351     button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
352     button.ClickedSignal().Connect( this, &ImageScalingAndFilteringController::OnButtonClicked );
353     return button;
354   }
355
356   Toolkit::Popup CreatePopup()
357   {
358     Stage stage = Stage::GetCurrent();
359     const float POPUP_WIDTH_DP = stage.GetSize().width * 0.75f;
360
361     Toolkit::Popup popup = Toolkit::Popup::New();
362     popup.SetProperty( Dali::Actor::Property::NAME, "POPUP" );
363     popup.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
364     popup.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
365     popup.SetProperty( Actor::Property::SIZE, Vector2( POPUP_WIDTH_DP, 0.0f ) );
366
367     popup.OutsideTouchedSignal().Connect( this, &ImageScalingAndFilteringController::OnPopupOutsideTouched );
368
369     return popup;
370   }
371
372   Toolkit::PushButton CreatePopupButton( Actor parent, const char* id )
373   {
374     Toolkit::PushButton button = Toolkit::PushButton::New();
375     button.SetProperty( Dali::Actor::Property::NAME, id );
376     button.SetProperty( Toolkit::Button::Property::LABEL, id );
377
378     button.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
379     button.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT );
380     button.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
381     button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
382
383     button.ClickedSignal().Connect( this, &ImageScalingAndFilteringController::OnButtonClicked );
384
385     parent.Add( button );
386     return button;
387   }
388
389   bool OnButtonClicked( Toolkit::Button button )
390   {
391     if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == FITTING_BUTTON_ID )
392     {
393       mPopup = CreatePopup();
394
395       // Four-row table to hold buttons:
396       Toolkit::TableView fittingModes = Toolkit::TableView::New( 4, 1 );
397       fittingModes.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
398       fittingModes.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
399       fittingModes.SetCellPadding( Size( MARGIN_SIZE, MARGIN_SIZE * 0.5 ) );
400       fittingModes.SetFitHeight( 0 );
401       fittingModes.SetFitHeight( 1 );
402       fittingModes.SetFitHeight( 2 );
403       fittingModes.SetFitHeight( 3 );
404
405       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::SCALE_TO_FILL ) );
406       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::SHRINK_TO_FIT ) );
407       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::FIT_WIDTH ) );
408       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::FIT_HEIGHT ) );
409
410       mPopup.SetContent( fittingModes );
411       Stage::GetCurrent().Add( mPopup );
412       mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
413     }
414     else if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == SAMPLING_BUTTON_ID )
415     {
416       mPopup = CreatePopup();
417
418       // Table to hold buttons for each sampling mode:
419       Toolkit::TableView samplingModes = Toolkit::TableView::New( 6, 1 );
420       samplingModes.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
421       samplingModes.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
422       samplingModes.SetCellPadding( Size( MARGIN_SIZE, MARGIN_SIZE * 0.5 ) );
423       samplingModes.SetFitHeight( 0 );
424       samplingModes.SetFitHeight( 1 );
425       samplingModes.SetFitHeight( 2 );
426       samplingModes.SetFitHeight( 3 );
427       samplingModes.SetFitHeight( 4 );
428       samplingModes.SetFitHeight( 5 );
429
430       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::NEAREST ) );
431       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::LINEAR ) );
432       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX ) );
433       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX_THEN_NEAREST ) );
434       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX_THEN_LINEAR ) );
435       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::NO_FILTER ) );
436
437       mPopup.SetContent( samplingModes );
438       Stage::GetCurrent().Add( mPopup );
439       mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
440     }
441     else if( CheckFittingModeButton( button, FittingMode::SCALE_TO_FILL) ||
442              CheckFittingModeButton( button, FittingMode::SHRINK_TO_FIT) ||
443              CheckFittingModeButton( button, FittingMode::FIT_WIDTH) ||
444              CheckFittingModeButton( button, FittingMode::FIT_HEIGHT) )
445     {
446     }
447     else if( CheckSamplingModeButton( button, SamplingMode::NEAREST ) ||
448              CheckSamplingModeButton( button, SamplingMode::LINEAR ) ||
449              CheckSamplingModeButton( button, SamplingMode::BOX ) ||
450              CheckSamplingModeButton( button, SamplingMode::LINEAR ) ||
451              CheckSamplingModeButton( button, SamplingMode::BOX_THEN_NEAREST ) ||
452              CheckSamplingModeButton( button, SamplingMode::BOX_THEN_LINEAR ) ||
453              CheckSamplingModeButton( button, SamplingMode::NO_FILTER ) )
454     {
455     }
456     return true;
457   }
458
459   bool CheckFittingModeButton( Actor &button, FittingMode::Type mode )
460   {
461     const char * const modeName = StringFromScalingMode( mode );
462     if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == modeName )
463     {
464       mFittingMode = mode;
465       mFittingModeButton.SetProperty( Toolkit::Button::Property::LABEL, modeName );
466       ResizeImage();
467       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
468       mPopup.Reset();
469       return true;
470     }
471     return false;
472   }
473
474   bool CheckSamplingModeButton( Actor &button, SamplingMode::Type mode )
475   {
476     const char * const modeName = StringFromFilterMode( mode );
477     if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == modeName )
478     {
479       mSamplingMode = mode;
480       mSamplingModeButton.SetProperty( Toolkit::Button::Property::LABEL, modeName );
481       ResizeImage();
482       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
483       mPopup.Reset();
484       return true;
485     }
486     return false;
487   }
488
489   void OnPopupOutsideTouched()
490   {
491     if( mPopup )
492     {
493       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
494       mPopup.Reset();
495     }
496   }
497
498   bool OnControlTouched( Actor actor, const TouchData& event )
499   {
500     if(event.GetPointCount() > 0)
501     {
502       switch( event.GetState( 0 ) )
503       {
504         case PointState::UP:
505         {
506           const std::string & name = actor.GetProperty< std::string >( Dali::Actor::Property::NAME );
507           if( name == NEXT_BUTTON_ID )
508           {
509             mCurrentPath = mCurrentPath + 1;
510             mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
511             ResizeImage();
512           }
513           else if( name == PREVIOUS_BUTTON_ID )
514           {
515             mCurrentPath = mCurrentPath - 1;
516             mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
517             ResizeImage();
518           }
519           break;
520         }
521         default:
522         {
523           break;
524         }
525       } // end switch
526     }
527
528     return false;
529   }
530
531   void OnPinch( Actor actor, const PinchGesture& pinch )
532   {
533     if( pinch.state == Gesture::Started )
534     {
535       mLastPinchScale = pinch.scale;
536     }
537     const float scale = pinch.scale;
538
539     if( ! Equals( scale, mLastPinchScale ) )
540     {
541       if ( scale < mLastPinchScale )
542       {
543         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
544         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
545       }
546       else
547       {
548         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
549         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
550       }
551       ResizeImage();
552     }
553     mLastPinchScale = scale;
554   }
555
556   void OnPan( Actor actor, const PanGesture& gesture )
557   {
558     Stage stage = Stage::GetCurrent();
559     // 1.0f and 0.75f are the maximum size caps of the resized image, as a factor of stage-size.
560     mImageStageScale.x = std::max( 0.05f, std::min( 0.95f,  mImageStageScale.x + ( gesture.displacement.x * 2.0f / stage.GetSize().width ) ) );
561     mImageStageScale.y = std::max( 0.05f, std::min( 0.70f, mImageStageScale.y + ( gesture.displacement.y * 2.0f / stage.GetSize().height ) ) );
562
563     ResizeImage();
564   }
565
566   void OnKeyEvent(const KeyEvent& event)
567   {
568     if( event.state == KeyEvent::Down )
569     {
570       if( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
571       {
572         if( mPopup && mPopup.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) )
573         {
574           mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
575           mPopup.Reset();
576         }
577         else
578         {
579           mApplication.Quit();
580         }
581       }
582       else if ( event.keyPressedName == "Right" )
583       {
584         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
585       }
586       else if ( event.keyPressedName == "Left" )
587       {
588         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
589       }
590       else if ( event.keyPressedName == "Up" )
591       {
592         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
593       }
594       else if ( event.keyPressedName == "Down" )
595       {
596         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
597       }
598       else if ( event.keyPressedName == "o" )
599       {
600         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
601         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
602       }
603       else if ( event.keyPressedName == "p" )
604       {
605         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
606         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
607       }
608       else if ( event.keyPressedName == "n" )
609       {
610         mCurrentPath = mCurrentPath + 1;
611         mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
612       }
613       else if ( event.keyPressedName == "b" )
614       {
615         mCurrentPath = mCurrentPath - 1;
616         mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
617       }
618       // Cycle filter and scaling modes:
619       else if ( event.keyPressedName == "f" )
620       {
621         mSamplingMode = NextFilterMode( mSamplingMode );
622         mSamplingModeButton.SetProperty( Toolkit::Button::Property::LABEL, StringFromFilterMode( mSamplingMode ) );
623       }
624       // Cycle filter and scaling modes:
625       else if ( event.keyPressedName == "s" )
626       {
627         mFittingMode = NextScalingMode( mFittingMode );
628         mFittingModeButton.SetProperty( Toolkit::Button::Property::LABEL, StringFromScalingMode( mFittingMode ) );
629       }
630       else
631       {
632         return;
633       }
634
635       ResizeImage();
636     }
637   }
638
639 private:
640
641   void LoadImage()
642   {
643     mImageLoading = true;
644
645     const char * const path = IMAGE_PATHS[ mCurrentPath ];
646     Stage stage = Stage::GetCurrent();
647     Size imageSize = stage.GetSize() * mImageStageScale;
648     mImageView.SetProperty( Actor::Property::SIZE, imageSize );
649
650     Property::Map map;
651     map[Toolkit::ImageVisual::Property::URL] = path;
652     map[Toolkit::ImageVisual::Property::DESIRED_WIDTH] = imageSize.x;
653     map[Toolkit::ImageVisual::Property::DESIRED_HEIGHT] = imageSize.y;
654     map[Toolkit::ImageVisual::Property::FITTING_MODE] = mFittingMode;
655     map[Toolkit::ImageVisual::Property::SAMPLING_MODE] = mSamplingMode;
656
657     mImageView.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
658
659   }
660
661   void ResizeImage()
662   {
663
664     Stage stage = Stage::GetCurrent();
665     Size imageSize = stage.GetSize() * mImageStageScale;
666
667     LoadImage();
668
669     // Border size needs to be modified to take into account the width of the frame.
670     Vector2 borderScale( ( imageSize + Vector2( BORDER_WIDTH * 2.0f, BORDER_WIDTH * 2.0f ) ) / stage.GetSize() );
671     mDesiredBox.SetProperty( Actor::Property::SIZE, stage.GetSize() * borderScale );
672   }
673
674 private:
675   Application&  mApplication;
676   Toolkit::ImageView mDesiredBox; //< Background rectangle to show requested image size.
677   Toolkit::PushButton mFittingModeButton;
678   Toolkit::PushButton mSamplingModeButton;
679   Toolkit::Popup mPopup;
680   PinchGestureDetector mPinchDetector;
681   float mLastPinchScale;
682   Toolkit::ImageView mGrabCorner;
683   PanGestureDetector mPanGestureDetector;
684   Toolkit::ImageView mImageView;
685   Vector2 mImageStageScale;
686   int mCurrentPath;
687   FittingMode::Type mFittingMode;
688   SamplingMode::Type mSamplingMode;
689   bool mImageLoading;
690   bool mQueuedImageLoad;
691
692 };
693
694 int DALI_EXPORT_API main( int argc, char **argv )
695 {
696   Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
697   ImageScalingAndFilteringController test( application );
698   application.MainLoop();
699   return 0;
700 }