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