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