Updated demos to use DALi clang-format
[platform/core/uifw/dali-demo.git] / examples / image-scaling-and-filtering / image-scaling-and-filtering-example.cpp
1 /*
2  * Copyright (c) 2020 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-toolkit/dali-toolkit.h>
19 #include <dali-toolkit/devel-api/controls/popup/popup.h>
20 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
21 #include <dali/dali.h>
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <iostream>
24 #include "shared/view.h"
25
26 using namespace Dali;
27 using Toolkit::TextLabel;
28
29 namespace
30 {
31 const char*   BACKGROUND_IMAGE(DEMO_IMAGE_DIR "background-gradient.jpg");
32 const Vector4 BACKGROUND_COLOUR(1.0f, 1.0f, 1.0f, 0.15f);
33
34 const char* BORDER_IMAGE(DEMO_IMAGE_DIR "border-4px.9.png");
35 const int   BORDER_WIDTH = (11.0f + 4.0f); // Shadow size = 11, border size = 4.
36 const char* RESIZE_HANDLE_IMAGE(DEMO_IMAGE_DIR "resize-handle.png");
37
38 const int MARGIN_SIZE = 10;
39
40 const char* const NEXT_BUTTON_ID     = "NEXT_BUTTON";
41 const char* const PREVIOUS_BUTTON_ID = "PREVIOUS_BUTTON";
42 const char* const DALI_ICON_PLAY     = DEMO_IMAGE_DIR "icon-play.png";
43
44 const char* const FITTING_BUTTON_ID    = "FITTING_BUTTON";
45 const char* const SAMPLING_BUTTON_ID   = "SAMPLING_BUTTON";
46 const char* const FITTING_BUTTON_TEXT  = "Fitting";
47 const char* const SAMPLING_BUTTON_TEXT = "Sampling";
48
49 const char* const STYLE_LABEL_TEXT  = "ImageScalingGroupLabel";
50 const char* const STYLE_BUTTON_TEXT = "ImageScalingButton";
51
52 const char* IMAGE_PATHS[] =
53   {
54     // Variety of sizes, shapes and formats:
55     DEMO_IMAGE_DIR "dali-logo.png",
56     DEMO_IMAGE_DIR "layer1.png",
57     DEMO_IMAGE_DIR "layer2.png",
58     DEMO_IMAGE_DIR "animation-list.png",
59     DEMO_IMAGE_DIR "music-libray-main-screen.png",
60     DEMO_IMAGE_DIR "music-libray-record-cover.png",
61     DEMO_IMAGE_DIR "contacts-background.png",
62     DEMO_IMAGE_DIR "portrait_screen_primitive_shapes.gif",
63     DEMO_IMAGE_DIR "landscape_screen_primitive_shapes.gif",
64     DEMO_IMAGE_DIR "square_primitive_shapes.bmp",
65     DEMO_IMAGE_DIR "gallery-large-14.jpg",
66     DEMO_IMAGE_DIR "book-landscape-cover.jpg",
67     DEMO_IMAGE_DIR "book-portrait-p1.jpg",
68     DEMO_IMAGE_DIR "book-landscape-cover-back.jpg",
69
70     // Worst case for aliasing in downscaling, 2k x 2k 1 bit per pixel dithered
71     // black and white image:
72     DEMO_IMAGE_DIR "gallery-large-14.wbmp",
73
74     DEMO_IMAGE_DIR "background-1.jpg",
75     DEMO_IMAGE_DIR "background-blocks.jpg",
76     DEMO_IMAGE_DIR "background-magnifier.jpg",
77     DEMO_IMAGE_DIR "gallery-large-14.jpg",
78     NULL};
79 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 } // namespace
146
147 // This example shows the load-time image scaling and filtering features.
148 //
149 class ImageScalingAndFilteringController : public ConnectionTracker
150 {
151 public:
152   ImageScalingAndFilteringController(Application& application)
153   : mApplication(application),
154     mLastPinchScale(1.0f),
155     mImageWindowScale(0.5f, 0.5f),
156     mCurrentPath(0),
157     mFittingMode(FittingMode::FIT_WIDTH),
158     mSamplingMode(SamplingMode::BOX_THEN_LINEAR),
159     mImageLoading(false),
160     mQueuedImageLoad(false)
161   {
162     // Connect to the Application's Init signal
163     mApplication.InitSignal().Connect(this, &ImageScalingAndFilteringController::Create);
164   }
165
166   ~ImageScalingAndFilteringController()
167   {
168     // Nothing to do here;
169   }
170
171   // The Init signal is received once (only) during the Application lifetime
172   void Create(Application& application)
173   {
174     // Get a handle to the window
175     Window  window     = application.GetWindow();
176     Vector2 windowSize = window.GetSize();
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, windowSize.width);
183     backgroundImage.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, windowSize.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.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
190     background.SetProperty(Actor::Property::SIZE, windowSize);
191     window.Add(background);
192
193     mDesiredBox = Toolkit::ImageView::New(BORDER_IMAGE);
194     background.Add(mDesiredBox);
195
196     mDesiredBox.SetProperty(Actor::Property::SIZE, windowSize * mImageWindowScale);
197     mDesiredBox.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
198     mDesiredBox.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
199
200     // Initialize the actor
201     mImageView = Toolkit::ImageView::New(IMAGE_PATHS[0]);
202
203     // Reposition the actor
204     mImageView.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
205     mImageView.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
206
207     // Display the actor on the window
208     mDesiredBox.Add(mImageView);
209
210     mImageView.SetProperty(Actor::Property::SIZE, windowSize * mImageWindowScale);
211
212     // Setup the pinch detector for scaling the desired image load dimensions:
213     mPinchDetector = PinchGestureDetector::New();
214     mPinchDetector.Attach(mImageView);
215     mPinchDetector.DetectedSignal().Connect(this, &ImageScalingAndFilteringController::OnPinch);
216
217     mGrabCorner = Toolkit::ImageView::New(RESIZE_HANDLE_IMAGE);
218     mGrabCorner.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
219     mGrabCorner.SetProperty(Dali::Actor::Property::NAME, "GrabCorner");
220     mGrabCorner.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
221     mGrabCorner.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT);
222     mGrabCorner.SetProperty(Actor::Property::POSITION, Vector2(-BORDER_WIDTH, -BORDER_WIDTH));
223     mGrabCorner.SetProperty(Actor::Property::OPACITY, 0.6f);
224
225     Layer grabCornerLayer = Layer::New();
226     grabCornerLayer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
227     grabCornerLayer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_RIGHT);
228     grabCornerLayer.Add(mGrabCorner);
229     mDesiredBox.Add(grabCornerLayer);
230
231     mPanGestureDetector = PanGestureDetector::New();
232     mPanGestureDetector.Attach(mGrabCorner);
233     mPanGestureDetector.DetectedSignal().Connect(this, &ImageScalingAndFilteringController::OnPan);
234
235     // Tie-in input event handlers:
236     window.KeyEventSignal().Connect(this, &ImageScalingAndFilteringController::OnKeyEvent);
237
238     CreateControls();
239
240     ResizeImage();
241   }
242
243   /**
244    * Create the GUI controls which float above the scene
245    */
246   void CreateControls()
247   {
248     Window  window     = mApplication.GetWindow();
249     Vector2 windowSize = window.GetSize();
250
251     Dali::Layer controlsLayer = Dali::Layer::New();
252     controlsLayer.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
253     controlsLayer.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(1.0f, 1.0f, 1.0f));
254     controlsLayer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
255     controlsLayer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
256     window.Add(controlsLayer);
257
258     // Back and next image buttons in corners of window:
259     unsigned int       playWidth     = std::min(windowSize.x * (1 / 5.0f), 58.0f);
260     Toolkit::ImageView imagePrevious = Toolkit::ImageView::New(DALI_ICON_PLAY, ImageDimensions(playWidth, playWidth));
261
262     // Last image button:
263     imagePrevious.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
264     imagePrevious.RotateBy(Radian(3.14159265358979323846f), Vector3(0, 1.0f, 0));
265     imagePrevious.SetProperty(Actor::Property::POSITION_Y, playWidth * 0.5f);
266     imagePrevious.SetProperty(Actor::Property::POSITION_X, playWidth + playWidth * 0.5f);
267     imagePrevious.SetProperty(Actor::Property::OPACITY, 0.6f);
268     controlsLayer.Add(imagePrevious);
269     imagePrevious.SetProperty(Dali::Actor::Property::NAME, PREVIOUS_BUTTON_ID);
270     imagePrevious.TouchedSignal().Connect(this, &ImageScalingAndFilteringController::OnControlTouched);
271
272     // Next image button:
273     Toolkit::ImageView imageNext = Toolkit::ImageView::New(DALI_ICON_PLAY, ImageDimensions(playWidth, playWidth));
274     imageNext.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT);
275     imageNext.SetProperty(Actor::Property::POSITION_Y, playWidth * 0.5f);
276     imageNext.SetProperty(Actor::Property::POSITION_X, windowSize.x - playWidth * 0.5f);
277     imageNext.SetProperty(Actor::Property::OPACITY, 0.6f);
278     controlsLayer.Add(imageNext);
279     imageNext.SetProperty(Dali::Actor::Property::NAME, NEXT_BUTTON_ID);
280     imageNext.TouchedSignal().Connect(this, &ImageScalingAndFilteringController::OnControlTouched);
281
282     // Buttons to popup selectors for fitting and sampling modes:
283
284     // Wrapper table to hold two buttons side by side:
285     Toolkit::TableView modesGroupBackground = Toolkit::TableView::New(1, 2);
286     modesGroupBackground.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
287     modesGroupBackground.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
288     modesGroupBackground.SetBackgroundColor(BACKGROUND_COLOUR);
289     modesGroupBackground.SetCellPadding(Size(MARGIN_SIZE * 0.5f, MARGIN_SIZE));
290     modesGroupBackground.SetFitHeight(0);
291
292     modesGroupBackground.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
293     modesGroupBackground.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT);
294     modesGroupBackground.SetProperty(Actor::Property::POSITION, Vector2(0.0f, 0.0f));
295
296     controlsLayer.Add(modesGroupBackground);
297
298     {
299       // Vertical table to hold label and button:
300       Toolkit::TableView fittingModeGroup = Toolkit::TableView::New(2, 1);
301       fittingModeGroup.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
302       fittingModeGroup.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
303       fittingModeGroup.SetBackgroundColor(BACKGROUND_COLOUR);
304       fittingModeGroup.SetCellPadding(Size(MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f));
305       fittingModeGroup.SetFitHeight(0);
306       fittingModeGroup.SetFitHeight(1);
307
308       TextLabel label = TextLabel::New("Image fitting mode:");
309       label.SetStyleName(STYLE_LABEL_TEXT);
310       fittingModeGroup.Add(label);
311
312       Toolkit::PushButton button = CreateButton(FITTING_BUTTON_ID, StringFromScalingMode(mFittingMode));
313       fittingModeGroup.Add(button);
314       mFittingModeButton = button;
315
316       modesGroupBackground.Add(fittingModeGroup);
317     }
318
319     {
320       // Vertical table to hold label and button:
321       Toolkit::TableView samplingModeGroup = Toolkit::TableView::New(2, 1);
322       samplingModeGroup.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
323       samplingModeGroup.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
324       samplingModeGroup.SetBackgroundColor(BACKGROUND_COLOUR);
325       samplingModeGroup.SetCellPadding(Size(MARGIN_SIZE * 0.5f, MARGIN_SIZE * 0.5f));
326       samplingModeGroup.SetFitHeight(0);
327       samplingModeGroup.SetFitHeight(1);
328
329       TextLabel label = TextLabel::New("Image sampling mode:");
330       label.SetStyleName(STYLE_LABEL_TEXT);
331       samplingModeGroup.Add(label);
332
333       Toolkit::PushButton button = CreateButton(SAMPLING_BUTTON_ID, StringFromFilterMode(mSamplingMode));
334       samplingModeGroup.Add(button);
335       mSamplingModeButton = button;
336
337       modesGroupBackground.Add(samplingModeGroup);
338     }
339   }
340
341   Toolkit::PushButton CreateButton(const char* id, const char* label)
342   {
343     Toolkit::PushButton button = Toolkit::PushButton::New();
344     button.SetStyleName(STYLE_BUTTON_TEXT);
345     button.SetProperty(Dali::Actor::Property::NAME, id);
346     button.SetProperty(Toolkit::Button::Property::LABEL, label);
347     button.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
348     button.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
349     button.ClickedSignal().Connect(this, &ImageScalingAndFilteringController::OnButtonClicked);
350     return button;
351   }
352
353   Toolkit::Popup CreatePopup()
354   {
355     Window      window         = mApplication.GetWindow();
356     const float POPUP_WIDTH_DP = window.GetSize().GetWidth() * 0.75f;
357
358     Toolkit::Popup popup = Toolkit::Popup::New();
359     popup.SetProperty(Dali::Actor::Property::NAME, "POPUP");
360     popup.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
361     popup.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
362     popup.SetProperty(Actor::Property::SIZE, Vector2(POPUP_WIDTH_DP, 0.0f));
363
364     popup.OutsideTouchedSignal().Connect(this, &ImageScalingAndFilteringController::OnPopupOutsideTouched);
365
366     return popup;
367   }
368
369   Toolkit::PushButton CreatePopupButton(Actor parent, const char* id)
370   {
371     Toolkit::PushButton button = Toolkit::PushButton::New();
372     button.SetProperty(Dali::Actor::Property::NAME, id);
373     button.SetProperty(Toolkit::Button::Property::LABEL, id);
374
375     button.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
376     button.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_LEFT);
377     button.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
378     button.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
379
380     button.ClickedSignal().Connect(this, &ImageScalingAndFilteringController::OnButtonClicked);
381
382     parent.Add(button);
383     return button;
384   }
385
386   bool OnButtonClicked(Toolkit::Button button)
387   {
388     if(button.GetProperty<std::string>(Dali::Actor::Property::NAME) == FITTING_BUTTON_ID)
389     {
390       mPopup = CreatePopup();
391
392       // Four-row table to hold buttons:
393       Toolkit::TableView fittingModes = Toolkit::TableView::New(4, 1);
394       fittingModes.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
395       fittingModes.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
396       fittingModes.SetCellPadding(Size(MARGIN_SIZE, MARGIN_SIZE * 0.5));
397       fittingModes.SetFitHeight(0);
398       fittingModes.SetFitHeight(1);
399       fittingModes.SetFitHeight(2);
400       fittingModes.SetFitHeight(3);
401
402       CreatePopupButton(fittingModes, StringFromScalingMode(FittingMode::SCALE_TO_FILL));
403       CreatePopupButton(fittingModes, StringFromScalingMode(FittingMode::SHRINK_TO_FIT));
404       CreatePopupButton(fittingModes, StringFromScalingMode(FittingMode::FIT_WIDTH));
405       CreatePopupButton(fittingModes, StringFromScalingMode(FittingMode::FIT_HEIGHT));
406
407       mPopup.SetContent(fittingModes);
408       mApplication.GetWindow().Add(mPopup);
409       mPopup.SetDisplayState(Toolkit::Popup::SHOWN);
410     }
411     else if(button.GetProperty<std::string>(Dali::Actor::Property::NAME) == SAMPLING_BUTTON_ID)
412     {
413       mPopup = CreatePopup();
414
415       // Table to hold buttons for each sampling mode:
416       Toolkit::TableView samplingModes = Toolkit::TableView::New(6, 1);
417       samplingModes.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
418       samplingModes.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT);
419       samplingModes.SetCellPadding(Size(MARGIN_SIZE, MARGIN_SIZE * 0.5));
420       samplingModes.SetFitHeight(0);
421       samplingModes.SetFitHeight(1);
422       samplingModes.SetFitHeight(2);
423       samplingModes.SetFitHeight(3);
424       samplingModes.SetFitHeight(4);
425       samplingModes.SetFitHeight(5);
426
427       CreatePopupButton(samplingModes, StringFromFilterMode(SamplingMode::NEAREST));
428       CreatePopupButton(samplingModes, StringFromFilterMode(SamplingMode::LINEAR));
429       CreatePopupButton(samplingModes, StringFromFilterMode(SamplingMode::BOX));
430       CreatePopupButton(samplingModes, StringFromFilterMode(SamplingMode::BOX_THEN_NEAREST));
431       CreatePopupButton(samplingModes, StringFromFilterMode(SamplingMode::BOX_THEN_LINEAR));
432       CreatePopupButton(samplingModes, StringFromFilterMode(SamplingMode::NO_FILTER));
433
434       mPopup.SetContent(samplingModes);
435       mApplication.GetWindow().Add(mPopup);
436       mPopup.SetDisplayState(Toolkit::Popup::SHOWN);
437     }
438     else if(CheckFittingModeButton(button, FittingMode::SCALE_TO_FILL) ||
439             CheckFittingModeButton(button, FittingMode::SHRINK_TO_FIT) ||
440             CheckFittingModeButton(button, FittingMode::FIT_WIDTH) ||
441             CheckFittingModeButton(button, FittingMode::FIT_HEIGHT))
442     {
443     }
444     else if(CheckSamplingModeButton(button, SamplingMode::NEAREST) ||
445             CheckSamplingModeButton(button, SamplingMode::LINEAR) ||
446             CheckSamplingModeButton(button, SamplingMode::BOX) ||
447             CheckSamplingModeButton(button, SamplingMode::LINEAR) ||
448             CheckSamplingModeButton(button, SamplingMode::BOX_THEN_NEAREST) ||
449             CheckSamplingModeButton(button, SamplingMode::BOX_THEN_LINEAR) ||
450             CheckSamplingModeButton(button, SamplingMode::NO_FILTER))
451     {
452     }
453     return true;
454   }
455
456   bool CheckFittingModeButton(Actor& button, FittingMode::Type mode)
457   {
458     const char* const modeName = StringFromScalingMode(mode);
459     if(button.GetProperty<std::string>(Dali::Actor::Property::NAME) == modeName)
460     {
461       mFittingMode = mode;
462       mFittingModeButton.SetProperty(Toolkit::Button::Property::LABEL, modeName);
463       ResizeImage();
464       mPopup.SetDisplayState(Toolkit::Popup::HIDDEN);
465       mPopup.Reset();
466       return true;
467     }
468     return false;
469   }
470
471   bool CheckSamplingModeButton(Actor& button, SamplingMode::Type mode)
472   {
473     const char* const modeName = StringFromFilterMode(mode);
474     if(button.GetProperty<std::string>(Dali::Actor::Property::NAME) == modeName)
475     {
476       mSamplingMode = mode;
477       mSamplingModeButton.SetProperty(Toolkit::Button::Property::LABEL, modeName);
478       ResizeImage();
479       mPopup.SetDisplayState(Toolkit::Popup::HIDDEN);
480       mPopup.Reset();
481       return true;
482     }
483     return false;
484   }
485
486   void OnPopupOutsideTouched()
487   {
488     if(mPopup)
489     {
490       mPopup.SetDisplayState(Toolkit::Popup::HIDDEN);
491       mPopup.Reset();
492     }
493   }
494
495   bool OnControlTouched(Actor actor, const TouchEvent& event)
496   {
497     if(event.GetPointCount() > 0)
498     {
499       switch(event.GetState(0))
500       {
501         case PointState::UP:
502         {
503           const std::string& name = actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
504           if(name == NEXT_BUTTON_ID)
505           {
506             mCurrentPath = mCurrentPath + 1;
507             mCurrentPath = mCurrentPath < NUM_IMAGE_PATHS ? mCurrentPath : 0;
508             ResizeImage();
509           }
510           else if(name == PREVIOUS_BUTTON_ID)
511           {
512             mCurrentPath = mCurrentPath - 1;
513             mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
514             ResizeImage();
515           }
516           break;
517         }
518         default:
519         {
520           break;
521         }
522       } // end switch
523     }
524
525     return false;
526   }
527
528   void OnPinch(Actor actor, const PinchGesture& pinch)
529   {
530     if(pinch.GetState() == GestureState::STARTED)
531     {
532       mLastPinchScale = pinch.GetScale();
533     }
534     const float scale = pinch.GetScale();
535
536     if(!Equals(scale, mLastPinchScale))
537     {
538       if(scale < mLastPinchScale)
539       {
540         mImageWindowScale.x = std::max(0.05f, mImageWindowScale.x * 0.9f);
541         mImageWindowScale.y = std::max(0.05f, mImageWindowScale.y * 0.9f);
542       }
543       else
544       {
545         mImageWindowScale.x = std::max(0.05f, std::min(1.0f, mImageWindowScale.x * 1.1f));
546         mImageWindowScale.y = std::max(0.05f, std::min(1.0f, mImageWindowScale.y * 1.1f));
547       }
548       ResizeImage();
549     }
550     mLastPinchScale = scale;
551   }
552
553   void OnPan(Actor actor, const PanGesture& gesture)
554   {
555     Window         window       = mApplication.GetWindow();
556     Vector2        windowSize   = window.GetSize();
557     const Vector2& displacement = gesture.GetDisplacement();
558
559     // 1.0f and 0.75f are the maximum size caps of the resized image, as a factor of window-size.
560     mImageWindowScale.x = std::max(0.05f, std::min(0.95f, mImageWindowScale.x + (displacement.x * 2.0f / windowSize.width)));
561     mImageWindowScale.y = std::max(0.05f, std::min(0.70f, mImageWindowScale.y + (displacement.y * 2.0f / windowSize.height)));
562
563     ResizeImage();
564   }
565
566   void OnKeyEvent(const KeyEvent& event)
567   {
568     if(event.GetState() == KeyEvent::DOWN)
569     {
570       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
571       {
572         if(mPopup && mPopup.GetCurrentProperty<bool>(Actor::Property::VISIBLE))
573         {
574           mPopup.SetDisplayState(Toolkit::Popup::HIDDEN);
575           mPopup.Reset();
576         }
577         else
578         {
579           mApplication.Quit();
580         }
581       }
582       else if(event.GetKeyName() == "Right")
583       {
584         mImageWindowScale.x = std::max(0.05f, std::min(1.0f, mImageWindowScale.x * 1.1f));
585       }
586       else if(event.GetKeyName() == "Left")
587       {
588         mImageWindowScale.x = std::max(0.05f, mImageWindowScale.x * 0.9f);
589       }
590       else if(event.GetKeyName() == "Up")
591       {
592         mImageWindowScale.y = std::max(0.05f, std::min(1.0f, mImageWindowScale.y * 1.1f));
593       }
594       else if(event.GetKeyName() == "Down")
595       {
596         mImageWindowScale.y = std::max(0.05f, mImageWindowScale.y * 0.9f);
597       }
598       else if(event.GetKeyName() == "o")
599       {
600         mImageWindowScale.x = std::max(0.05f, mImageWindowScale.x * 0.9f);
601         mImageWindowScale.y = std::max(0.05f, mImageWindowScale.y * 0.9f);
602       }
603       else if(event.GetKeyName() == "p")
604       {
605         mImageWindowScale.x = std::max(0.05f, std::min(1.0f, mImageWindowScale.x * 1.1f));
606         mImageWindowScale.y = std::max(0.05f, std::min(1.0f, mImageWindowScale.y * 1.1f));
607       }
608       else if(event.GetKeyName() == "n")
609       {
610         mCurrentPath = mCurrentPath + 1;
611         mCurrentPath = mCurrentPath < NUM_IMAGE_PATHS ? mCurrentPath : 0;
612       }
613       else if(event.GetKeyName() == "b")
614       {
615         mCurrentPath = mCurrentPath - 1;
616         mCurrentPath = mCurrentPath >= 0 ? mCurrentPath : NUM_IMAGE_PATHS - 1;
617       }
618       // Cycle filter and scaling modes:
619       else if(event.GetKeyName() == "f")
620       {
621         mSamplingMode = NextFilterMode(mSamplingMode);
622         mSamplingModeButton.SetProperty(Toolkit::Button::Property::LABEL, StringFromFilterMode(mSamplingMode));
623       }
624       // Cycle filter and scaling modes:
625       else if(event.GetKeyName() == "s")
626       {
627         mFittingMode = NextScalingMode(mFittingMode);
628         mFittingModeButton.SetProperty(Toolkit::Button::Property::LABEL, StringFromScalingMode(mFittingMode));
629       }
630       else
631       {
632         return;
633       }
634
635       ResizeImage();
636     }
637   }
638
639 private:
640   void LoadImage()
641   {
642     mImageLoading = true;
643
644     const char* const path      = IMAGE_PATHS[mCurrentPath];
645     Window            window    = mApplication.GetWindow();
646     Size              imageSize = Vector2(window.GetSize()) * mImageWindowScale;
647     mImageView.SetProperty(Actor::Property::SIZE, imageSize);
648
649     Property::Map map;
650     map[Toolkit::ImageVisual::Property::URL]            = path;
651     map[Toolkit::ImageVisual::Property::DESIRED_WIDTH]  = imageSize.x;
652     map[Toolkit::ImageVisual::Property::DESIRED_HEIGHT] = imageSize.y;
653     map[Toolkit::ImageVisual::Property::FITTING_MODE]   = mFittingMode;
654     map[Toolkit::ImageVisual::Property::SAMPLING_MODE]  = mSamplingMode;
655
656     mImageView.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
657   }
658
659   void ResizeImage()
660   {
661     Window  window     = mApplication.GetWindow();
662     Vector2 windowSize = window.GetSize();
663     Size    imageSize  = windowSize * mImageWindowScale;
664
665     LoadImage();
666
667     // Border size needs to be modified to take into account the width of the frame.
668     Vector2 borderScale((imageSize + Vector2(BORDER_WIDTH * 2.0f, BORDER_WIDTH * 2.0f)) / windowSize);
669     mDesiredBox.SetProperty(Actor::Property::SIZE, windowSize * borderScale);
670   }
671
672 private:
673   Application&         mApplication;
674   Toolkit::ImageView   mDesiredBox; //< Background rectangle to show requested image size.
675   Toolkit::PushButton  mFittingModeButton;
676   Toolkit::PushButton  mSamplingModeButton;
677   Toolkit::Popup       mPopup;
678   PinchGestureDetector mPinchDetector;
679   float                mLastPinchScale;
680   Toolkit::ImageView   mGrabCorner;
681   PanGestureDetector   mPanGestureDetector;
682   Toolkit::ImageView   mImageView;
683   Vector2              mImageWindowScale;
684   int                  mCurrentPath;
685   FittingMode::Type    mFittingMode;
686   SamplingMode::Type   mSamplingMode;
687   bool                 mImageLoading;
688   bool                 mQueuedImageLoad;
689 };
690
691 int DALI_EXPORT_API main(int argc, char** argv)
692 {
693   Application                        application = Application::New(&argc, &argv, DEMO_THEME_PATH);
694   ImageScalingAndFilteringController test(application);
695   application.MainLoop();
696   return 0;
697 }