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