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/render/image_painter.h"
17 
18 #include "core/components_ng/render/drawing_prop_convertor.h"
19 #include "core/pipeline_ng/pipeline_context.h"
20 
21 namespace OHOS::Ace::NG {
22 
23 namespace {
24 
25 constexpr Color COLOR_PRIVATE_MODE = Color(0x66d7d7d7);
26 constexpr Dimension RECT_BORDER_RADIUS = 2.0_vp;
27 constexpr uint32_t RADIUS_POINT_COUNT = 4;
28 
ApplyContain(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)29 void ApplyContain(const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
30 {
31     if (rawPicSize.IsNonPositive()) {
32         return;
33     }
34     if (Size::CalcRatio(srcRect) > Size::CalcRatio(dstRect)) {
35         dstRect.SetSize(rawPicSize * (dstSize.Width() / rawPicSize.Width()));
36     } else {
37         dstRect.SetSize(rawPicSize * (dstSize.Height() / rawPicSize.Height()));
38     }
39     dstRect.SetOffset(Alignment::GetAlignPosition(dstSize, dstRect.GetSize(), Alignment::CENTER));
40 }
41 
ApplyCover(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)42 void ApplyCover(const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
43 {
44     if (Size::CalcRatio(srcRect) > Size::CalcRatio(dstRect)) {
45         srcRect.SetSize(dstSize * (rawPicSize.Height() / dstSize.Height()));
46     } else {
47         srcRect.SetSize(dstSize * (rawPicSize.Width() / dstSize.Width()));
48     }
49     srcRect.SetOffset(Alignment::GetAlignPosition(rawPicSize, srcRect.GetSize(), Alignment::CENTER));
50 }
51 
ApplyCoverTopLeft(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)52 void ApplyCoverTopLeft(const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
53 {
54     ApplyCover(rawPicSize, dstSize, srcRect, dstRect);
55 }
56 
ApplyFitWidth(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)57 void ApplyFitWidth(const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
58 {
59     if (Size::CalcRatio(srcRect) > Size::CalcRatio(dstRect)) {
60         dstRect.SetSize(rawPicSize * (dstSize.Width() / rawPicSize.Width()));
61         dstRect.SetOffset(Alignment::GetAlignPosition(dstSize, dstRect.GetSize(), Alignment::CENTER));
62     } else {
63         srcRect.SetSize(dstSize * (rawPicSize.Width() / dstSize.Width()));
64         srcRect.SetOffset(Alignment::GetAlignPosition(rawPicSize, srcRect.GetSize(), Alignment::CENTER));
65     }
66 }
67 
ApplyFitHeight(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)68 void ApplyFitHeight(const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
69 {
70     if (Size::CalcRatio(srcRect) > Size::CalcRatio(dstRect)) {
71         srcRect.SetSize(dstSize * (rawPicSize.Height() / dstSize.Height()));
72         srcRect.SetOffset(Alignment::GetAlignPosition(rawPicSize, srcRect.GetSize(), Alignment::CENTER));
73     } else {
74         dstRect.SetSize(rawPicSize * (dstSize.Height() / rawPicSize.Height()));
75         dstRect.SetOffset(Alignment::GetAlignPosition(dstSize, dstRect.GetSize(), Alignment::CENTER));
76     }
77 }
78 
ApplyNone(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)79 void ApplyNone(const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
80 {
81     SizeF srcSize(std::min(dstSize.Width(), rawPicSize.Width()), std::min(dstSize.Height(), rawPicSize.Height()));
82     dstRect.SetRect(Alignment::GetAlignPosition(dstSize, srcSize, Alignment::CENTER), srcSize);
83     srcRect.SetRect(Alignment::GetAlignPosition(rawPicSize, srcSize, Alignment::CENTER), srcSize);
84 }
85 
ApplyAlignment(const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect,const Alignment & alignMent)86 void ApplyAlignment(
87     const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect, const Alignment& alignMent)
88 {
89     SizeF srcSize(std::min(dstSize.Width(), rawPicSize.Width()), std::min(dstSize.Height(), rawPicSize.Height()));
90     dstRect.SetRect(Alignment::GetAlignPosition(dstSize, srcSize, alignMent), srcSize);
91     srcRect.SetRect(Alignment::GetAlignPosition(rawPicSize, srcSize, alignMent), srcSize);
92 }
93 } // namespace
94 
95 const std::unordered_map<ImageFit, std::function<Alignment(bool)>> ImagePainter::ALIMENT_OPERATIONS = {
96     { ImageFit::TOP_LEFT,
__anon1ef437450202() 97         [](bool isRightToLeft) { return isRightToLeft ? Alignment::TOP_RIGHT : Alignment::TOP_LEFT; } },
__anon1ef437450302() 98     { ImageFit::TOP, [](bool) { return Alignment::TOP_CENTER; } },
99     { ImageFit::TOP_END,
__anon1ef437450402() 100         [](bool isRightToLeft) { return isRightToLeft ? Alignment::TOP_LEFT : Alignment::TOP_RIGHT; } },
101     { ImageFit::START,
__anon1ef437450502() 102         [](bool isRightToLeft) { return isRightToLeft ? Alignment::CENTER_RIGHT : Alignment::CENTER_LEFT; } },
__anon1ef437450602() 103     { ImageFit::CENTER, [](bool) { return Alignment::CENTER; } },
104     { ImageFit::END,
__anon1ef437450702() 105         [](bool isRightToLeft) { return isRightToLeft ? Alignment::CENTER_LEFT : Alignment::CENTER_RIGHT; } },
106     { ImageFit::BOTTOM_START,
__anon1ef437450802() 107         [](bool isRightToLeft) { return isRightToLeft ? Alignment::BOTTOM_RIGHT : Alignment::BOTTOM_LEFT; } },
__anon1ef437450902() 108     { ImageFit::BOTTOM, [](bool) { return Alignment::BOTTOM_CENTER; } },
109     { ImageFit::BOTTOM_END,
__anon1ef437450a02() 110         [](bool isRightToLeft) { return isRightToLeft ? Alignment::BOTTOM_LEFT : Alignment::BOTTOM_RIGHT; } }
111 };
112 
DrawObscuration(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const113 void ImagePainter::DrawObscuration(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
114 {
115     CHECK_NULL_VOID(canvasImage_);
116     const auto config = canvasImage_->GetPaintConfig();
117     RSBrush brush;
118     Color fillColor = COLOR_PRIVATE_MODE;
119     RSColor rSColor(fillColor.GetRed(), fillColor.GetGreen(), fillColor.GetBlue(), fillColor.GetAlpha());
120     brush.SetColor(rSColor);
121     std::vector<RSPoint> radiusXY(RADIUS_POINT_COUNT);
122     if (config.borderRadiusXY_) {
123         for (auto index = 0U; index < radiusXY.size(); index++) {
124             radiusXY[index].SetX(static_cast<float>((*config.borderRadiusXY_)[index].GetX()));
125             radiusXY[index].SetY(static_cast<float>((*config.borderRadiusXY_)[index].GetY()));
126         }
127     } else if (config.isSvg_) {
128         // obscured SVGs need a default corner radius
129         for (auto& radius : radiusXY) {
130             radius.SetX(static_cast<float>(RECT_BORDER_RADIUS.ConvertToPx()));
131             radius.SetY(static_cast<float>(RECT_BORDER_RADIUS.ConvertToPx()));
132         }
133     }
134     canvas.AttachBrush(brush);
135     RSRoundRect rsRoundRect(
136         RSRect(offset.GetX(), offset.GetY(), contentSize.Width() + offset.GetX(), contentSize.Height() + offset.GetY()),
137         radiusXY);
138     canvas.DrawRoundRect(rsRoundRect);
139     canvas.DetachBrush();
140 }
141 
DrawImage(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const142 void ImagePainter::DrawImage(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
143 {
144     CHECK_NULL_VOID(canvasImage_);
145     const auto config = canvasImage_->GetPaintConfig();
146     bool drawObscuration = std::any_of(config.obscuredReasons_.begin(), config.obscuredReasons_.end(),
147         [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
148     if (drawObscuration) {
149         DrawObscuration(canvas, offset, contentSize);
150     } else if (config.isSvg_) {
151         DrawSVGImage(canvas, offset, contentSize);
152     } else {
153         DrawStaticImage(canvas, offset, contentSize);
154     }
155 }
156 
DrawSVGImage(RSCanvas & canvas,const OffsetF & offset,const SizeF & svgContainerSize) const157 void ImagePainter::DrawSVGImage(RSCanvas& canvas, const OffsetF& offset, const SizeF& svgContainerSize) const
158 {
159     CHECK_NULL_VOID(canvasImage_);
160     canvas.Save();
161     canvas.Translate(offset.GetX(), offset.GetY());
162     const auto config = canvasImage_->GetPaintConfig();
163     if (config.flipHorizontally_) {
164         ImagePainter::FlipHorizontal(canvas, svgContainerSize);
165     }
166 
167     RectF srcRect;
168     srcRect.SetSize(svgContainerSize);
169     canvasImage_->DrawToRSCanvas(canvas, ToRSRect(srcRect), ToRSRect(config.dstRect_),
170         config.borderRadiusXY_ ? *config.borderRadiusXY_ : BorderRadiusArray());
171     canvas.Restore();
172 }
173 
DrawStaticImage(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const174 void ImagePainter::DrawStaticImage(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
175 {
176     CHECK_NULL_VOID(canvasImage_);
177     const auto config = canvasImage_->GetPaintConfig();
178     canvas.Save();
179     canvas.Translate(offset.GetX(), offset.GetY());
180 
181     if (config.flipHorizontally_) {
182         ImagePainter::FlipHorizontal(canvas, contentSize);
183     }
184 
185     canvasImage_->DrawToRSCanvas(canvas, ToRSRect(config.srcRect_), ToRSRect(config.dstRect_),
186         config.borderRadiusXY_ ? *config.borderRadiusXY_ : BorderRadiusArray());
187     canvas.Restore();
188 }
189 
FlipHorizontal(RSCanvas & canvas,const SizeF & contentSize)190 void ImagePainter::FlipHorizontal(RSCanvas& canvas, const SizeF& contentSize)
191 {
192     canvas.Translate(contentSize.Width() / 2, contentSize.Height() / 2);
193     canvas.Scale(-1.0, 1.0);
194     canvas.Translate(-contentSize.Width() / 2, -contentSize.Height() / 2);
195 }
196 
DrawImageWithRepeat(RSCanvas & canvas,const RectF & contentRect) const197 void ImagePainter::DrawImageWithRepeat(RSCanvas& canvas, const RectF& contentRect) const
198 {
199     auto config = canvasImage_->GetPaintConfig();
200     if (config.imageRepeat_ == ImageRepeat::NO_REPEAT) {
201         return;
202     }
203     auto offset = contentRect.GetOffset();
204     float contentWidth = contentRect.Width();
205     float contentHeight = contentRect.Height();
206     float singleImageWidth = config.dstRect_.Width();
207     float singleImageHeight = config.dstRect_.Height();
208     if (NearZero(singleImageWidth) || NearZero(singleImageHeight)) {
209         return;
210     }
211 
212     bool imageRepeatX = config.imageRepeat_ == ImageRepeat::REPEAT || config.imageRepeat_ == ImageRepeat::REPEAT_X;
213     bool imageRepeatY = config.imageRepeat_ == ImageRepeat::REPEAT || config.imageRepeat_ == ImageRepeat::REPEAT_Y;
214     std::vector<uint32_t> dirRepeatNum = { static_cast<uint32_t>(ceil(config.dstRect_.GetY() / singleImageHeight)),
215         static_cast<uint32_t>((ceil((contentHeight - config.dstRect_.GetY()) / singleImageHeight))) - 1,
216         static_cast<uint32_t>(ceil(config.dstRect_.GetX() / singleImageWidth)),
217         imageRepeatX ? static_cast<uint32_t>(ceil((contentWidth - config.dstRect_.GetX()) / singleImageWidth)) : 1 };
218 
219     canvas.Save();
220     auto clipRect = RSRect(offset.GetX(), offset.GetY(), static_cast<float>(offset.GetX() + contentWidth),
221         static_cast<float>(offset.GetY() + contentHeight));
222     canvas.ClipRect(clipRect, RSClipOp::INTERSECT);
223     uint32_t up = 0;
224     uint32_t down = 1;
225     uint32_t left = 2;
226     uint32_t right = 3;
227     auto drawRepeatYTask = [this, &canvas, &config, &dirRepeatNum, &singleImageHeight, &imageRepeatY, &contentRect](
228                                OffsetF offsetTempY, uint32_t dir) {
229         float downNum = (dir == 0) ? -1 : 1;
230         for (size_t j = 0; j < dirRepeatNum[dir] && imageRepeatY; j++) {
231             offsetTempY.SetY(static_cast<float>(offsetTempY.GetY() + singleImageHeight * downNum));
232             DrawStaticImage(canvas, offsetTempY, contentRect.GetSize());
233         }
234     };
235     auto offsetTempX = offset;
236     // right
237     for (size_t i = 0; i < dirRepeatNum[right]; i++) {
238         DrawStaticImage(canvas, offsetTempX, contentRect.GetSize());
239         drawRepeatYTask(offsetTempX, up);
240         drawRepeatYTask(offsetTempX, down);
241         offsetTempX.SetX(static_cast<float>(offsetTempX.GetX() + singleImageWidth));
242     }
243     // left
244     offsetTempX = offset;
245     for (size_t i = 0; i < dirRepeatNum[left] && imageRepeatX; i++) {
246         offsetTempX.SetX(static_cast<float>(offsetTempX.GetX() - singleImageWidth));
247         DrawStaticImage(canvas, offsetTempX, contentRect.GetSize());
248         drawRepeatYTask(offsetTempX, up);
249         drawRepeatYTask(offsetTempX, down);
250     }
251     canvas.Restore();
252 }
253 
ApplyImageAlignmentFit(ImageFit imageFit,const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)254 void ImagePainter::ApplyImageAlignmentFit(
255     ImageFit imageFit, const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
256 {
257     auto isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft();
258     auto itImageFit = ALIMENT_OPERATIONS.find(imageFit);
259     if (itImageFit != ALIMENT_OPERATIONS.end()) {
260         Alignment alignment = itImageFit->second(isRightToLeft);
261         ApplyAlignment(rawPicSize, dstSize, srcRect, dstRect, alignment);
262     }
263 }
264 
ApplyImageFit(ImageFit imageFit,const SizeF & rawPicSize,const SizeF & dstSize,RectF & srcRect,RectF & dstRect)265 void ImagePainter::ApplyImageFit(
266     ImageFit imageFit, const SizeF& rawPicSize, const SizeF& dstSize, RectF& srcRect, RectF& dstRect)
267 {
268     auto context = PipelineContext::GetCurrentContext();
269     float viewScale = context ? context->GetViewScale() : 1.0;
270     srcRect.SetOffset(OffsetF());
271     srcRect.SetSize(rawPicSize);
272     srcRect.ApplyScale(1.0 / viewScale);
273     dstRect.SetOffset(OffsetF());
274     dstRect.SetSize(dstSize);
275     if (imageFit >= ImageFit::TOP_LEFT && imageFit <= ImageFit::BOTTOM_END) {
276         ApplyImageAlignmentFit(imageFit, rawPicSize, dstSize, srcRect, dstRect);
277         srcRect.ApplyScale(viewScale);
278         return;
279     }
280     switch (imageFit) {
281         case ImageFit::FILL:
282             break;
283         case ImageFit::NONE:
284             ApplyNone(rawPicSize, dstSize, srcRect, dstRect);
285             break;
286         case ImageFit::COVER:
287             ApplyCover(rawPicSize, dstSize, srcRect, dstRect);
288             break;
289         case ImageFit::COVER_TOP_LEFT:
290             ApplyCoverTopLeft(rawPicSize, dstSize, srcRect, dstRect);
291             break;
292         case ImageFit::FITWIDTH:
293             ApplyFitWidth(rawPicSize, dstSize, srcRect, dstRect);
294             break;
295         case ImageFit::FITHEIGHT:
296             ApplyFitHeight(rawPicSize, dstSize, srcRect, dstRect);
297             break;
298         case ImageFit::SCALE_DOWN:
299             if (srcRect.GetSize() < dstRect.GetSize()) {
300                 ApplyNone(rawPicSize, dstSize, srcRect, dstRect);
301             } else {
302                 ApplyContain(rawPicSize, dstSize, srcRect, dstRect);
303             }
304             break;
305         case ImageFit::CONTAIN:
306         default:
307             ApplyContain(rawPicSize, dstSize, srcRect, dstRect);
308             break;
309     }
310     srcRect.ApplyScale(viewScale);
311 }
312 
CalculateBgImagePosition(const SizeF & boxPaintSize_,const SizeF & imageRenderSize_,const std::optional<BackgroundImagePosition> & bgImgPositionOpt)313 OffsetF ImagePainter::CalculateBgImagePosition(const SizeF& boxPaintSize_, const SizeF& imageRenderSize_,
314     const std::optional<BackgroundImagePosition>& bgImgPositionOpt)
315 {
316     OffsetF offset(.0, .0);
317     if (bgImgPositionOpt == std::nullopt) {
318         return offset;
319     }
320     const auto& bgImgPosition = bgImgPositionOpt.value();
321     if (bgImgPosition.GetSizeTypeX() == BackgroundImagePositionType::PX) {
322         offset.SetX(bgImgPosition.GetSizeValueX());
323     } else {
324         offset.SetX(bgImgPosition.GetSizeValueX() * boxPaintSize_.Width());
325     }
326 
327     if (bgImgPosition.GetSizeTypeY() == BackgroundImagePositionType::PX) {
328         offset.SetY(bgImgPosition.GetSizeValueY());
329     } else {
330         offset.SetY(bgImgPosition.GetSizeValueY() * boxPaintSize_.Height());
331     }
332 
333     if (bgImgPosition.IsAlign()) {
334         offset.SetX(
335             bgImgPosition.GetSizeValueX() * (boxPaintSize_.Width() - imageRenderSize_.Width()) / PERCENT_TRANSLATE);
336         offset.SetY(
337             bgImgPosition.GetSizeValueY() * (boxPaintSize_.Height() - imageRenderSize_.Height()) / PERCENT_TRANSLATE);
338     }
339     return offset;
340 }
341 
342 namespace {
CalculateBgWidth(const SizeF & boxPaintSize_,const SizeF & srcSize,const BackgroundImageSize & bgImageSize)343 float CalculateBgWidth(const SizeF& boxPaintSize_, const SizeF& srcSize, const BackgroundImageSize& bgImageSize)
344 {
345     float width = 0.0f;
346     float paintAspectRatio = boxPaintSize_.Width() / boxPaintSize_.Height();
347     float srcAspectRatio = srcSize.Width() / srcSize.Height();
348     auto bgImageSizeTypeX = bgImageSize.GetSizeTypeX();
349     switch (bgImageSizeTypeX) {
350         case BackgroundImageSizeType::COVER:
351             width = paintAspectRatio >= srcAspectRatio ? boxPaintSize_.Width()
352                                                        : srcSize.Width() * (boxPaintSize_.Height() / srcSize.Height());
353             break;
354         case BackgroundImageSizeType::CONTAIN:
355             width = paintAspectRatio >= srcAspectRatio ? srcSize.Width() * (boxPaintSize_.Height() / srcSize.Height())
356                                                        : boxPaintSize_.Width();
357             break;
358         case BackgroundImageSizeType::FILL:
359             width = boxPaintSize_.Width();
360             break;
361         case BackgroundImageSizeType::LENGTH:
362             width = bgImageSize.GetSizeValueX();
363             break;
364         case BackgroundImageSizeType::PERCENT:
365             width = boxPaintSize_.Width() * bgImageSize.GetSizeValueX() / PERCENT_TRANSLATE;
366             break;
367         default:
368             break;
369     }
370     return width;
371 }
372 
CalculateBgHeight(const SizeF & boxPaintSize_,const SizeF & srcSize,const BackgroundImageSize & bgImageSize)373 float CalculateBgHeight(const SizeF& boxPaintSize_, const SizeF& srcSize, const BackgroundImageSize& bgImageSize)
374 {
375     float height = 0.0f;
376     float paintAspectRatio = boxPaintSize_.Width() / boxPaintSize_.Height();
377     float srcAspectRatio = srcSize.Width() / srcSize.Height();
378     auto bgImageSizeTypeY = bgImageSize.GetSizeTypeY();
379     switch (bgImageSizeTypeY) {
380         case BackgroundImageSizeType::COVER:
381             height = paintAspectRatio >= srcAspectRatio ? srcSize.Height() * (boxPaintSize_.Width() / srcSize.Width())
382                                                         : boxPaintSize_.Height();
383             break;
384         case BackgroundImageSizeType::CONTAIN:
385             height = paintAspectRatio >= srcAspectRatio ? boxPaintSize_.Height()
386                                                         : srcSize.Height() * (boxPaintSize_.Width() / srcSize.Width());
387             break;
388         case BackgroundImageSizeType::FILL:
389             height = boxPaintSize_.Height();
390             break;
391         case BackgroundImageSizeType::LENGTH:
392             height = bgImageSize.GetSizeValueY();
393             break;
394         case BackgroundImageSizeType::PERCENT:
395             height = boxPaintSize_.Height() * bgImageSize.GetSizeValueY() / PERCENT_TRANSLATE;
396             break;
397         default:
398             break;
399     }
400     return height;
401 }
402 } // namespace
403 
CalculateBgImageSize(const SizeF & boxPaintSize_,const SizeF & srcSize,const std::optional<BackgroundImageSize> & bgImageSizeOpt)404 SizeF ImagePainter::CalculateBgImageSize(
405     const SizeF& boxPaintSize_, const SizeF& srcSize, const std::optional<BackgroundImageSize>& bgImageSizeOpt)
406 {
407     SizeF sizeRet(srcSize.Width(), srcSize.Height());
408     if (bgImageSizeOpt == std::nullopt || NearZero(srcSize.Width()) || NearZero(srcSize.Height()) ||
409         NearZero(boxPaintSize_.Width()) || NearZero(boxPaintSize_.Height())) {
410         return sizeRet;
411     }
412     auto bgImageSize = bgImageSizeOpt.value();
413     float renderSizeX = CalculateBgWidth(boxPaintSize_, srcSize, bgImageSize);
414     float renderSizeY = CalculateBgHeight(boxPaintSize_, srcSize, bgImageSize);
415 
416     if (bgImageSize.GetSizeTypeX() == BackgroundImageSizeType::AUTO &&
417         bgImageSize.GetSizeTypeY() == BackgroundImageSizeType::AUTO) {
418         renderSizeX = srcSize.Width();
419         renderSizeY = srcSize.Height();
420     } else if (bgImageSize.GetSizeTypeX() == BackgroundImageSizeType::AUTO) {
421         renderSizeX = srcSize.Width() * (renderSizeY / srcSize.Height());
422     } else if (bgImageSize.GetSizeTypeY() == BackgroundImageSizeType::AUTO) {
423         renderSizeY = srcSize.Height() * (renderSizeX / srcSize.Width());
424     }
425     // fit image ratio if width or height is not set
426     float srcRatio = srcSize.Width() / srcSize.Height();
427     CHECK_NULL_RETURN(srcRatio > 0, sizeRet);
428     if (renderSizeX <= 0) {
429         renderSizeX = renderSizeY * srcRatio;
430     } else if (renderSizeY <= 0) {
431         renderSizeY = renderSizeX / srcRatio;
432     }
433     sizeRet.SetWidth(renderSizeX);
434     sizeRet.SetHeight(renderSizeY);
435     return sizeRet;
436 }
437 
438 } // namespace OHOS::Ace::NG
439