Changes after TouchedSignal changes
[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 <dali-toolkit/devel-api/controls/table-view/table-view.h>
23 #include "shared/view.h"
24 #include <iostream>
25
26 using namespace Dali;
27 using Toolkit::TextLabel;
28
29 namespace
30 {
31
32 const char* BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-gradient.jpg" );
33 const Vector4 BACKGROUND_COLOUR( 1.0f, 1.0f, 1.0f, 0.15f );
34
35 const char* BORDER_IMAGE( DEMO_IMAGE_DIR "border-4px.9.png" );
36 const int BORDER_WIDTH = ( 11.0f + 4.0f ); // Shadow size = 11, border size = 4.
37 const char* RESIZE_HANDLE_IMAGE( DEMO_IMAGE_DIR "resize-handle.png" );
38
39 const int MARGIN_SIZE = 10;
40
41 const char* const NEXT_BUTTON_ID = "NEXT_BUTTON";
42 const char* const PREVIOUS_BUTTON_ID = "PREVIOUS_BUTTON";
43 const char * const DALI_ICON_PLAY = DEMO_IMAGE_DIR "icon-play.png";
44
45 const char* const FITTING_BUTTON_ID = "FITTING_BUTTON";
46 const char* const SAMPLING_BUTTON_ID = "SAMPLING_BUTTON";
47 const char* const FITTING_BUTTON_TEXT = "Fitting";
48 const char* const SAMPLING_BUTTON_TEXT = "Sampling";
49
50 const char* const STYLE_LABEL_TEXT  = "ImageScalingGroupLabel";
51 const char* const STYLE_BUTTON_TEXT = "ImageScalingButton";
52
53 const char* IMAGE_PATHS[] =
54 {
55   // Variety of sizes, shapes and formats:
56   DEMO_IMAGE_DIR "dali-logo.png",
57   DEMO_IMAGE_DIR "layer1.png",
58   DEMO_IMAGE_DIR "layer2.png",
59   DEMO_IMAGE_DIR "animation-list.png",
60   DEMO_IMAGE_DIR "music-libray-main-screen.png",
61   DEMO_IMAGE_DIR "music-libray-record-cover.png",
62   DEMO_IMAGE_DIR "contacts-background.png",
63   DEMO_IMAGE_DIR "portrait_screen_primitive_shapes.gif",
64   DEMO_IMAGE_DIR "landscape_screen_primitive_shapes.gif",
65   DEMO_IMAGE_DIR "square_primitive_shapes.bmp",
66   DEMO_IMAGE_DIR "gallery-large-14.jpg",
67   DEMO_IMAGE_DIR "book-landscape-cover.jpg",
68   DEMO_IMAGE_DIR "book-portrait-p1.jpg",
69   DEMO_IMAGE_DIR "book-landscape-cover-back.jpg",
70
71   // Worst case for aliasing in downscaling, 2k x 2k 1 bit per pixel dithered
72   // black and white image:
73   DEMO_IMAGE_DIR "gallery-large-14.wbmp",
74
75   DEMO_IMAGE_DIR "background-1.jpg",
76   DEMO_IMAGE_DIR "background-blocks.jpg",
77   DEMO_IMAGE_DIR "background-magnifier.jpg",
78   DEMO_IMAGE_DIR "gallery-large-14.jpg",
79   NULL
80 };
81 const int NUM_IMAGE_PATHS = sizeof(IMAGE_PATHS) / sizeof(IMAGE_PATHS[0]) - 1u;
82
83 /** Cycle the scaling mode options. */
84 FittingMode::Type NextScalingMode( FittingMode::Type oldMode )
85 {
86   FittingMode::Type newMode = FittingMode::SHRINK_TO_FIT;
87   switch ( oldMode )
88   {
89     case FittingMode::SHRINK_TO_FIT:
90       newMode = FittingMode::SCALE_TO_FILL;
91       break;
92     case FittingMode::SCALE_TO_FILL:
93       newMode = FittingMode::FIT_WIDTH;
94       break;
95     case FittingMode::FIT_WIDTH:
96       newMode = FittingMode::FIT_HEIGHT;
97       break;
98     case FittingMode::FIT_HEIGHT:
99       newMode = FittingMode::SHRINK_TO_FIT;
100       break;
101   }
102   return newMode;
103 }
104
105 /** Cycle through filter mode options. */
106 SamplingMode::Type NextFilterMode( SamplingMode::Type oldMode )
107 {
108   SamplingMode::Type newMode = SamplingMode::BOX;
109
110   switch ( oldMode )
111   {
112     case SamplingMode::BOX:
113       newMode = SamplingMode::NEAREST;
114       break;
115     case SamplingMode::NEAREST:
116       newMode = SamplingMode::LINEAR;
117       break;
118     case SamplingMode::LINEAR:
119       newMode = SamplingMode::BOX_THEN_NEAREST;
120       break;
121     case SamplingMode::BOX_THEN_NEAREST:
122       newMode = SamplingMode::BOX_THEN_LINEAR;
123       break;
124     case SamplingMode::BOX_THEN_LINEAR:
125       newMode = SamplingMode::NO_FILTER;
126       break;
127     case SamplingMode::NO_FILTER:
128       newMode = SamplingMode::BOX;
129       break;
130     case SamplingMode::DONT_CARE:
131       newMode = SamplingMode::BOX;
132       break;
133   }
134   return newMode;
135 }
136
137 const char* StringFromScalingMode( FittingMode::Type scalingMode )
138 {
139   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";
140 }
141
142 const char* StringFromFilterMode( SamplingMode::Type filterMode )
143 {
144   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";
145 }
146
147 }
148
149 // This example shows the load-time image scaling and filtering features.
150 //
151 class ImageScalingAndFilteringController : public ConnectionTracker
152 {
153 public:
154
155   ImageScalingAndFilteringController( Application& application )
156   : mApplication( application ),
157     mLastPinchScale( 1.0f ),
158     mImageWindowScale( 0.5f, 0.5f ),
159     mCurrentPath( 0 ),
160     mFittingMode( FittingMode::FIT_WIDTH ),
161     mSamplingMode( SamplingMode::BOX_THEN_LINEAR),
162     mImageLoading( false ),
163     mQueuedImageLoad( false )
164   {
165     // Connect to the Application's Init signal
166     mApplication.InitSignal().Connect( this, &ImageScalingAndFilteringController::Create );
167   }
168
169   ~ImageScalingAndFilteringController()
170   {
171     // Nothing to do here;
172   }
173
174   // The Init signal is received once (only) during the Application lifetime
175   void Create( Application& application )
176   {
177     // Get a handle to the window
178     Window window = application.GetWindow();
179     Vector2 windowSize = window.GetSize();
180
181     // Background image:
182     Dali::Property::Map backgroundImage;
183     backgroundImage.Insert( Toolkit::Visual::Property::TYPE,  Toolkit::Visual::IMAGE );
184     backgroundImage.Insert( Toolkit::ImageVisual::Property::URL,  BACKGROUND_IMAGE );
185     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, windowSize.width );
186     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, windowSize.height );
187     backgroundImage.Insert( Toolkit::ImageVisual::Property::FITTING_MODE,   FittingMode::SCALE_TO_FILL );
188     backgroundImage.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE,   SamplingMode::BOX_THEN_NEAREST );
189
190     Toolkit::ImageView background = Toolkit::ImageView::New();
191     background.SetProperty( Toolkit::ImageView::Property::IMAGE, backgroundImage );
192     background.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
193     background.SetProperty( Actor::Property::SIZE, windowSize );
194     window.Add( background );
195
196     mDesiredBox = Toolkit::ImageView::New( BORDER_IMAGE );
197     background.Add( mDesiredBox );
198
199     mDesiredBox.SetProperty( Actor::Property::SIZE, windowSize * mImageWindowScale );
200     mDesiredBox.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
201     mDesiredBox.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
202
203     // Initialize the actor
204     mImageView = Toolkit::ImageView::New( IMAGE_PATHS[ 0 ] );
205
206     // Reposition the actor
207     mImageView.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
208     mImageView.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
209
210     // Display the actor on the window
211     mDesiredBox.Add( mImageView );
212
213     mImageView.SetProperty( Actor::Property::SIZE, windowSize * mImageWindowScale );
214
215     // Setup the pinch detector for scaling the desired image load dimensions:
216     mPinchDetector = PinchGestureDetector::New();
217     mPinchDetector.Attach( mImageView );
218     mPinchDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPinch );
219
220     mGrabCorner = Toolkit::ImageView::New( RESIZE_HANDLE_IMAGE );
221     mGrabCorner.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
222     mGrabCorner.SetProperty( Dali::Actor::Property::NAME, "GrabCorner" );
223     mGrabCorner.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
224     mGrabCorner.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT );
225     mGrabCorner.SetProperty( Actor::Property::POSITION, Vector2( -BORDER_WIDTH, -BORDER_WIDTH ));
226     mGrabCorner.SetProperty( Actor::Property::OPACITY, 0.6f );
227
228     Layer grabCornerLayer = Layer::New();
229     grabCornerLayer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
230     grabCornerLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT );
231     grabCornerLayer.Add( mGrabCorner );
232     mDesiredBox.Add( grabCornerLayer );
233
234     mPanGestureDetector = PanGestureDetector::New();
235     mPanGestureDetector.Attach( mGrabCorner );
236     mPanGestureDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPan );
237
238     // Tie-in input event handlers:
239     window.KeyEventSignal().Connect( this, &ImageScalingAndFilteringController::OnKeyEvent );
240
241     CreateControls();
242
243     ResizeImage();
244   }
245
246   /**
247    * Create the GUI controls which float above the scene
248    */
249   void CreateControls()
250   {
251     Window window = mApplication.GetWindow();
252     Vector2 windowSize = window.GetSize();
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     window.Add( controlsLayer );
260
261     // Back and next image buttons in corners of window:
262     unsigned int playWidth = std::min( windowSize.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.TouchedSignal().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,  windowSize.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.TouchedSignal().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     Window window = mApplication.GetWindow();
359     const float POPUP_WIDTH_DP = window.GetSize().GetWidth() * 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       mApplication.GetWindow().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       mApplication.GetWindow().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 TouchEvent& 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.GetState() == Gesture::Started )
534     {
535       mLastPinchScale = pinch.GetScale();
536     }
537     const float scale = pinch.GetScale();
538
539     if( ! Equals( scale, mLastPinchScale ) )
540     {
541       if ( scale < mLastPinchScale )
542       {
543         mImageWindowScale.x = std::max( 0.05f, mImageWindowScale.x * 0.9f );
544         mImageWindowScale.y = std::max( 0.05f, mImageWindowScale.y * 0.9f );
545       }
546       else
547       {
548         mImageWindowScale.x = std::max( 0.05f, std::min( 1.0f, mImageWindowScale.x * 1.1f ) );
549         mImageWindowScale.y = std::max( 0.05f, std::min( 1.0f, mImageWindowScale.y * 1.1f ) );
550       }
551       ResizeImage();
552     }
553     mLastPinchScale = scale;
554   }
555
556   void OnPan( Actor actor, const PanGesture& gesture )
557   {
558     Window window = mApplication.GetWindow();
559     Vector2 windowSize = window.GetSize();
560     const Vector2& displacement = gesture.GetDisplacement();
561
562     // 1.0f and 0.75f are the maximum size caps of the resized image, as a factor of window-size.
563     mImageWindowScale.x = std::max( 0.05f, std::min( 0.95f,  mImageWindowScale.x + ( displacement.x * 2.0f / windowSize.width ) ) );
564     mImageWindowScale.y = std::max( 0.05f, std::min( 0.70f, mImageWindowScale.y + ( displacement.y * 2.0f / windowSize.height ) ) );
565
566     ResizeImage();
567   }
568
569   void OnKeyEvent(const KeyEvent& event)
570   {
571     if( event.GetState() == KeyEvent::DOWN )
572     {
573       if( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
574       {
575         if( mPopup && mPopup.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) )
576         {
577           mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
578           mPopup.Reset();
579         }
580         else
581         {
582           mApplication.Quit();
583         }
584       }
585       else if ( event.GetKeyName() == "Right" )
586       {
587         mImageWindowScale.x = std::max( 0.05f, std::min( 1.0f, mImageWindowScale.x * 1.1f ) );
588       }
589       else if ( event.GetKeyName() == "Left" )
590       {
591         mImageWindowScale.x = std::max( 0.05f, mImageWindowScale.x * 0.9f );
592       }
593       else if ( event.GetKeyName() == "Up" )
594       {
595         mImageWindowScale.y = std::max( 0.05f, std::min( 1.0f, mImageWindowScale.y * 1.1f ) );
596       }
597       else if ( event.GetKeyName() == "Down" )
598       {
599         mImageWindowScale.y = std::max( 0.05f, mImageWindowScale.y * 0.9f );
600       }
601       else if ( event.GetKeyName() == "o" )
602       {
603         mImageWindowScale.x = std::max( 0.05f, mImageWindowScale.x * 0.9f );
604         mImageWindowScale.y = std::max( 0.05f, mImageWindowScale.y * 0.9f );
605       }
606       else if ( event.GetKeyName() == "p" )
607       {
608         mImageWindowScale.x = std::max( 0.05f, std::min( 1.0f, mImageWindowScale.x * 1.1f ) );
609         mImageWindowScale.y = std::max( 0.05f, std::min( 1.0f, mImageWindowScale.y * 1.1f ) );
610       }
611       else if ( event.GetKeyName() == "n" )
612       {
613         mCurrentPath = mCurrentPath + 1;
614         mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
615       }
616       else if ( event.GetKeyName() == "b" )
617       {
618         mCurrentPath = mCurrentPath - 1;
619         mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
620       }
621       // Cycle filter and scaling modes:
622       else if ( event.GetKeyName() == "f" )
623       {
624         mSamplingMode = NextFilterMode( mSamplingMode );
625         mSamplingModeButton.SetProperty( Toolkit::Button::Property::LABEL, StringFromFilterMode( mSamplingMode ) );
626       }
627       // Cycle filter and scaling modes:
628       else if ( event.GetKeyName() == "s" )
629       {
630         mFittingMode = NextScalingMode( mFittingMode );
631         mFittingModeButton.SetProperty( Toolkit::Button::Property::LABEL, StringFromScalingMode( mFittingMode ) );
632       }
633       else
634       {
635         return;
636       }
637
638       ResizeImage();
639     }
640   }
641
642 private:
643
644   void LoadImage()
645   {
646     mImageLoading = true;
647
648     const char * const path = IMAGE_PATHS[ mCurrentPath ];
649     Window window = mApplication.GetWindow();
650     Size imageSize = Vector2(window.GetSize()) * mImageWindowScale;
651     mImageView.SetProperty( Actor::Property::SIZE, imageSize );
652
653     Property::Map map;
654     map[Toolkit::ImageVisual::Property::URL] = path;
655     map[Toolkit::ImageVisual::Property::DESIRED_WIDTH] = imageSize.x;
656     map[Toolkit::ImageVisual::Property::DESIRED_HEIGHT] = imageSize.y;
657     map[Toolkit::ImageVisual::Property::FITTING_MODE] = mFittingMode;
658     map[Toolkit::ImageVisual::Property::SAMPLING_MODE] = mSamplingMode;
659
660     mImageView.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
661
662   }
663
664   void ResizeImage()
665   {
666
667     Window window = mApplication.GetWindow();
668     Vector2 windowSize = window.GetSize();
669     Size imageSize = windowSize * mImageWindowScale;
670
671     LoadImage();
672
673     // Border size needs to be modified to take into account the width of the frame.
674     Vector2 borderScale( ( imageSize + Vector2( BORDER_WIDTH * 2.0f, BORDER_WIDTH * 2.0f ) ) / windowSize );
675     mDesiredBox.SetProperty( Actor::Property::SIZE, windowSize * borderScale );
676   }
677
678 private:
679   Application&  mApplication;
680   Toolkit::ImageView mDesiredBox; //< Background rectangle to show requested image size.
681   Toolkit::PushButton mFittingModeButton;
682   Toolkit::PushButton mSamplingModeButton;
683   Toolkit::Popup mPopup;
684   PinchGestureDetector mPinchDetector;
685   float mLastPinchScale;
686   Toolkit::ImageView mGrabCorner;
687   PanGestureDetector mPanGestureDetector;
688   Toolkit::ImageView mImageView;
689   Vector2 mImageWindowScale;
690   int mCurrentPath;
691   FittingMode::Type mFittingMode;
692   SamplingMode::Type mSamplingMode;
693   bool mImageLoading;
694   bool mQueuedImageLoad;
695
696 };
697
698 int DALI_EXPORT_API main( int argc, char **argv )
699 {
700   Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
701   ImageScalingAndFilteringController test( application );
702   application.MainLoop();
703   return 0;
704 }