• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 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 #include "core/components_ng/pattern/image/image_pattern.h"
19 #include "core/components_ng/property/measure_utils.h"
20 #include "core/pipeline_ng/pipeline_context.h"
21 
22 namespace OHOS::Ace::NG {
23 namespace {
24 // returns maximum size of image component
25 // if maxSize is infinite, match screen size and retain aspectRatio
GetMaxSize(const SizeF & maxSize,float aspectRatio)26 SizeF GetMaxSize(const SizeF& maxSize, float aspectRatio)
27 {
28     if (NearZero(aspectRatio)) {
29         return { 0.0, 0.0 };
30     }
31     // infinite size not allowed
32     bool infWidth = GreaterOrEqualToInfinity(maxSize.Width());
33     bool infHeight = GreaterOrEqualToInfinity(maxSize.Height());
34     if (infWidth && infHeight) {
35         auto width = PipelineContext::GetCurrentRootWidth();
36         return { width, width / aspectRatio };
37     }
38     if (infWidth) {
39         return { maxSize.Height() * aspectRatio, maxSize.Height() };
40     }
41     if (infHeight) {
42         return { maxSize.Width(), maxSize.Width() / aspectRatio };
43     }
44     return maxSize;
45 }
46 } // namespace
47 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)48 std::optional<SizeF> ImageLayoutAlgorithm::MeasureContent(
49     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
50 {
51     // case 1: image component is set with valid size, return contentConstraint.selfIdealSize as component size
52     if (contentConstraint.selfIdealSize.IsValid()) {
53         return contentConstraint.selfIdealSize.ConvertToSizeT();
54     }
55     // case 2: image component is not set with size, use image source size to determine component size
56     // if image data and altImage are both not ready, can not decide content size,
57     // return std::nullopt and wait for next layout task triggered by [OnImageDataReady]
58     auto host = layoutWrapper->GetHostNode();
59     CHECK_NULL_RETURN(host, std::nullopt);
60     auto pattern = host->GetPattern<ImagePattern>();
61     CHECK_NULL_RETURN(pattern, std::nullopt);
62     auto rawImageSize = pattern->GetImageSizeForMeasure();
63     if (rawImageSize == std::nullopt) {
64         return std::nullopt;
65     }
66     SizeF size(rawImageSize.value());
67     do {
68         auto aspectRatio = static_cast<float>(Size::CalcRatio(rawImageSize.value()));
69         if (NearZero(aspectRatio)) {
70             TAG_LOGW(AceLogTag::ACE_IMAGE, "image aspectRatio is 0");
71             return std::nullopt;
72         }
73         // case 2.1: image component is not set with size, use image source size as image component size
74         //          if fitOriginalSize is true, use image source size as image component size
75         //          if fitOriginalSize is false, use the parent component LayoutConstraint size as image component size
76         const auto& props = DynamicCast<ImageLayoutProperty>(layoutWrapper->GetLayoutProperty());
77         bool fitOriginalSize = props->GetFitOriginalSize().value_or(false);
78         if (contentConstraint.selfIdealSize.IsNull()) {
79             if (!fitOriginalSize) {
80                 size.SetSizeT(GetMaxSize(contentConstraint.maxSize, aspectRatio));
81             }
82             break;
83         }
84         // case 2.2 image component is set with width or height, and
85         //          image data is ready, use image source size to determine image component size
86         //          keep the principle of making the component aspect ratio and the image source aspect ratio the same.
87         //          the fitOriginSize is only useful in case 2.1.
88         auto sizeSet = contentConstraint.selfIdealSize.ConvertToSizeT();
89         size.SetSizeT(sizeSet);
90         uint8_t sizeSetStatus = (Negative(sizeSet.Width()) << 1) | Negative(sizeSet.Height());
91         switch (sizeSetStatus) {
92             case 0b01: // width is positive and height is negative
93                 size.SetHeight(sizeSet.Width() / aspectRatio);
94                 break;
95             case 0b10: // width is negative and height is positive
96                 size.SetWidth(sizeSet.Height() * aspectRatio);
97                 break;
98             case 0b11: // both width and height are negative
99             default:
100                 break;
101         }
102     } while (false);
103     return contentConstraint.Constrain(size);
104 }
105 
106 /**
107  * Measure the layout size of the Image component based on its layout policy.
108  *
109  * This method handles different layout policies:
110  * - For WRAP_CONTENT or FIXED policies, it calculates the image's frame size using its raw size.
111  * - For MATCH_PARENT, it delegates the measurement to the base BoxLayoutAlgorithm and uses the result as the frame
112  *  size. If a valid size is determined, it is set on the geometry node.
113  */
Measure(LayoutWrapper * layoutWrapper)114 void ImageLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
115 {
116     CHECK_NULL_VOID(layoutWrapper);
117     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
118     CHECK_NULL_VOID(layoutProperty);
119     auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
120     bool isPolicy = layoutPolicy.has_value();
121     if (isPolicy && (layoutPolicy->IsWrap() || layoutPolicy->IsFix())) {
122         auto host = layoutWrapper->GetHostNode();
123         CHECK_NULL_VOID(host);
124         auto pattern = host->GetPattern<ImagePattern>();
125         CHECK_NULL_VOID(pattern);
126         auto rawImageSize = pattern->GetImageSizeForMeasure();
127         if (rawImageSize == std::nullopt) {
128             return;
129         }
130         OptionalSizeF imageFrameSize;
131         if (layoutPolicy->IsMatch()) {
132             BoxLayoutAlgorithm::Measure(layoutWrapper);
133             imageFrameSize.SetWidth(layoutWrapper->GetGeometryNode()->GetFrameSize().Width());
134             imageFrameSize.SetHeight(layoutWrapper->GetGeometryNode()->GetFrameSize().Height());
135         }
136         UpdateFrameSizeWithLayoutPolicy(layoutWrapper, imageFrameSize, rawImageSize);
137         if (imageFrameSize.IsValid()) {
138             layoutWrapper->GetGeometryNode()->SetFrameSize(imageFrameSize.ConvertToSizeT());
139         }
140     } else {
141         BoxLayoutAlgorithm::Measure(layoutWrapper);
142     }
143 }
144 
145 /**
146  * Update the frame size of the image (`imageFrameSize`) based on the layout policy defined in the layout property.
147  *
148  * This method handles different layout policies for width and height:
149  * - WRAP_CONTENT:
150  *   - If width or height is set to WRAP, use the smaller of the raw image size and the parent ideal size.
151  *   - This ensures the image adapts to its content but remains within parent constraints.
152  *
153  * - FIX_AT_IDEAL_SIZE:
154  *   - If width or height is FIXED, directly use the raw image's dimensions.
155  *   - This simulates a hardcoded size, unaffected by surrounding layout constraints.
156  *
157  * - MATCH_PARENT:
158  *   - MATCH is not handled here; it is assumed to be processed by other logic.
159  *   - Typically used when the image should stretch to fill the available space.
160  *
161  * - Default (neither WRAP, FIXED, nor MATCH):
162  *   - Use the self ideal size if specified, otherwise fall back to raw image size.
163  */
UpdateFrameSizeWithLayoutPolicy(LayoutWrapper * layoutWrapper,OptionalSizeF & imageFrameSize,const std::optional<SizeF> & rawImageSize)164 void ImageLayoutAlgorithm::UpdateFrameSizeWithLayoutPolicy(
165     LayoutWrapper* layoutWrapper, OptionalSizeF& imageFrameSize, const std::optional<SizeF>& rawImageSize)
166 {
167     // Update frame size based on layout policy
168     auto layoutProperty = AceType::DynamicCast<ImageLayoutProperty>(layoutWrapper->GetLayoutProperty());
169     CHECK_NULL_VOID(layoutProperty);
170     auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
171     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
172     CHECK_NULL_VOID(layoutConstraint.has_value());
173     // Calculate the frameWidth size based on the layout policy
174     if (layoutPolicy->IsWidthWrap()) {
175         auto parentIdealSize = layoutConstraint->parentIdealSize;
176         imageFrameSize.SetWidth(
177             std::min(parentIdealSize.Width().value_or(rawImageSize->Width()), rawImageSize->Width()));
178     } else if (layoutPolicy->IsWidthFix()) {
179         // If width is fixed, use the raw image size
180         imageFrameSize.SetWidth(rawImageSize->Width());
181     } else if (!layoutPolicy->IsWidthMatch()) {
182         imageFrameSize.SetWidth(layoutConstraint->selfIdealSize.Width().value_or(
183             layoutConstraint->parentIdealSize.Width().value_or(rawImageSize->Width())));
184     }
185     // Calculate the frameHeight size based on the layout policy
186     if (layoutPolicy->IsHeightWrap()) {
187         auto parentIdealSize = layoutConstraint->parentIdealSize;
188         imageFrameSize.SetHeight(
189             std::min(parentIdealSize.Height().value_or(rawImageSize->Height()), rawImageSize->Height()));
190     } else if (layoutPolicy->IsHeightFix()) {
191         // If height is fixed, use the raw image size
192         imageFrameSize.SetHeight(rawImageSize->Height());
193     } else if (!layoutPolicy->IsHeightMatch()) {
194         imageFrameSize.SetHeight(layoutConstraint->selfIdealSize.Height().value_or(
195             layoutConstraint->parentIdealSize.Height().value_or(rawImageSize->Height())));
196     }
197 }
198 
Layout(LayoutWrapper * layoutWrapper)199 void ImageLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
200 {
201     CHECK_NULL_VOID(layoutWrapper);
202     if (IsImageAnimationLayout(layoutWrapper)) {
203         PerformImageAnimationLayout(layoutWrapper);
204         for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
205             child->Layout();
206         }
207         return;
208     }
209     BoxLayoutAlgorithm::Layout(layoutWrapper);
210     auto host = layoutWrapper->GetHostNode();
211     CHECK_NULL_VOID(host);
212     auto pattern = host->GetPattern<ImagePattern>();
213     CHECK_NULL_VOID(pattern);
214     pattern->FinishMeasureForOnComplete();
215 }
216 
PerformImageAnimationLayout(LayoutWrapper * layoutWrapper)217 void ImageLayoutAlgorithm::PerformImageAnimationLayout(LayoutWrapper* layoutWrapper)
218 {
219     // update child position.
220     CHECK_NULL_VOID(layoutWrapper);
221     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
222     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
223     MinusPaddingToSize(padding, size);
224     auto left = padding.left.value_or(0);
225     auto top = padding.top.value_or(0);
226     auto paddingOffset = OffsetF(left, top);
227     auto align = Alignment::CENTER;
228     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
229         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
230     }
231     // Update child position.
232     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
233         if (!child) {
234             continue;
235         }
236         SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
237         auto translate = Alignment::GetAlignPosition(size, childSize, align);
238         if (!child->GetHostNode() || child->GetHostNode()->GetTag() != V2::IMAGE_ETS_TAG) {
239             translate += paddingOffset;
240         }
241         child->GetGeometryNode()->SetMarginFrameOffset(translate);
242     }
243     // Update content position.
244     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
245     if (content) {
246         auto translate = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
247         content->SetOffset(translate);
248     }
249 }
250 
IsImageAnimationLayout(LayoutWrapper * layoutWrapper)251 bool ImageLayoutAlgorithm::IsImageAnimationLayout(LayoutWrapper* layoutWrapper)
252 {
253     CHECK_NULL_RETURN(layoutWrapper, false);
254     auto frameNode = layoutWrapper->GetHostNode();
255     CHECK_NULL_RETURN(frameNode, false);
256     auto pattern = AceType::DynamicCast<ImagePattern>(frameNode->GetPattern());
257     CHECK_NULL_RETURN(pattern, false);
258     CHECK_EQUAL_RETURN(pattern->GetIsAnimation(), true, true);
259     return false;
260 }
261 } // namespace OHOS::Ace::NG
262