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