1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/image/image_layout_algorithm.h"
17 
18 #ifdef FLUTTER_2_5
19 #include "ace_shell/shell/common/window_manager.h"
20 #endif
21 #include "core/common/container.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/pattern/image/image_layout_property.h"
24 #include "core/components_ng/pattern/image/image_pattern.h"
25 #include "core/components_ng/pattern/image/image_render_property.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
29 namespace {
30 // returns maximum size of image component
31 // if maxSize is infinite, match screen size and retain aspectRatio
GetMaxSize(const SizeF & maxSize,float aspectRatio)32 SizeF GetMaxSize(const SizeF& maxSize, float aspectRatio)
33 {
34     if (NearZero(aspectRatio)) {
35         return { 0.0, 0.0 };
36     }
37     // infinite size not allowed
38     bool infWidth = GreaterOrEqualToInfinity(maxSize.Width());
39     bool infHeight = GreaterOrEqualToInfinity(maxSize.Height());
40     if (infWidth && infHeight) {
41         auto width = PipelineContext::GetCurrentRootWidth();
42         return { width, width / aspectRatio };
43     }
44     if (infWidth) {
45         return { maxSize.Height() * aspectRatio, maxSize.Height() };
46     }
47     if (infHeight) {
48         return { maxSize.Width(), maxSize.Width() / aspectRatio };
49     }
50     return maxSize;
51 }
52 } // namespace
53 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)54 std::optional<SizeF> ImageLayoutAlgorithm::MeasureContent(
55     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
56 {
57     // case 1: image component is set with valid size, return contentConstraint.selfIdealSize as component size
58     if (contentConstraint.selfIdealSize.IsValid()) {
59         return contentConstraint.selfIdealSize.ConvertToSizeT();
60     }
61     // case 2: image component is not set with size, use image source size to determine component size
62     // if image data and altImage are both not ready, can not decide content size,
63     // return std::nullopt and wait for next layout task triggered by [OnImageDataReady]
64     auto loadingCtx = loadingCtx_.Upgrade();
65     auto altLoadingCtx = altLoadingCtx_.Upgrade();
66     if ((!loadingCtx || !loadingCtx->GetImageSize().IsPositive()) &&
67         (!altLoadingCtx || !altLoadingCtx->GetImageSize().IsPositive())) {
68         return std::nullopt;
69     }
70     // if image data is valid, use image source, or use altImage data
71     auto rawImageSize = SizeF(-1.0, -1.0);
72     if (loadingCtx) {
73         rawImageSize = loadingCtx->GetImageSize();
74     }
75     if (rawImageSize.IsNegative() && altLoadingCtx) {
76         rawImageSize = altLoadingCtx->GetImageSize();
77     }
78     SizeF size(rawImageSize);
79     do {
80         auto aspectRatio = static_cast<float>(Size::CalcRatio(rawImageSize));
81         if (NearZero(aspectRatio)) {
82             TAG_LOGW(AceLogTag::ACE_IMAGE, "image aspectRatio is 0");
83             return std::nullopt;
84         }
85         // case 2.1: image component is not set with size, use image source size as image component size
86         //          if fitOriginalSize is true, use image source size as image component size
87         //          if fitOriginalSize is false, use the parent component LayoutConstraint size as image component size
88         const auto& props = DynamicCast<ImageLayoutProperty>(layoutWrapper->GetLayoutProperty());
89         bool fitOriginalSize = props->GetFitOriginalSize().value_or(false);
90         if (contentConstraint.selfIdealSize.IsNull()) {
91             if (!fitOriginalSize) {
92                 size.SetSizeT(GetMaxSize(contentConstraint.maxSize, aspectRatio));
93             }
94             break;
95         }
96         // case 2.2 image component is set with width or height, and
97         //          image data is ready, use image source size to determine image component size
98         //          keep the principle of making the component aspect ratio and the image source aspect ratio the same.
99         //          the fitOriginSize is only useful in case 2.1.
100         auto sizeSet = contentConstraint.selfIdealSize.ConvertToSizeT();
101         size.SetSizeT(sizeSet);
102         uint8_t sizeSetStatus = (Negative(sizeSet.Width()) << 1) | Negative(sizeSet.Height());
103         switch (sizeSetStatus) {
104             case 0b01: // width is positive and height is negative
105                 size.SetHeight(sizeSet.Width() / aspectRatio);
106                 break;
107             case 0b10: // width is negative and height is positive
108                 size.SetWidth(sizeSet.Height() * aspectRatio);
109                 break;
110             case 0b11: // both width and height are negative
111             default:
112                 break;
113         }
114     } while (false);
115     return contentConstraint.Constrain(size);
116 }
117 
Measure(LayoutWrapper * layoutWrapper)118 void ImageLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
119 {
120     BoxLayoutAlgorithm::Measure(layoutWrapper);
121     auto ctx = loadingCtx_.Upgrade();
122     CHECK_NULL_VOID(ctx);
123     ctx->FinishMearuse();
124     ctx->CallbackAfterMeasureIfNeed();
125 }
126 
Layout(LayoutWrapper * layoutWrapper)127 void ImageLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
128 {
129     CHECK_NULL_VOID(layoutWrapper);
130     if (IsImageAnimationLayout(layoutWrapper)) {
131         PerformImageAnimationLayout(layoutWrapper);
132         for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
133             child->Layout();
134         }
135         return;
136     }
137     BoxLayoutAlgorithm::Layout(layoutWrapper);
138 }
139 
PerformImageAnimationLayout(LayoutWrapper * layoutWrapper)140 void ImageLayoutAlgorithm::PerformImageAnimationLayout(LayoutWrapper* layoutWrapper)
141 {
142     // update child position.
143     CHECK_NULL_VOID(layoutWrapper);
144     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
145     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
146     MinusPaddingToSize(padding, size);
147     auto left = padding.left.value_or(0);
148     auto top = padding.top.value_or(0);
149     auto paddingOffset = OffsetF(left, top);
150     auto align = Alignment::CENTER;
151     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
152         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
153     }
154     // Update child position.
155     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
156         if (!child) {
157             continue;
158         }
159         SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
160         auto translate = Alignment::GetAlignPosition(size, childSize, align);
161         if (!child->GetHostNode() || child->GetHostNode()->GetTag() != V2::IMAGE_ETS_TAG) {
162             translate += paddingOffset;
163         }
164         child->GetGeometryNode()->SetMarginFrameOffset(translate);
165     }
166     // Update content position.
167     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
168     if (content) {
169         auto translate = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
170         content->SetOffset(translate);
171     }
172 }
173 
IsImageAnimationLayout(LayoutWrapper * layoutWrapper)174 bool ImageLayoutAlgorithm::IsImageAnimationLayout(LayoutWrapper* layoutWrapper)
175 {
176     CHECK_NULL_RETURN(layoutWrapper, false);
177     auto frameNode = layoutWrapper->GetHostNode();
178     CHECK_NULL_RETURN(frameNode, false);
179     auto pattern = AceType::DynamicCast<ImagePattern>(frameNode->GetPattern());
180     CHECK_NULL_RETURN(pattern, false);
181     CHECK_EQUAL_RETURN(pattern->GetIsAnimation(), true, true);
182     return false;
183 }
184 } // namespace OHOS::Ace::NG
185