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