[dali_1.1.45] Merge branch 'devel/master'
[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( 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  = "grouplabel";
49 const char* const STYLE_BUTTON_TEXT = "buttonlabel";
50
51
52 const char* IMAGE_PATHS[] =
53 {
54   // Variety of sizes, shapes and formats:
55   DEMO_IMAGE_DIR "dali-logo.png",
56   DEMO_IMAGE_DIR "layer1.png",
57   DEMO_IMAGE_DIR "layer2.png",
58   DEMO_IMAGE_DIR "animation-list.png",
59   DEMO_IMAGE_DIR "music-libray-main-screen.png",
60   DEMO_IMAGE_DIR "music-libray-record-cover.png",
61   DEMO_IMAGE_DIR "contacts-background.png",
62   DEMO_IMAGE_DIR "portrait_screen_primitive_shapes.gif",
63   DEMO_IMAGE_DIR "landscape_screen_primitive_shapes.gif",
64   DEMO_IMAGE_DIR "square_primitive_shapes.bmp",
65   DEMO_IMAGE_DIR "gallery-large-14.jpg",
66   DEMO_IMAGE_DIR "book-landscape-cover.jpg",
67   DEMO_IMAGE_DIR "book-portrait-p1.jpg",
68   DEMO_IMAGE_DIR "book-landscape-cover-back.jpg",
69
70   // Worst case for aliasing in downscaling, 2k x 2k 1 bit per pixel dithered
71   // black and white image:
72   DEMO_IMAGE_DIR "gallery-large-14.wbmp",
73
74   DEMO_IMAGE_DIR "background-1.jpg",
75   DEMO_IMAGE_DIR "background-blocks.jpg",
76   DEMO_IMAGE_DIR "background-magnifier.jpg",
77   DEMO_IMAGE_DIR "gallery-large-14.jpg",
78   NULL
79 };
80 const int NUM_IMAGE_PATHS = sizeof(IMAGE_PATHS) / sizeof(IMAGE_PATHS[0]) - 1u;
81
82 /** Cycle the scaling mode options. */
83 FittingMode::Type NextScalingMode( FittingMode::Type oldMode )
84 {
85   FittingMode::Type newMode = FittingMode::SHRINK_TO_FIT;
86   switch ( oldMode )
87   {
88     case FittingMode::SHRINK_TO_FIT:
89       newMode = FittingMode::SCALE_TO_FILL;
90       break;
91     case FittingMode::SCALE_TO_FILL:
92       newMode = FittingMode::FIT_WIDTH;
93       break;
94     case FittingMode::FIT_WIDTH:
95       newMode = FittingMode::FIT_HEIGHT;
96       break;
97     case FittingMode::FIT_HEIGHT:
98       newMode = FittingMode::SHRINK_TO_FIT;
99       break;
100   }
101   return newMode;
102 }
103
104 /** Cycle through filter mode options. */
105 SamplingMode::Type NextFilterMode( SamplingMode::Type oldMode )
106 {
107   SamplingMode::Type newMode = SamplingMode::BOX;
108
109   switch ( oldMode )
110   {
111     case SamplingMode::BOX:
112       newMode = SamplingMode::NEAREST;
113       break;
114     case SamplingMode::NEAREST:
115       newMode = SamplingMode::LINEAR;
116       break;
117     case SamplingMode::LINEAR:
118       newMode = SamplingMode::BOX_THEN_NEAREST;
119       break;
120     case SamplingMode::BOX_THEN_NEAREST:
121       newMode = SamplingMode::BOX_THEN_LINEAR;
122       break;
123     case SamplingMode::BOX_THEN_LINEAR:
124       newMode = SamplingMode::NO_FILTER;
125       break;
126     case SamplingMode::NO_FILTER:
127       newMode = SamplingMode::BOX;
128       break;
129     case SamplingMode::DONT_CARE:
130       newMode = SamplingMode::BOX;
131       break;
132   }
133   return newMode;
134 }
135
136 const char* StringFromScalingMode( FittingMode::Type scalingMode )
137 {
138   return scalingMode == FittingMode::SCALE_TO_FILL ? "SCALE_TO_FILL" : scalingMode == FittingMode::SHRINK_TO_FIT ? "SHRINK_TO_FIT" : scalingMode == FittingMode::FIT_WIDTH ? "FIT_WIDTH" : scalingMode == FittingMode::FIT_HEIGHT ? "FIT_HEIGHT" : "UnknownScalingMode";
139 }
140
141 const char* StringFromFilterMode( SamplingMode::Type filterMode )
142 {
143   return filterMode == SamplingMode::BOX ? "BOX" : filterMode == SamplingMode::BOX_THEN_NEAREST ? "BOX_THEN_NEAREST" : filterMode == SamplingMode::BOX_THEN_LINEAR ? "BOX_THEN_LINEAR" : filterMode == SamplingMode::NEAREST ? "NEAREST" : filterMode == SamplingMode::LINEAR ? "LINEAR" : filterMode == SamplingMode::NO_FILTER ? "NO_FILTER" : filterMode == SamplingMode::DONT_CARE ? "DONT_CARE" : "UnknownFilterMode";
144 }
145
146 }
147
148 // This example shows the load-time image scaling and filtering features.
149 //
150 class ImageScalingAndFilteringController : public ConnectionTracker
151 {
152 public:
153
154   ImageScalingAndFilteringController( Application& application )
155   : mApplication( application ),
156     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     // Background image:
179     Dali::Property::Map backgroundImage;
180     backgroundImage.Insert( Toolkit::Visual::Property::TYPE,  Toolkit::Visual::IMAGE );
181     backgroundImage.Insert( Toolkit::ImageVisual::Property::URL,  BACKGROUND_IMAGE );
182     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, stage.GetSize().width );
183     backgroundImage.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, stage.GetSize().height );
184     backgroundImage.Insert( Toolkit::ImageVisual::Property::FITTING_MODE,   FittingMode::SCALE_TO_FILL );
185     backgroundImage.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE,   SamplingMode::BOX_THEN_NEAREST );
186
187     Toolkit::ImageView background = Toolkit::ImageView::New();
188     background.SetProperty( Toolkit::ImageView::Property::IMAGE, backgroundImage );
189     background.SetAnchorPoint( AnchorPoint::TOP_LEFT );
190     background.SetSize( stage.GetSize() );
191     stage.Add( background );
192
193     BufferImage heightBackground = BufferImage::WHITE();
194     PixelBuffer* const heightPixel = heightBackground.GetBuffer();
195     heightPixel[0] = 0x8f;
196     heightPixel[1] = 0x8f;
197     heightPixel[2] = 0x8f;
198
199     BufferImage widthBackground = BufferImage::WHITE();
200     PixelBuffer* const widthPixel = widthBackground.GetBuffer();
201     widthPixel[0] = 0x4f;
202     widthPixel[1] = 0x4f;
203     widthPixel[2] = 0x4f;
204
205     mHeightBox = Toolkit::ImageView::New( heightBackground );
206     mHeightBox.SetOpacity( 0.2f );
207     background.Add( mHeightBox );
208
209     mWidthBox = Toolkit::ImageView::New( widthBackground );
210     mWidthBox.SetOpacity( 0.2f );
211     background.Add( mWidthBox );
212
213     mDesiredBox = Toolkit::ImageView::New( BORDER_IMAGE );
214     background.Add( mDesiredBox );
215
216     mDesiredBox.SetSize( stage.GetSize() * mImageStageScale );
217     mDesiredBox.SetParentOrigin( ParentOrigin::CENTER );
218     mDesiredBox.SetAnchorPoint( AnchorPoint::CENTER );
219
220     mHeightBox.SetSize( stage.GetSize().width,  (stage.GetSize() * mImageStageScale).height );
221     mHeightBox.SetParentOrigin( ParentOrigin::CENTER );
222     mHeightBox.SetAnchorPoint( AnchorPoint::CENTER );
223
224     mWidthBox.SetSize( (stage.GetSize() * mImageStageScale).width, stage.GetSize().height );
225     mWidthBox.SetParentOrigin( ParentOrigin::CENTER );
226     mWidthBox.SetAnchorPoint( AnchorPoint::CENTER );
227
228     // Initialize the actor
229     mImageView = Toolkit::ImageView::New( IMAGE_PATHS[ 0 ] );
230
231     // Reposition the actor
232     mImageView.SetParentOrigin( ParentOrigin::CENTER );
233     mImageView.SetAnchorPoint( AnchorPoint::CENTER );
234
235     // Display the actor on the stage
236     mDesiredBox.Add( mImageView );
237
238     mImageView.SetSize( stage.GetSize() * mImageStageScale );
239
240     // Setup the pinch detector for scaling the desired image load dimensions:
241     mPinchDetector = PinchGestureDetector::New();
242     mPinchDetector.Attach( mImageView );
243     mPinchDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPinch );
244
245     mGrabCorner = Toolkit::ImageView::New( RESIZE_HANDLE_IMAGE );
246     mGrabCorner.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
247     mGrabCorner.SetName( "GrabCorner" );
248     mGrabCorner.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
249     mGrabCorner.SetParentOrigin( ParentOrigin::BOTTOM_RIGHT );
250     mGrabCorner.SetPosition( -BORDER_WIDTH, -BORDER_WIDTH );
251     mGrabCorner.SetOpacity( 0.6f );
252
253     Layer grabCornerLayer = Layer::New();
254     grabCornerLayer.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
255     grabCornerLayer.SetParentOrigin( ParentOrigin::BOTTOM_RIGHT );
256     grabCornerLayer.Add( mGrabCorner );
257     mDesiredBox.Add( grabCornerLayer );
258
259     mPanGestureDetector = PanGestureDetector::New();
260     mPanGestureDetector.Attach( mGrabCorner );
261     mPanGestureDetector.DetectedSignal().Connect( this, &ImageScalingAndFilteringController::OnPan );
262
263     // Tie-in input event handlers:
264     stage.KeyEventSignal().Connect( this, &ImageScalingAndFilteringController::OnKeyEvent );
265
266     CreateControls();
267
268     ResizeImage();
269   }
270
271   /**
272    * Create the GUI controls which float above the scene
273    */
274   void CreateControls()
275   {
276     Stage stage = Stage::GetCurrent();
277
278     Dali::Layer controlsLayer = Dali::Layer::New();
279     controlsLayer.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
280     controlsLayer.SetSizeModeFactor( Vector3( 1.0f, 1.0f, 1.0f ) );
281     controlsLayer.SetAnchorPoint( AnchorPoint::TOP_LEFT);
282     controlsLayer.SetParentOrigin( ParentOrigin::TOP_LEFT);
283     stage.Add( controlsLayer );
284
285     // Back and next image buttons in corners of stage:
286     unsigned int playWidth = std::min( stage.GetSize().x * (1 / 5.0f), 58.0f );
287     Toolkit::ImageView imagePrevious = Toolkit::ImageView::New( DALI_ICON_PLAY, ImageDimensions( playWidth, playWidth ) );
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.TouchSignal().Connect( this, &ImageScalingAndFilteringController::OnControlTouched );
298
299     // Next image button:
300     Toolkit::ImageView imageNext = Toolkit::ImageView::New( DALI_ICON_PLAY, ImageDimensions( playWidth, playWidth ) );
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.TouchSignal().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       fittingModeGroup.Add( button );
341       mFittingModeButton = button;
342
343       modesGroupBackground.Add( fittingModeGroup );
344     }
345
346     {
347       // Vertical table to hold label and button:
348       Toolkit::TableView samplingModeGroup = Toolkit::TableView::New( 2, 1 );
349       samplingModeGroup.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
350       samplingModeGroup.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
351       samplingModeGroup.SetBackgroundColor( BACKGROUND_COLOUR );
352       samplingModeGroup.SetCellPadding( Size( MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f ) );
353       samplingModeGroup.SetFitHeight( 0 );
354       samplingModeGroup.SetFitHeight( 1 );
355
356       TextLabel label = TextLabel::New( "Image sampling mode:" );
357       label.SetProperty( Toolkit::Control::Property::STYLE_NAME, STYLE_LABEL_TEXT );
358       samplingModeGroup.Add( label );
359
360       Toolkit::PushButton button = CreateButton( SAMPLING_BUTTON_ID, StringFromFilterMode( mSamplingMode ) );
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.SetLabelText( label );
374     button.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
375     button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
376     button.ClickedSignal().Connect( this, &ImageScalingAndFilteringController::OnButtonClicked );
377     return button;
378   }
379
380   Toolkit::Popup CreatePopup()
381   {
382     Stage stage = Stage::GetCurrent();
383     const float POPUP_WIDTH_DP = stage.GetSize().width * 0.75f;
384
385     Toolkit::Popup popup = Toolkit::Popup::New();
386     popup.SetName( "POPUP" );
387     popup.SetParentOrigin( ParentOrigin::CENTER );
388     popup.SetAnchorPoint( AnchorPoint::CENTER );
389     popup.SetSize( POPUP_WIDTH_DP, 0.0f );
390
391     popup.OutsideTouchedSignal().Connect( this, &ImageScalingAndFilteringController::OnPopupOutsideTouched );
392
393     return popup;
394   }
395
396   Toolkit::PushButton CreatePopupButton( Actor parent, const char* id )
397   {
398     Toolkit::PushButton button = Toolkit::PushButton::New();
399     button.SetName( id );
400     button.SetLabelText( id );
401
402     button.SetAnchorPoint( AnchorPoint::TOP_LEFT );
403     button.SetParentOrigin( ParentOrigin::BOTTOM_LEFT );
404     button.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
405     button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
406
407     button.ClickedSignal().Connect( this, &ImageScalingAndFilteringController::OnButtonClicked );
408
409     parent.Add( button );
410     return button;
411   }
412
413   bool OnButtonClicked( Toolkit::Button button )
414   {
415     if( button.GetName() == FITTING_BUTTON_ID )
416     {
417       mPopup = CreatePopup();
418
419       // Four-row table to hold buttons:
420       Toolkit::TableView fittingModes = Toolkit::TableView::New( 4, 1 );
421       fittingModes.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
422       fittingModes.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
423       fittingModes.SetCellPadding( Size( MARGIN_SIZE, MARGIN_SIZE * 0.5 ) );
424       fittingModes.SetFitHeight( 0 );
425       fittingModes.SetFitHeight( 1 );
426       fittingModes.SetFitHeight( 2 );
427       fittingModes.SetFitHeight( 3 );
428
429       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::SCALE_TO_FILL ) );
430       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::SHRINK_TO_FIT ) );
431       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::FIT_WIDTH ) );
432       CreatePopupButton( fittingModes, StringFromScalingMode( FittingMode::FIT_HEIGHT ) );
433
434       mPopup.SetContent( fittingModes );
435       Stage::GetCurrent().Add( mPopup );
436       mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
437     }
438     else if( button.GetName() == SAMPLING_BUTTON_ID )
439     {
440       mPopup = CreatePopup();
441
442       // Table to hold buttons for each sampling mode:
443       Toolkit::TableView samplingModes = Toolkit::TableView::New( 6, 1 );
444       samplingModes.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
445       samplingModes.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
446       samplingModes.SetCellPadding( Size( MARGIN_SIZE, MARGIN_SIZE * 0.5 ) );
447       samplingModes.SetFitHeight( 0 );
448       samplingModes.SetFitHeight( 1 );
449       samplingModes.SetFitHeight( 2 );
450       samplingModes.SetFitHeight( 3 );
451       samplingModes.SetFitHeight( 4 );
452       samplingModes.SetFitHeight( 5 );
453
454       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::NEAREST ) );
455       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::LINEAR ) );
456       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX ) );
457       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX_THEN_NEAREST ) );
458       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::BOX_THEN_LINEAR ) );
459       CreatePopupButton( samplingModes, StringFromFilterMode( SamplingMode::NO_FILTER ) );
460
461       mPopup.SetContent( samplingModes );
462       Stage::GetCurrent().Add( mPopup );
463       mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
464     }
465     else if( CheckFittingModeButton( button, FittingMode::SCALE_TO_FILL) ||
466              CheckFittingModeButton( button, FittingMode::SHRINK_TO_FIT) ||
467              CheckFittingModeButton( button, FittingMode::FIT_WIDTH) ||
468              CheckFittingModeButton( button, FittingMode::FIT_HEIGHT) )
469     {
470     }
471     else if( CheckSamplingModeButton( button, SamplingMode::NEAREST ) ||
472              CheckSamplingModeButton( button, SamplingMode::LINEAR ) ||
473              CheckSamplingModeButton( button, SamplingMode::BOX ) ||
474              CheckSamplingModeButton( button, SamplingMode::LINEAR ) ||
475              CheckSamplingModeButton( button, SamplingMode::BOX_THEN_NEAREST ) ||
476              CheckSamplingModeButton( button, SamplingMode::BOX_THEN_LINEAR ) ||
477              CheckSamplingModeButton( button, SamplingMode::NO_FILTER ) )
478     {
479     }
480     return true;
481   }
482
483   bool CheckFittingModeButton( Actor &button, FittingMode::Type mode )
484   {
485     const char * const modeName = StringFromScalingMode( mode );
486     if( button.GetName() == modeName )
487     {
488       mFittingMode = mode;
489       mFittingModeButton.SetLabelText( modeName );
490       ResizeImage();
491       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
492       mPopup.Reset();
493       return true;
494     }
495     return false;
496   }
497
498   bool CheckSamplingModeButton( Actor &button, SamplingMode::Type mode )
499   {
500     const char * const modeName = StringFromFilterMode( mode );
501     if( button.GetName() == modeName )
502     {
503       mSamplingMode = mode;
504       mSamplingModeButton.SetLabelText( modeName );
505       ResizeImage();
506       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
507       mPopup.Reset();
508       return true;
509     }
510     return false;
511   }
512
513   void OnPopupOutsideTouched()
514   {
515     if( mPopup )
516     {
517       mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
518       mPopup.Reset();
519     }
520   }
521
522   void OnImageLoaded( ResourceImage image )
523   {
524     DALI_ASSERT_DEBUG( image == mNextImage );
525     mImageView.SetImage( image );
526     mImageView.SetSize( Size( image.GetWidth(), image.GetHeight() ) );
527     mImageLoading = false;
528
529     // We have finished loading, if a resize had occured during the load, trigger another load now.
530     if( mQueuedImageLoad )
531     {
532       mQueuedImageLoad = false;
533       LoadImage();
534     }
535   }
536
537   bool OnControlTouched( Actor actor, const TouchData& event )
538   {
539     if(event.GetPointCount() > 0)
540     {
541       switch( event.GetState( 0 ) )
542       {
543         case PointState::UP:
544         {
545           const std::string & name = actor.GetName();
546           if( name == NEXT_BUTTON_ID )
547           {
548             mCurrentPath = mCurrentPath + 1;
549             mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
550             ResizeImage();
551           }
552           else if( name == PREVIOUS_BUTTON_ID )
553           {
554             mCurrentPath = mCurrentPath - 1;
555             mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
556             ResizeImage();
557           }
558           break;
559         }
560         default:
561         {
562           break;
563         }
564       } // end switch
565     }
566
567     return false;
568   }
569
570   void OnPinch( Actor actor, const PinchGesture& pinch )
571   {
572     if( pinch.state == Gesture::Started )
573     {
574       mLastPinchScale = pinch.scale;
575     }
576     const float scale = pinch.scale;
577
578     if( ! Equals( scale, mLastPinchScale ) )
579     {
580       if ( scale < mLastPinchScale )
581       {
582         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
583         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
584       }
585       else
586       {
587         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
588         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
589       }
590       ResizeImage();
591     }
592     mLastPinchScale = scale;
593   }
594
595   void OnPan( Actor actor, const PanGesture& gesture )
596   {
597     Stage stage = Stage::GetCurrent();
598     // 1.0f and 0.75f are the maximum size caps of the resized image, as a factor of stage-size.
599     mImageStageScale.x = std::max( 0.05f, std::min( 0.95f,  mImageStageScale.x + ( gesture.displacement.x * 2.0f / stage.GetSize().width ) ) );
600     mImageStageScale.y = std::max( 0.05f, std::min( 0.70f, mImageStageScale.y + ( gesture.displacement.y * 2.0f / stage.GetSize().height ) ) );
601
602     ResizeImage();
603   }
604
605   void OnKeyEvent(const KeyEvent& event)
606   {
607     if( event.state == KeyEvent::Down )
608     {
609       if( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
610       {
611         if( mPopup && mPopup.IsVisible() )
612         {
613           mPopup.SetDisplayState( Toolkit::Popup::HIDDEN );
614           mPopup.Reset();
615         }
616         else
617         {
618           mApplication.Quit();
619         }
620       }
621       else if ( event.keyPressedName == "Right" )
622       {
623         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
624       }
625       else if ( event.keyPressedName == "Left" )
626       {
627         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
628       }
629       else if ( event.keyPressedName == "Up" )
630       {
631         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
632       }
633       else if ( event.keyPressedName == "Down" )
634       {
635         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
636       }
637       else if ( event.keyPressedName == "o" )
638       {
639         mImageStageScale.x = std::max( 0.05f, mImageStageScale.x * 0.9f );
640         mImageStageScale.y = std::max( 0.05f, mImageStageScale.y * 0.9f );
641       }
642       else if ( event.keyPressedName == "p" )
643       {
644         mImageStageScale.x = std::max( 0.05f, std::min( 1.0f, mImageStageScale.x * 1.1f ) );
645         mImageStageScale.y = std::max( 0.05f, std::min( 1.0f, mImageStageScale.y * 1.1f ) );
646       }
647       else if ( event.keyPressedName == "n" )
648       {
649         mCurrentPath = mCurrentPath + 1;
650         mCurrentPath = mCurrentPath <  NUM_IMAGE_PATHS ? mCurrentPath : 0;
651       }
652       else if ( event.keyPressedName == "b" )
653       {
654         mCurrentPath = mCurrentPath - 1;
655         mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
656       }
657       // Cycle filter and scaling modes:
658       else if ( event.keyPressedName == "f" )
659       {
660         mSamplingMode = NextFilterMode( mSamplingMode );
661         mSamplingModeButton.SetLabelText( StringFromFilterMode( mSamplingMode ) );
662       }
663       // Cycle filter and scaling modes:
664       else if ( event.keyPressedName == "s" )
665       {
666         mFittingMode = NextScalingMode( mFittingMode );
667         mFittingModeButton.SetLabelText( StringFromScalingMode( mFittingMode ) );
668       }
669       else
670       {
671         return;
672       }
673
674       ResizeImage();
675     }
676   }
677
678 private:
679
680   void LoadImage()
681   {
682     mImageLoading = true;
683
684     const char * const path = IMAGE_PATHS[ mCurrentPath ];
685     Stage stage = Stage::GetCurrent();
686     Size imageSize = stage.GetSize() * mImageStageScale;
687     const ImageDimensions imageSizeInt = ImageDimensions::FromFloatArray( &imageSize.x );
688
689     ResourceImage image = ResourceImage::New( path, imageSizeInt, mFittingMode, mSamplingMode );
690
691     // If the image was cached, the load has already occured, bypass hooking the signal.
692     if( image.GetLoadingState() )
693     {
694       OnImageLoaded( image );
695     }
696     else
697     {
698       image.LoadingFinishedSignal().Connect( this, &ImageScalingAndFilteringController::OnImageLoaded );
699     }
700
701     mNextImage = image;
702   }
703
704   void ResizeImage()
705   {
706
707     Stage stage = Stage::GetCurrent();
708     Size imageSize = stage.GetSize() * mImageStageScale;
709
710     // If an image is already loading, queue another load when it has finished.
711     // This way we get continuous updates instead of constantly re-requesting loads.
712     if( mImageLoading )
713     {
714       mQueuedImageLoad = true;
715     }
716     else
717     {
718       LoadImage();
719     }
720
721     // Border size needs to be modified to take into account the width of the frame.
722     Vector2 borderScale( ( imageSize + Vector2( BORDER_WIDTH * 2.0f, BORDER_WIDTH * 2.0f ) ) / stage.GetSize() );
723     mDesiredBox.SetSize( stage.GetSize() * borderScale );
724
725     mHeightBox.SetSize( stage.GetSize().width, (stage.GetSize() * mImageStageScale).height );
726     mWidthBox.SetSize( (stage.GetSize() * mImageStageScale).width, stage.GetSize().height );
727   }
728
729 private:
730   Application&  mApplication;
731   Toolkit::ImageView mDesiredBox; //< Background rectangle to show requested image size.
732   Toolkit::ImageView mHeightBox;  //< Background horizontal stripe to show requested image height.
733   Toolkit::ImageView mWidthBox;   //< Background vertical stripe to show requested image width.
734   Toolkit::PushButton mFittingModeButton;
735   Toolkit::PushButton mSamplingModeButton;
736   Toolkit::Popup mPopup;
737   PinchGestureDetector mPinchDetector;
738   float mLastPinchScale;
739   Toolkit::ImageView mGrabCorner;
740   PanGestureDetector mPanGestureDetector;
741   Toolkit::ImageView mImageView;
742   ResourceImage mNextImage; //< Currently-loading image
743   Vector2 mImageStageScale;
744   int mCurrentPath;
745   FittingMode::Type mFittingMode;
746   SamplingMode::Type mSamplingMode;
747   bool mImageLoading;
748   bool mQueuedImageLoad;
749
750 };
751
752 void RunTest( Application& application )
753 {
754   ImageScalingAndFilteringController test( application );
755
756   application.MainLoop();
757 }
758
759 // Entry point for Linux & Tizen applications
760 int DALI_EXPORT_API main( int argc, char **argv )
761 {
762   Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
763
764   RunTest( application );
765
766   return 0;
767 }