Merge "Remove non-touch related deprecated APIs" into devel/master
[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     mImageStageScale( 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 stage
178     Stage stage = Stage::GetCurrent();
179
180     // Background image:
181     Dali::Property::Map backgroundImage;
182     backgroundImage.Insert( Toolkit::Visual::Property::TYPE,  Toolkit::Visual::IMAGE );
183     backgroundImage.Insert( Toolkit::ImageVisual::Property::URL,  BACKGROUND_IMAGE );
184     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, stage.GetSize().width );
185     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, stage.GetSize().height );
186     backgroundImage.Insert( Toolkit::ImageVisual::Property::FITTING_MODE,   FittingMode::SCALE_TO_FILL );
187     backgroundImage.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE,   SamplingMode::BOX_THEN_NEAREST );
188
189     Toolkit::ImageView background = Toolkit::ImageView::New();
190     background.SetProperty( Toolkit::ImageView::Property::IMAGE, backgroundImage );
191     background.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
192     background.SetProperty( Actor::Property::SIZE, stage.GetSize() );
193     stage.Add( background );
194
195     mDesiredBox = Toolkit::ImageView::New( BORDER_IMAGE );
196     background.Add( mDesiredBox );
197
198     mDesiredBox.SetProperty( Actor::Property::SIZE, stage.GetSize() * mImageStageScale );
199     mDesiredBox.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
200     mDesiredBox.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
201
202     // Initialize the actor
203     mImageView = Toolkit::ImageView::New( IMAGE_PATHS[ 0 ] );
204
205     // Reposition the actor
206     mImageView.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
207     mImageView.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
208
209     // Display the actor on the stage
210     mDesiredBox.Add( mImageView );
211
212     mImageView.SetProperty( Actor::Property::SIZE, stage.GetSize() * mImageStageScale );
213
214     // Setup the pinch detector for scaling the desired image load dimensions:
215     mPinchDetector = PinchGestureDetector::New();
216     mPinchDetector.Attach( mImageView );
217     mPinchDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPinch );
218
219     mGrabCorner = Toolkit::ImageView::New( RESIZE_HANDLE_IMAGE );
220     mGrabCorner.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
221     mGrabCorner.SetProperty( Dali::Actor::Property::NAME, "GrabCorner" );
222     mGrabCorner.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
223     mGrabCorner.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT );
224     mGrabCorner.SetProperty( Actor::Property::POSITION, Vector2( -BORDER_WIDTH, -BORDER_WIDTH ));
225     mGrabCorner.SetProperty( Actor::Property::OPACITY, 0.6f );
226
227     Layer grabCornerLayer = Layer::New();
228     grabCornerLayer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
229     grabCornerLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT );
230     grabCornerLayer.Add( mGrabCorner );
231     mDesiredBox.Add( grabCornerLayer );
232
233     mPanGestureDetector = PanGestureDetector::New();
234     mPanGestureDetector.Attach( mGrabCorner );
235     mPanGestureDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPan );
236
237     // Tie-in input event handlers:
238     stage.KeyEventSignal().Connect( this, &ImageScalingAndFilteringController::OnKeyEvent );
239
240     CreateControls();
241
242     ResizeImage();
243   }
244
245   /**
246    * Create the GUI controls which float above the scene
247    */
248   void CreateControls()
249   {
250     Stage stage = Stage::GetCurrent();
251
252     Dali::Layer controlsLayer = Dali::Layer::New();
253     controlsLayer.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
254     controlsLayer.SetProperty( Actor::Property::SIZE_MODE_FACTOR, Vector3( 1.0f, 1.0f, 1.0f ) );
255     controlsLayer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
256     controlsLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
257     stage.Add( controlsLayer );
258
259     // Back and next image buttons in corners of stage:
260     unsigned int playWidth = std::min( stage.GetSize().x * (1 / 5.0f), 58.0f );
261     Toolkit::ImageView imagePrevious = Toolkit::ImageView::New( DALI_ICON_PLAY, ImageDimensions( playWidth, playWidth ) );
262
263     // Last image button:
264     imagePrevious.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
265     imagePrevious.RotateBy( Radian(3.14159265358979323846f), Vector3( 0, 1.0f, 0 ) );
266     imagePrevious.SetProperty( Actor::Property::POSITION_Y,  playWidth * 0.5f );
267     imagePrevious.SetProperty( Actor::Property::POSITION_X,  playWidth + playWidth * 0.5f );
268     imagePrevious.SetProperty( Actor::Property::OPACITY, 0.6f );
269     controlsLayer.Add( imagePrevious );
270     imagePrevious.SetProperty( Dali::Actor::Property::NAME, PREVIOUS_BUTTON_ID );
271     imagePrevious.TouchSignal().Connect( this, &ImageScalingAndFilteringController::OnControlTouched );
272
273     // Next image button:
274     Toolkit::ImageView imageNext = Toolkit::ImageView::New( DALI_ICON_PLAY, ImageDimensions( playWidth, playWidth ) );
275     imageNext.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT );
276     imageNext.SetProperty( Actor::Property::POSITION_Y,  playWidth * 0.5f );
277     imageNext.SetProperty( Actor::Property::POSITION_X,  stage.GetSize().x - playWidth * 0.5f );
278     imageNext.SetProperty( Actor::Property::OPACITY, 0.6f );
279     controlsLayer.Add( imageNext );
280     imageNext.SetProperty( Dali::Actor::Property::NAME, NEXT_BUTTON_ID );
281     imageNext.TouchSignal().Connect( this, &ImageScalingAndFilteringController::OnControlTouched );
282
283     // Buttons to popup selectors for fitting and sampling modes:
284
285     // Wrapper table to hold two buttons side by side:
286     Toolkit::TableView modesGroupBackground = Toolkit::TableView::New( 1, 2 );
287     modesGroupBackground.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
288     modesGroupBackground.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
289     modesGroupBackground.SetBackgroundColor( BACKGROUND_COLOUR );
290     modesGroupBackground.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE ) );
291     modesGroupBackground.SetFitHeight( 0 );
292
293     modesGroupBackground.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
294     modesGroupBackground.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT );
295     modesGroupBackground.SetProperty( Actor::Property::POSITION, Vector2( 0.0f, 0.0f ));
296
297     controlsLayer.Add( modesGroupBackground );
298
299     {
300       // Vertical table to hold label and button:
301       Toolkit::TableView fittingModeGroup = Toolkit::TableView::New( 2, 1 );
302       fittingModeGroup.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
303       fittingModeGroup.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
304       fittingModeGroup.SetBackgroundColor( BACKGROUND_COLOUR );
305       fittingModeGroup.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f ) );
306       fittingModeGroup.SetFitHeight( 0 );
307       fittingModeGroup.SetFitHeight( 1 );
308
309       TextLabel label = TextLabel::New( "Image fitting mode:" );
310       label.SetStyleName( STYLE_LABEL_TEXT );
311       fittingModeGroup.Add( label );
312
313       Toolkit::PushButton button = CreateButton( FITTING_BUTTON_ID, StringFromScalingMode( mFittingMode ) );
314       fittingModeGroup.Add( button );
315       mFittingModeButton = button;
316
317       modesGroupBackground.Add( fittingModeGroup );
318     }
319
320     {
321       // Vertical table to hold label and button:
322       Toolkit::TableView samplingModeGroup = Toolkit::TableView::New( 2, 1 );
323       samplingModeGroup.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
324       samplingModeGroup.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
325       samplingModeGroup.SetBackgroundColor( BACKGROUND_COLOUR );
326       samplingModeGroup.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f ) );
327       samplingModeGroup.SetFitHeight( 0 );
328       samplingModeGroup.SetFitHeight( 1 );
329
330       TextLabel label = TextLabel::New( "Image sampling mode:" );
331       label.SetStyleName( STYLE_LABEL_TEXT );
332       samplingModeGroup.Add( label );
333
334       Toolkit::PushButton button = CreateButton( SAMPLING_BUTTON_ID, StringFromFilterMode( mSamplingMode ) );
335       samplingModeGroup.Add( button );
336       mSamplingModeButton = button;
337
338       modesGroupBackground.Add( samplingModeGroup );
339     }
340   }
341
342   Toolkit::PushButton CreateButton( const char * id, const char * label )
343   {
344     Toolkit::PushButton button = Toolkit::PushButton::New();
345     button.SetStyleName( STYLE_BUTTON_TEXT );
346     button.SetProperty( Dali::Actor::Property::NAME, id );
347     button.SetProperty( Toolkit::Button::Property::LABEL, label );
348     button.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
349     button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
350     button.ClickedSignal().Connect( this, &ImageScalingAndFilteringController::OnButtonClicked );
351     return button;
352   }
353
354   Toolkit::Popup CreatePopup()
355   {
356     Stage stage = Stage::GetCurrent();
357     const float POPUP_WIDTH_DP = stage.GetSize().width * 0.75f;
358
359     Toolkit::Popup popup = Toolkit::Popup::New();
360     popup.SetProperty( Dali::Actor::Property::NAME, "POPUP" );
361     popup.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
362     popup.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
363     popup.SetProperty( Actor::Property::SIZE, Vector2( POPUP_WIDTH_DP, 0.0f ) );
364
365     popup.OutsideTouchedSignal().Connect( this, &ImageScalingAndFilteringController::OnPopupOutsideTouched );
366
367     return popup;
368   }
369
370   Toolkit::PushButton CreatePopupButton( Actor parent, const char* id )
371   {
372     Toolkit::PushButton button = Toolkit::PushButton::New();
373     button.SetProperty( Dali::Actor::Property::NAME, id );
374     button.SetProperty( Toolkit::Button::Property::LABEL, id );
375
376     button.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
377     button.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT );
378     button.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
379     button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
380
381     button.ClickedSignal().Connect( this, &ImageScalingAndFilteringController::OnButtonClicked );
382
383     parent.Add( button );
384     return button;
385   }
386
387   bool OnButtonClicked( Toolkit::Button button )
388   {
389     if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == FITTING_BUTTON_ID )
390     {
391       mPopup = CreatePopup();
392
393       // Four-row table to hold buttons:
394       Toolkit::TableView fittingModes = Toolkit::TableView::New( 4, 1 );
395       fittingModes.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
396       fittingModes.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
397       fittingModes.SetCellPadding( Size( MARGIN_SIZE, MARGIN_SIZE * 0.5 ) );
398       fittingModes.SetFitHeight( 0 );
399       fittingModes.SetFitHeight( 1 );
400       fittingModes.SetFitHeight( 2 );
401       fittingModes.SetFitHeight( 3 );
402
403       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::SCALE_TO_FILL ) );
404       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::SHRINK_TO_FIT ) );
405       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::FIT_WIDTH ) );
406       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::FIT_HEIGHT ) );
407
408       mPopup.SetContent( fittingModes );
409       Stage::GetCurrent().Add( mPopup );
410       mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
411     }
412     else if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == SAMPLING_BUTTON_ID )
413     {
414       mPopup = CreatePopup();
415
416       // Table to hold buttons for each sampling mode:
417       Toolkit::TableView samplingModes = Toolkit::TableView::New( 6, 1 );
418       samplingModes.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
419       samplingModes.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
420       samplingModes.SetCellPadding( Size( MARGIN_SIZE, MARGIN_SIZE * 0.5 ) );
421       samplingModes.SetFitHeight( 0 );
422       samplingModes.SetFitHeight( 1 );
423       samplingModes.SetFitHeight( 2 );
424       samplingModes.SetFitHeight( 3 );
425       samplingModes.SetFitHeight( 4 );
426       samplingModes.SetFitHeight( 5 );
427
428       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::NEAREST ) );
429       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::LINEAR ) );
430       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX ) );
431       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX_THEN_NEAREST ) );
432       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX_THEN_LINEAR ) );
433       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::NO_FILTER ) );
434
435       mPopup.SetContent( samplingModes );
436       Stage::GetCurrent().Add( mPopup );
437       mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
438     }
439     else if( CheckFittingModeButton( button, FittingMode::SCALE_TO_FILL) ||
440              CheckFittingModeButton( button, FittingMode::SHRINK_TO_FIT) ||
441              CheckFittingModeButton( button, FittingMode::FIT_WIDTH) ||
442              CheckFittingModeButton( button, FittingMode::FIT_HEIGHT) )
443     {
444     }
445     else if( CheckSamplingModeButton( button, SamplingMode::NEAREST ) ||
446              CheckSamplingModeButton( button, SamplingMode::LINEAR ) ||
447              CheckSamplingModeButton( button, SamplingMode::BOX ) ||
448              CheckSamplingModeButton( button, SamplingMode::LINEAR ) ||
449              CheckSamplingModeButton( button, SamplingMode::BOX_THEN_NEAREST ) ||
450              CheckSamplingModeButton( button, SamplingMode::BOX_THEN_LINEAR ) ||
451              CheckSamplingModeButton( button, SamplingMode::NO_FILTER ) )
452     {
453     }
454     return true;
455   }
456
457   bool CheckFittingModeButton( Actor &button, FittingMode::Type mode )
458   {
459     const char * const modeName = StringFromScalingMode( mode );
460     if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == modeName )
461     {
462       mFittingMode = mode;
463       mFittingModeButton.SetProperty( Toolkit::Button::Property::LABEL, modeName );
464       ResizeImage();
465       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
466       mPopup.Reset();
467       return true;
468     }
469     return false;
470   }
471
472   bool CheckSamplingModeButton( Actor &button, SamplingMode::Type mode )
473   {
474     const char * const modeName = StringFromFilterMode( mode );
475     if( button.GetProperty< std::string >( Dali::Actor::Property::NAME ) == modeName )
476     {
477       mSamplingMode = mode;
478       mSamplingModeButton.SetProperty( Toolkit::Button::Property::LABEL, modeName );
479       ResizeImage();
480       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
481       mPopup.Reset();
482       return true;
483     }
484     return false;
485   }
486
487   void OnPopupOutsideTouched()
488   {
489     if( mPopup )
490     {
491       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
492       mPopup.Reset();
493     }
494   }
495
496   bool OnControlTouched( Actor actor, const TouchData& event )
497   {
498     if(event.GetPointCount() > 0)
499     {
500       switch( event.GetState( 0 ) )
501       {
502         case PointState::UP:
503         {
504           const std::string & name = actor.GetProperty< std::string >( Dali::Actor::Property::NAME );
505           if( name == NEXT_BUTTON_ID )
506           {
507             mCurrentPath = mCurrentPath + 1;
508             mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
509             ResizeImage();
510           }
511           else if( name == PREVIOUS_BUTTON_ID )
512           {
513             mCurrentPath = mCurrentPath - 1;
514             mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
515             ResizeImage();
516           }
517           break;
518         }
519         default:
520         {
521           break;
522         }
523       } // end switch
524     }
525
526     return false;
527   }
528
529   void OnPinch( Actor actor, const PinchGesture& pinch )
530   {
531     if( pinch.state == Gesture::Started )
532     {
533       mLastPinchScale = pinch.scale;
534     }
535     const float scale = pinch.scale;
536
537     if( ! Equals( scale, mLastPinchScale ) )
538     {
539       if ( scale < mLastPinchScale )
540       {
541         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
542         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
543       }
544       else
545       {
546         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
547         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
548       }
549       ResizeImage();
550     }
551     mLastPinchScale = scale;
552   }
553
554   void OnPan( Actor actor, const PanGesture& gesture )
555   {
556     Stage stage = Stage::GetCurrent();
557     // 1.0f and 0.75f are the maximum size caps of the resized image, as a factor of stage-size.
558     mImageStageScale.x = std::max( 0.05f, std::min( 0.95f,  mImageStageScale.x + ( gesture.displacement.x * 2.0f / stage.GetSize().width ) ) );
559     mImageStageScale.y = std::max( 0.05f, std::min( 0.70f, mImageStageScale.y + ( gesture.displacement.y * 2.0f / stage.GetSize().height ) ) );
560
561     ResizeImage();
562   }
563
564   void OnKeyEvent(const KeyEvent& event)
565   {
566     if( event.state == KeyEvent::Down )
567     {
568       if( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
569       {
570         if( mPopup && mPopup.GetCurrentProperty< bool >( Actor::Property::VISIBLE ) )
571         {
572           mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
573           mPopup.Reset();
574         }
575         else
576         {
577           mApplication.Quit();
578         }
579       }
580       else if ( event.keyPressedName == "Right" )
581       {
582         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
583       }
584       else if ( event.keyPressedName == "Left" )
585       {
586         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
587       }
588       else if ( event.keyPressedName == "Up" )
589       {
590         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
591       }
592       else if ( event.keyPressedName == "Down" )
593       {
594         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
595       }
596       else if ( event.keyPressedName == "o" )
597       {
598         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
599         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
600       }
601       else if ( event.keyPressedName == "p" )
602       {
603         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
604         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
605       }
606       else if ( event.keyPressedName == "n" )
607       {
608         mCurrentPath = mCurrentPath + 1;
609         mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
610       }
611       else if ( event.keyPressedName == "b" )
612       {
613         mCurrentPath = mCurrentPath - 1;
614         mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
615       }
616       // Cycle filter and scaling modes:
617       else if ( event.keyPressedName == "f" )
618       {
619         mSamplingMode = NextFilterMode( mSamplingMode );
620         mSamplingModeButton.SetProperty( Toolkit::Button::Property::LABEL, StringFromFilterMode( mSamplingMode ) );
621       }
622       // Cycle filter and scaling modes:
623       else if ( event.keyPressedName == "s" )
624       {
625         mFittingMode = NextScalingMode( mFittingMode );
626         mFittingModeButton.SetProperty( Toolkit::Button::Property::LABEL, StringFromScalingMode( mFittingMode ) );
627       }
628       else
629       {
630         return;
631       }
632
633       ResizeImage();
634     }
635   }
636
637 private:
638
639   void LoadImage()
640   {
641     mImageLoading = true;
642
643     const char * const path = IMAGE_PATHS[ mCurrentPath ];
644     Stage stage = Stage::GetCurrent();
645     Size imageSize = stage.GetSize() * mImageStageScale;
646     mImageView.SetProperty( Actor::Property::SIZE, imageSize );
647
648     Property::Map map;
649     map[Toolkit::ImageVisual::Property::URL] = path;
650     map[Toolkit::ImageVisual::Property::DESIRED_WIDTH] = imageSize.x;
651     map[Toolkit::ImageVisual::Property::DESIRED_HEIGHT] = imageSize.y;
652     map[Toolkit::ImageVisual::Property::FITTING_MODE] = mFittingMode;
653     map[Toolkit::ImageVisual::Property::SAMPLING_MODE] = mSamplingMode;
654
655     mImageView.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
656
657   }
658
659   void ResizeImage()
660   {
661
662     Stage stage = Stage::GetCurrent();
663     Size imageSize = stage.GetSize() * mImageStageScale;
664
665     LoadImage();
666
667     // Border size needs to be modified to take into account the width of the frame.
668     Vector2 borderScale( ( imageSize + Vector2( BORDER_WIDTH * 2.0f, BORDER_WIDTH * 2.0f ) ) / stage.GetSize() );
669     mDesiredBox.SetProperty( Actor::Property::SIZE, stage.GetSize() * borderScale );
670   }
671
672 private:
673   Application&  mApplication;
674   Toolkit::ImageView mDesiredBox; //< Background rectangle to show requested image size.
675   Toolkit::PushButton mFittingModeButton;
676   Toolkit::PushButton mSamplingModeButton;
677   Toolkit::Popup mPopup;
678   PinchGestureDetector mPinchDetector;
679   float mLastPinchScale;
680   Toolkit::ImageView mGrabCorner;
681   PanGestureDetector mPanGestureDetector;
682   Toolkit::ImageView mImageView;
683   Vector2 mImageStageScale;
684   int mCurrentPath;
685   FittingMode::Type mFittingMode;
686   SamplingMode::Type mSamplingMode;
687   bool mImageLoading;
688   bool mQueuedImageLoad;
689
690 };
691
692 int DALI_EXPORT_API main( int argc, char **argv )
693 {
694   Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
695   ImageScalingAndFilteringController test( application );
696   application.MainLoop();
697   return 0;
698 }