X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fimage-view%2Fimage-view-impl.cpp;h=7f11261cf5b4c76d6eb4e966bb1fc9191568e0d2;hb=HEAD;hp=e3701ec6debe1d6a65a6c035fe16c797050a71b2;hpb=8e2fd34769d0e9657a0e13776b405c5229775bc1;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/controls/image-view/image-view-impl.cpp b/dali-toolkit/internal/controls/image-view/image-view-impl.cpp index e3701ec..91e33df 100644 --- a/dali-toolkit/internal/controls/image-view/image-view-impl.cpp +++ b/dali-toolkit/internal/controls/image-view/image-view-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,15 @@ // EXTERNAL INCLUDES #include +#include #include #include // INTERNAL INCLUDES +#include #include #include +#include #include #include #include @@ -41,7 +44,13 @@ namespace Internal { namespace { -const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); +constexpr float FULL_OPACITY = 1.0f; +constexpr float LOW_OPACITY = 0.2f; +constexpr float TRANSITION_EFFECT_SPEED = 0.3f; + +constexpr int PLACEHOLDER_DEPTH_INDEX = -2; +constexpr int PREVIOUS_VISUAL_DEPTH_INDEX = -1; +constexpr int CURRENT_VISUAL_DEPTH_INDEX = 0; BaseHandle Create() { @@ -52,7 +61,9 @@ BaseHandle Create() DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ImageView, Toolkit::Control, Create); DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "image", MAP, IMAGE) DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "preMultipliedAlpha", BOOLEAN, PRE_MULTIPLIED_ALPHA) - +DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "placeholderImage", STRING, PLACEHOLDER_IMAGE) +DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "enableTransitionEffect", BOOLEAN, ENABLE_TRANSITION_EFFECT) +DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "transitionEffectOption", MAP, TRANSITION_EFFECT_OPTION) DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, ImageView, "pixelArea", Vector4(0.f, 0.f, 1.f, 1.f), PIXEL_AREA) DALI_TYPE_REGISTRATION_END() @@ -60,11 +71,12 @@ DALI_TYPE_REGISTRATION_END() using namespace Dali; -ImageView::ImageView() -: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)), +ImageView::ImageView(ControlBehaviour additionalBehaviour) +: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT | additionalBehaviour)), mImageSize(), - mImageVisualPaddingSetByTransform(false), - mImageViewPixelAreaSetByFittingMode(false) + mTransitionTargetAlpha(FULL_OPACITY), + mTransitionEffect(false), + mImageReplaced(false) { } @@ -72,9 +84,9 @@ ImageView::~ImageView() { } -Toolkit::ImageView ImageView::New() +Toolkit::ImageView ImageView::New(ControlBehaviour additionalBehaviour) { - ImageView* impl = new ImageView(); + ImageView* impl = new ImageView(additionalBehaviour); Toolkit::ImageView handle = Toolkit::ImageView(*impl); @@ -93,21 +105,65 @@ void ImageView::OnInitialize() Dali::Toolkit::Control handle(GetOwner()); handle.ResourceReadySignal().Connect(this, &ImageView::OnResourceReady); - DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) { - return std::unique_ptr( - new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::IMAGE)); - }); + Self().SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::IMAGE); } void ImageView::SetImage(const Property::Map& map) { + if(mTransitionEffect && mVisual) + { + // Clear previous transition effect if it is playing + if(mPreviousVisual) + { + if(mTransitionAnimation) + { + if(mTransitionAnimation.GetState() == Animation::PLAYING) + { + // Hide placeholder + HidePlaceholderImage(); + ClearTransitionAnimation(); + } + } + } + + // Enable transition effect for previous visual. + // This previous visual will be deleted when transition effect is done. + Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this); + controlDataImpl.EnableReadyTransitionOverriden(mVisual, true); + mPreviousVisual = mVisual; + } + // Comparing a property map is too expensive so just creating a new visual mPropertyMap = map; mUrl.clear(); + mImageReplaced = true; + + // keep alpha for transition effect + if(mTransitionEffect) + { + float alpha = FULL_OPACITY; + Property::Value* alphaValue = map.Find(Toolkit::Visual::Property::OPACITY); + if(alphaValue && alphaValue->Get(alpha)) + { + mTransitionTargetAlpha = alpha; + } + } + + if(!mVisual) + { + ShowPlaceholderImage(); + } + Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(mPropertyMap); if(visual) { + Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual); + if(visualImpl.GetFittingMode() == Visual::FittingMode::DONT_CARE) + { + visualImpl.SetFittingMode(Visual::FittingMode::FILL); + } + // Don't set mVisual until it is ready and shown. Getters will still use current visual. if(!mVisual) { @@ -116,11 +172,10 @@ void ImageView::SetImage(const Property::Map& map) if(!mShaderMap.Empty()) { - Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual); visualImpl.SetCustomShader(mShaderMap); } - DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::IMAGE, visual); + DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::IMAGE, visual, DepthIndex::CONTENT); } else { @@ -130,21 +185,55 @@ void ImageView::SetImage(const Property::Map& map) // Trigger a size negotiation request that may be needed when unregistering a visual. RelayoutRequest(); } - // Signal that a Relayout may be needed } void ImageView::SetImage(const std::string& url, ImageDimensions size) { + if(mTransitionEffect && mVisual) + { + // Clear previous transition effect if it is playing + if(mPreviousVisual) + { + if(mTransitionAnimation) + { + if(mTransitionAnimation.GetState() == Animation::PLAYING) + { + HidePlaceholderImage(); + ClearTransitionAnimation(); + } + } + } + + // Enable transition effect for previous visual. + // This previous visual will be deleted when transition effect is done. + Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this); + controlDataImpl.EnableReadyTransitionOverriden(mVisual, true); + mPreviousVisual = mVisual; + } + // Don't bother comparing if we had a visual previously, just drop old visual and create new one mUrl = url; mImageSize = size; mPropertyMap.Clear(); + mImageReplaced = true; + + if(!mVisual) + { + ShowPlaceholderImage(); + } + // Don't set mVisual until it is ready and shown. Getters will still use current visual. Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(url, size); if(visual) { + Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual); + if(visualImpl.GetFittingMode() == Visual::FittingMode::DONT_CARE) + { + visualImpl.SetFittingMode(Visual::FittingMode::FILL); + } + if(!mVisual) { mVisual = visual; @@ -152,11 +241,10 @@ void ImageView::SetImage(const std::string& url, ImageDimensions size) if(!mShaderMap.Empty()) { - Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual); visualImpl.SetCustomShader(mShaderMap); } - DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::IMAGE, visual); + DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::IMAGE, visual, DepthIndex::CONTENT); } else { @@ -166,10 +254,23 @@ void ImageView::SetImage(const std::string& url, ImageDimensions size) // Trigger a size negotiation request that may be needed when unregistering a visual. RelayoutRequest(); } - // Signal that a Relayout may be needed } +void ImageView::ClearImageVisual() +{ + // Clear cached properties + mPropertyMap.Clear(); + mUrl.clear(); + mVisual.Reset(); + + // Unregister the exsiting visual + DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::IMAGE); + + // Trigger a size negotiation request that may be needed when unregistering a visual. + RelayoutRequest(); +} + void ImageView::EnablePreMultipliedAlpha(bool preMultipled) { if(mVisual) @@ -195,6 +296,48 @@ void ImageView::SetDepthIndex(int depthIndex) } } +void ImageView::SetPlaceholderUrl(const std::string& url) +{ + mPlaceholderUrl = url; + if(!url.empty()) + { + mPlaceholderVisual.Reset(); + CreatePlaceholderImage(); + } + else + { + // Clear current placeholder image + Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE); + if(visual) + { + DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE); + } + + mPlaceholderVisual.Reset(); + mPlaceholderUrl = url; + } +} + +std::string ImageView::GetPlaceholderUrl() const +{ + return mPlaceholderUrl; +} + +void ImageView::EnableTransitionEffect(bool effectEnable) +{ + mTransitionEffect = effectEnable; +} + +bool ImageView::IsTransitionEffectEnabled() const +{ + return mTransitionEffect; +} + +void ImageView::SetTransitionEffectOption(const Property::Map& map) +{ + mTransitionEffectOptionMap = map; +} + Vector3 ImageView::GetNaturalSize() { if(mVisual) @@ -249,30 +392,6 @@ void ImageView::OnRelayout(const Vector2& size, RelayoutContainer& container) Control::OnRelayout(size, container); if(mVisual) { - Property::Map transformMap = Property::Map(); - - Extents padding = Self().GetProperty(Toolkit::Control::Property::PADDING); - - bool zeroPadding = (padding == Extents()); - - Vector2 naturalSize; - mVisual.GetNaturalSize(naturalSize); - - Dali::LayoutDirection::Type layoutDirection = static_cast( - Self().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get()); - if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection) - { - std::swap(padding.start, padding.end); - } - - // remove padding from the size to know how much is left for the visual - Vector2 finalSize = size - Vector2(padding.start + padding.end, padding.top + padding.bottom); - Vector2 finalOffset = Vector2(padding.start, padding.top); - - ApplyFittingMode(finalSize, naturalSize, finalOffset, zeroPadding, transformMap); - - mVisual.SetTransformAndSize(transformMap, size); - // mVisual is not updated util the resource is ready in the case of visual replacement. // in this case, the Property Map must be initialized so that the previous value is not reused. // after mVisual is updated, the correct value will be reset. @@ -281,250 +400,212 @@ void ImageView::OnRelayout(const Vector2& size, RelayoutContainer& container) { visual.SetTransformAndSize(Property::Map(), size); } + + if(!mTransitionEffect) + { + // we don't need placeholder anymore because visual is replaced. so hide placeholder. + HidePlaceholderImage(); + } } } -void ImageView::OnCreateTransitions(Dali::Animation& animation, Dali::Toolkit::Control source, AlphaFunction alphaFunction, TimePeriod timePeriod) +void ImageView::OnCreateTransitions(std::vector>& sourceProperties, + std::vector>& destinationProperties, + Dali::Toolkit::Control source, + Dali::Toolkit::Control destination) { - Dali::Toolkit::ImageView destinationHandle = Toolkit::ImageView(GetOwner()); - Toolkit::Visual::Base destinationVisual = DevelControl::GetVisual(GetImplementation(destinationHandle), Toolkit::ImageView::Property::IMAGE); - Property::Map destinationMap; - - if(!destinationVisual) + // Retrieves image properties to be transitioned. + Dali::Property::Map imageSourcePropertyMap, imageDestinationPropertyMap; + MakeVisualTransition(imageSourcePropertyMap, imageDestinationPropertyMap, source, destination, Toolkit::ImageView::Property::IMAGE); + if(imageSourcePropertyMap.Count() > 0) { - return; + sourceProperties.push_back(std::pair(Toolkit::ImageView::Property::IMAGE, imageSourcePropertyMap)); + destinationProperties.push_back(std::pair(Toolkit::ImageView::Property::IMAGE, imageDestinationPropertyMap)); } +} - destinationVisual.CreatePropertyMap(destinationMap); - - static auto findValueVector4 = [](const Property::Map& map, Property::Index index, const Vector4& defaultValue = Vector4()) -> Vector4 { - Property::Value* propertyValue = map.Find(index); - if(propertyValue) - { - return propertyValue->Get(); - } - return defaultValue; - }; +void ImageView::OnUpdateVisualProperties(const std::vector>& properties) +{ + Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE); + if(visual) + { + Dali::Toolkit::Control handle(GetOwner()); - static auto findValueFloat = [](const Property::Map& map, Property::Index index, const float& defaultValue = 0.0f) -> float { - Property::Value* propertyValue = map.Find(index); - if(propertyValue) + for(auto&& data : properties) { - return propertyValue->Get(); + if(data.first == Toolkit::ImageView::Property::IMAGE) + { + DevelControl::DoAction(handle, Toolkit::ImageView::Property::IMAGE, DevelVisual::Action::UPDATE_PROPERTY, data.second); + break; + } } - return defaultValue; - }; - - Vector4 sourceMixColor(0.0f, 0.0f, 0.0f, 0.0f); - Vector4 sourceCornerRadius(0.0f, 0.0f, 0.0f, 0.0f); - float sourceBorderlineWidth(0.0f); - Vector4 sourceBorderlineColor(0.0f, 0.0f, 0.0f, 1.0f); - float sourceBorderlineOffset(0.0f); - Vector4 destinationMixColor = findValueVector4(destinationMap, Dali::Toolkit::Visual::Property::MIX_COLOR, sourceMixColor); - Vector4 destinationCornerRadius = findValueVector4(destinationMap, Toolkit::DevelVisual::Property::CORNER_RADIUS, sourceCornerRadius); - float destinationBorderlineWidth = findValueFloat(destinationMap, Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, sourceBorderlineWidth); - Vector4 destinationBorderlineColor = findValueVector4(destinationMap, Toolkit::DevelVisual::Property::BORDERLINE_COLOR, sourceBorderlineColor); - float destinationBorderlineOffset = findValueFloat(destinationMap, Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, sourceBorderlineOffset); - - Dali::Toolkit::ImageView sourceHandle = Dali::Toolkit::ImageView::DownCast(source); - Toolkit::Visual::Base sourceVisual; - Property::Map sourceMap; + } +} - if(sourceHandle) +void ImageView::OnResourceReady(Toolkit::Control control) +{ + // In case of placeholder, we need to skip this call. + // TODO: In case of placeholder, it needs to be modified not to call OnResourceReady() + if(control.GetVisualResourceStatus(Toolkit::ImageView::Property::IMAGE) != Toolkit::Visual::ResourceStatus::READY) { - sourceVisual = DevelControl::GetVisual(GetImplementation(sourceHandle), Toolkit::ImageView::Property::IMAGE); + return; } - if(sourceVisual) + // Do transition effect if need. + if(mTransitionEffect) { - sourceVisual.CreatePropertyMap(sourceMap); - sourceMixColor = findValueVector4(sourceMap, Dali::Toolkit::Visual::Property::MIX_COLOR, sourceMixColor); - sourceCornerRadius = findValueVector4(sourceMap, Toolkit::DevelVisual::Property::CORNER_RADIUS, sourceCornerRadius); - sourceBorderlineWidth = findValueFloat(sourceMap, Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, sourceBorderlineWidth); - sourceBorderlineColor = findValueVector4(sourceMap, Toolkit::DevelVisual::Property::BORDERLINE_COLOR, sourceBorderlineColor); - sourceBorderlineOffset = findValueFloat(sourceMap, Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, sourceBorderlineOffset); + // TODO: Consider about placeholder image is loaded failed + Toolkit::Visual::Base placeholderVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE); + if(!placeholderVisual || control.GetVisualResourceStatus(Toolkit::ImageView::Property::PLACEHOLDER_IMAGE) == Toolkit::Visual::ResourceStatus::READY) + { + // when placeholder is disabled or ready placeholder and image, we need to transition effect + TransitionImageWithEffect(); + } } - std::vector properties; - std::vector> values; + // Visual ready so update visual attached to this ImageView, following call to RelayoutRequest will use this visual. + mVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE); - if(Vector3(sourceMixColor) != Vector3(destinationMixColor)) - { - properties.push_back(DevelControl::GetVisualProperty(destinationHandle, Toolkit::ImageView::Property::IMAGE, Toolkit::Visual::Property::MIX_COLOR)); - values.push_back(std::make_pair(Vector3(sourceMixColor), Vector3(destinationMixColor))); - } - if(std::abs(sourceMixColor.a - destinationMixColor.a) > Math::MACHINE_EPSILON_1) - { - properties.push_back(DevelControl::GetVisualProperty(destinationHandle, Toolkit::ImageView::Property::IMAGE, Toolkit::Visual::Property::OPACITY)); - values.push_back(std::make_pair(sourceMixColor.a, destinationMixColor.a)); - } - if(sourceCornerRadius != destinationCornerRadius) - { - properties.push_back(DevelControl::GetVisualProperty(destinationHandle, Toolkit::ImageView::Property::IMAGE, Toolkit::DevelVisual::Property::CORNER_RADIUS)); - values.push_back(std::make_pair(sourceCornerRadius, destinationCornerRadius)); - } - if(sourceBorderlineWidth != destinationBorderlineWidth) - { - properties.push_back(DevelControl::GetVisualProperty(destinationHandle, Toolkit::ImageView::Property::IMAGE, Toolkit::DevelVisual::Property::BORDERLINE_WIDTH)); - values.push_back(std::make_pair(sourceBorderlineWidth, destinationBorderlineWidth)); - } - if(sourceBorderlineColor != destinationBorderlineColor) + // Signal that a Relayout may be needed +} + +void ImageView::CreatePlaceholderImage() +{ + Property::Map propertyMap; + propertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE); + propertyMap.Insert(Toolkit::ImageVisual::Property::URL, mPlaceholderUrl); + //propertyMap.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, Toolkit::ImageVisual::LoadPolicy::IMMEDIATE); // TODO: need to enable this property + propertyMap.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, Toolkit::ImageVisual::ReleasePolicy::DESTROYED); + propertyMap.Insert(Toolkit::DevelImageVisual::Property::ENABLE_BROKEN_IMAGE, false); + mPlaceholderVisual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap); + if(mPlaceholderVisual) { - properties.push_back(DevelControl::GetVisualProperty(destinationHandle, Toolkit::ImageView::Property::IMAGE, Toolkit::DevelVisual::Property::BORDERLINE_COLOR)); - values.push_back(std::make_pair(sourceBorderlineColor, destinationBorderlineColor)); + mPlaceholderVisual.SetName("placeholder"); + mPlaceholderVisual.SetDepthIndex(mPlaceholderVisual.GetDepthIndex() + PLACEHOLDER_DEPTH_INDEX); } - if(sourceBorderlineOffset != destinationBorderlineOffset) + else { - properties.push_back(DevelControl::GetVisualProperty(destinationHandle, Toolkit::ImageView::Property::IMAGE, Toolkit::DevelVisual::Property::BORDERLINE_OFFSET)); - values.push_back(std::make_pair(sourceBorderlineOffset, destinationBorderlineOffset)); + DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE); + mPlaceholderVisual.Reset(); } +} - for(uint32_t i = 0; i < properties.size(); ++i) +void ImageView::ShowPlaceholderImage() +{ + if(mPlaceholderVisual) { - if(timePeriod.delaySeconds > 0.0f) - { - Dali::KeyFrames initialKeyframes = Dali::KeyFrames::New(); - initialKeyframes.Add(0.0f, values[i].first); - initialKeyframes.Add(1.0f, values[i].first); - animation.AnimateBetween(properties[i], initialKeyframes, TimePeriod(timePeriod.delaySeconds)); - } - Dali::KeyFrames keyframes = Dali::KeyFrames::New(); - keyframes.Add(0.0f, values[i].first); - keyframes.Add(1.0f, values[i].second); - animation.AnimateBetween(properties[i], keyframes, alphaFunction, timePeriod); + DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE, mPlaceholderVisual, false); + Actor self = Self(); + Toolkit::GetImplementation(mPlaceholderVisual).SetOnScene(self); } } -void ImageView::OnResourceReady(Toolkit::Control control) +void ImageView::HidePlaceholderImage() { - // Visual ready so update visual attached to this ImageView, following call to RelayoutRequest will use this visual. - mVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE); - // Signal that a Relayout may be needed + if(mPlaceholderVisual) + { + DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE); + + // Hide placeholder + Actor self = Self(); + Toolkit::GetImplementation(mPlaceholderVisual).SetOffScene(self); + } } -void ImageView::SetTransformMapForFittingMode(Vector2 finalSize, Vector2 naturalSize, Vector2 finalOffset, Visual::FittingMode fittingMode, Property::Map& transformMap) +void ImageView::TransitionImageWithEffect() { - switch(fittingMode) + Toolkit::ImageView handle = Toolkit::ImageView(GetOwner()); + + if(handle) { - case Visual::FittingMode::FIT_KEEP_ASPECT_RATIO: + if(!mImageReplaced) { - auto availableVisualSize = finalSize; - - // scale to fit the padded area - finalSize = naturalSize * std::min((naturalSize.width ? (availableVisualSize.width / naturalSize.width) : 0), - (naturalSize.height ? (availableVisualSize.height / naturalSize.height) : 0)); - - // calculate final offset within the padded area - finalOffset += (availableVisualSize - finalSize) * .5f; + // If the image is not replaced, the transition effect is not required. + return; + } - // populate the transform map - transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset) - .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize); - break; + if(mTransitionAnimation) + { + ClearTransitionAnimation(); } - case Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO: + + // Control visual's depth for transition effect + if(mPreviousVisual) { - mImageViewPixelAreaSetByFittingMode = true; - auto availableVisualSize = finalSize; - finalSize = naturalSize * std::max((naturalSize.width ? (availableVisualSize.width / naturalSize.width) : 0), - (naturalSize.height ? (availableVisualSize.height / naturalSize.height) : 0)); - - auto originalOffset = finalOffset; - finalOffset += (availableVisualSize - finalSize) * .5f; - - float x = abs((availableVisualSize.width - finalSize.width) / finalSize.width) * .5f; - float y = abs((availableVisualSize.height - finalSize.height) / finalSize.height) * .5f; - float widthRatio = 1.f - abs((availableVisualSize.width - finalSize.width) / finalSize.width); - float heightRatio = 1.f - abs((availableVisualSize.height - finalSize.height) / finalSize.height); - Vector4 pixelArea = Vector4(x, y, widthRatio, heightRatio); - Self().SetProperty(Toolkit::ImageView::Property::PIXEL_AREA, pixelArea); - - // populate the transform map - transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, originalOffset) - .Add(Toolkit::Visual::Transform::Property::SIZE, availableVisualSize); - break; + mPreviousVisual.SetDepthIndex(mPreviousVisual.GetDepthIndex() + PREVIOUS_VISUAL_DEPTH_INDEX); } - case Visual::FittingMode::CENTER: + + // Transition current image + Toolkit::Visual::Base imageVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE); + if(imageVisual) { - auto availableVisualSize = finalSize; - if(availableVisualSize.width > naturalSize.width && availableVisualSize.height > naturalSize.height) + imageVisual.SetDepthIndex(imageVisual.GetDepthIndex() + CURRENT_VISUAL_DEPTH_INDEX); + if(!mTransitionEffectOptionMap.Empty()) { - finalSize = naturalSize; + // Set user's transition effect options + Dali::Toolkit::TransitionData transition = Toolkit::TransitionData::New(mTransitionEffectOptionMap); + Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this); + mTransitionAnimation = controlDataImpl.CreateTransition(transition); + if(mTransitionAnimation) + { + mTransitionAnimation.SetEndAction(Animation::EndAction::DISCARD); + mTransitionAnimation.FinishedSignal().Connect(this, &ImageView::OnTransitionAnimationFinishedCallback); + mTransitionAnimation.Play(); + } + else + { + DALI_LOG_ERROR("Create Transition Animation failed"); + } } else { - finalSize = naturalSize * std::min((naturalSize.width ? (availableVisualSize.width / naturalSize.width) : 0), - (naturalSize.height ? (availableVisualSize.height / naturalSize.height) : 0)); - } + mTransitionAnimation = Animation::New(TRANSITION_EFFECT_SPEED); + mTransitionAnimation.SetEndAction(Animation::EndAction::DISCARD); + float destinationAlpha = (mTransitionTargetAlpha > LOW_OPACITY) ? mTransitionTargetAlpha : LOW_OPACITY; - finalOffset += (availableVisualSize - finalSize) * .5f; + // Transition current image + Toolkit::Visual::Base imageVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE); + if(imageVisual) + { + Dali::KeyFrames fadeinKeyFrames = Dali::KeyFrames::New(); + fadeinKeyFrames.Add(0.0f, LOW_OPACITY); + fadeinKeyFrames.Add(1.0f, destinationAlpha); + mTransitionAnimation.AnimateBetween(DevelControl::GetVisualProperty(handle, Toolkit::ImageView::Property::IMAGE, Toolkit::Visual::Property::OPACITY), fadeinKeyFrames, AlphaFunction::EASE_IN_OUT); + imageVisual.SetDepthIndex(imageVisual.GetDepthIndex() + CURRENT_VISUAL_DEPTH_INDEX); + } - // populate the transform map - transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset) - .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize); - break; - } - case Visual::FittingMode::FILL: - { - transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset) - .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize); - break; - } - case Visual::FittingMode::FIT_WIDTH: - case Visual::FittingMode::FIT_HEIGHT: - { - // This FittingMode already converted - break; + // Play transition animation + mTransitionAnimation.FinishedSignal().Connect(this, &ImageView::OnTransitionAnimationFinishedCallback); + mTransitionAnimation.Play(); + } } } } -void ImageView::ApplyFittingMode(Vector2 finalSize, Vector2 naturalSize, Vector2 finalOffset, bool zeroPadding, Property::Map& transformMap) +void ImageView::ClearTransitionAnimation() { - Visual::FittingMode fittingMode = Toolkit::GetImplementation(mVisual).GetFittingMode(); - - // Reset PIXEL_AREA after using OVER_FIT_KEEP_ASPECT_RATIO - if(mImageViewPixelAreaSetByFittingMode) + // Clear PreviousVisual + if(mPreviousVisual) { - Self().SetProperty(Toolkit::ImageView::Property::PIXEL_AREA, FULL_TEXTURE_RECT); - mImageViewPixelAreaSetByFittingMode = false; + Actor self = Self(); + Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this); + controlDataImpl.EnableReadyTransitionOverriden(mVisual, false); + Toolkit::GetImplementation(mPreviousVisual).SetOffScene(self); + mPreviousVisual.Reset(); } - if((!zeroPadding) || // If padding is not zero - (fittingMode != Visual::FittingMode::FILL)) + if(mTransitionAnimation) { - mImageVisualPaddingSetByTransform = true; - - // If FittingMode use FIT_WIDTH or FIT_HEIGTH, it need to change proper fittingMode - if(fittingMode == Visual::FittingMode::FIT_WIDTH) + if(mTransitionAnimation.GetState() == Animation::PLAYING) { - fittingMode = (finalSize.height / naturalSize.height) < (finalSize.width / naturalSize.width) ? Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::FIT_KEEP_ASPECT_RATIO; + mTransitionAnimation.Stop(); } - else if(fittingMode == Visual::FittingMode::FIT_HEIGHT) - { - fittingMode = (finalSize.height / naturalSize.height) < (finalSize.width / naturalSize.width) ? Visual::FittingMode::FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO; - } - - SetTransformMapForFittingMode(finalSize, naturalSize, finalOffset, fittingMode, transformMap); + mTransitionAnimation.FinishedSignal().Disconnect(this, &ImageView::OnTransitionAnimationFinishedCallback); + mTransitionAnimation.Clear(); + mTransitionAnimation.Reset(); - // Set extra value for applying transformMap - transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, - Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE)) - .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN) - .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN) - .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, - Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE)); - } - else if(mImageVisualPaddingSetByTransform && zeroPadding) // Reset offset to zero only if padding applied previously - { - mImageVisualPaddingSetByTransform = false; - // Reset the transform map - transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, Vector2::ZERO) - .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY, - Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE)) - .Add(Toolkit::Visual::Transform::Property::SIZE, Vector2::ONE) - .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY, - Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE)); + // After transition effect is cleared, we don't need transition effect until image is replaced. + mImageReplaced = false; } } @@ -554,33 +635,46 @@ void ImageView::SetProperty(BaseObject* object, Property::Index index, const Pro else { map = value.GetMap(); - if(map) + if(DALI_LIKELY(map)) { - Property::Value* shaderValue = map->Find(Toolkit::Visual::Property::SHADER, CUSTOM_SHADER); - // set image only if property map contains image information other than custom shader - if(map->Count() > 1u || !shaderValue) + // the property map is emtpy map. Unregister visual. + if(DALI_UNLIKELY(map->Count() == 0u)) { - impl.SetImage(*map); + impl.ClearImageVisual(); } - // the property map contains only the custom shader - else if((map->Count() == 1u) && (shaderValue)) + else { - Property::Map* shaderMap = shaderValue->GetMap(); - if(shaderMap) + Property::Value* shaderValue = map->Find(Toolkit::Visual::Property::SHADER, CUSTOM_SHADER); + // set image only if property map contains image information other than custom shader + if(map->Count() > 1u || !shaderValue) { - impl.mShaderMap = *shaderMap; - - if(!impl.mUrl.empty()) - { - impl.SetImage(impl.mUrl, impl.mImageSize); - } - else if(!impl.mPropertyMap.Empty()) + impl.SetImage(*map); + } + // the property map contains only the custom shader + else if((map->Count() == 1u) && (shaderValue)) + { + Property::Map* shaderMap = shaderValue->GetMap(); + if(shaderMap) { - impl.SetImage(impl.mPropertyMap); + impl.mShaderMap = *shaderMap; + + if(!impl.mUrl.empty()) + { + impl.SetImage(impl.mUrl, impl.mImageSize); + } + else if(!impl.mPropertyMap.Empty()) + { + impl.SetImage(impl.mPropertyMap); + } } } } } + else + { + // invalid property value comes. Unregister visual. + impl.ClearImageVisual(); + } } break; } @@ -594,6 +688,34 @@ void ImageView::SetProperty(BaseObject* object, Property::Index index, const Pro } break; } + + case Toolkit::ImageView::Property::PLACEHOLDER_IMAGE: + { + std::string placeholderUrl; + if(value.Get(placeholderUrl)) + { + impl.SetPlaceholderUrl(placeholderUrl); + } + break; + } + + case Toolkit::ImageView::Property::ENABLE_TRANSITION_EFFECT: + { + bool transitionEffect; + if(value.Get(transitionEffect)) + { + impl.EnableTransitionEffect(transitionEffect); + } + break; + } + case Toolkit::ImageView::Property::TRANSITION_EFFECT_OPTION: + { + Property::Map map; + if(value.Get(map)) + { + impl.SetTransitionEffectOption(map); + } + } } } } @@ -633,12 +755,31 @@ Property::Value ImageView::GetProperty(BaseObject* object, Property::Index prope value = impl.IsPreMultipliedAlphaEnabled(); break; } + + case Toolkit::ImageView::Property::PLACEHOLDER_IMAGE: + { + value = impl.GetPlaceholderUrl(); + break; + } + + case Toolkit::ImageView::Property::ENABLE_TRANSITION_EFFECT: + { + value = impl.IsTransitionEffectEnabled(); + break; + } } } return value; } +void ImageView::OnTransitionAnimationFinishedCallback(Animation& animation) +{ + // Hide placeholder + HidePlaceholderImage(); + ClearTransitionAnimation(); +} + } // namespace Internal } // namespace Toolkit } // namespace Dali