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