1 /*
2  * Copyright (c) 2022 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 #include "core/components_ng/pattern/bubble/bubble_paint_method.h"
16 
17 #include <vector>
18 
19 #include "base/geometry/dimension.h"
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/rect_t.h"
22 #include "base/geometry/rect.h"
23 #include "base/geometry/rrect.h"
24 #include "base/utils/utils.h"
25 #include "core/components/common/properties/alignment.h"
26 #include "core/components/common/properties/border.h"
27 #include "core/components/common/properties/color.h"
28 #include "core/components/common/properties/decoration.h"
29 #include "core/components/common/properties/placement.h"
30 #include "core/components/common/properties/shadow_config.h"
31 #include "core/components/popup/popup_theme.h"
32 #include "core/components/theme/theme_manager.h"
33 #include "core/components_ng/pattern/bubble/bubble_pattern.h"
34 #include "core/components_ng/pattern/bubble/bubble_render_property.h"
35 #include "core/components_ng/property/measure_utils.h"
36 #include "core/components_ng/render/canvas_image.h"
37 #include "core/components_ng/render/drawing.h"
38 #include "core/components_ng/render/drawing_prop_convertor.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40 #include "core/common/container.h"
41 
42 namespace OHOS::Ace::NG {
43 namespace {
44 constexpr Dimension BEZIER_WIDTH_HALF = 16.0_vp;
45 constexpr Dimension BEZIER_HORIZON_OFFSET_FIRST = 1.3_vp;
46 constexpr Dimension BEZIER_HORIZON_OFFSET_SECOND = 3.2_vp;
47 constexpr Dimension BEZIER_HORIZON_OFFSET_THIRD = 6.6_vp;
48 constexpr Dimension BEZIER_HORIZON_OFFSET_FOURTH = 16.0_vp;
49 constexpr Dimension BEZIER_VERTICAL_OFFSET_FIRST = 0.1_vp;
50 constexpr Dimension BEZIER_VERTICAL_OFFSET_SECOND = 3.0_vp;
51 constexpr Dimension BEZIER_VERTICAL_OFFSET_THIRD = 8.0_vp;
52 constexpr Dimension ARROW_WIDTH = 32.0_vp;
53 constexpr Dimension ARROW_ZERO_PERCENT_VALUE = Dimension(0.0, DimensionUnit::PERCENT);
54 constexpr Dimension ARROW_HALF_PERCENT_VALUE = Dimension(0.5, DimensionUnit::PERCENT);
55 constexpr Dimension ARROW_ONE_HUNDRED_PERCENT_VALUE = Dimension(1.0, DimensionUnit::PERCENT);
56 constexpr float BLUR_MASK_FILTER = 0.55f;
57 
58 constexpr double HALF = 2.0;
59 constexpr Dimension ARROW_RADIUS = 2.0_vp;
60 Dimension BUBBLE_ARROW_WIDTH = 16.0_vp;
61 Dimension BUBBLE_ARROW_HEIGHT = 10.0_vp;
62 constexpr int16_t P1INDEX = 0;
63 constexpr int16_t P2INDEX = 1;
64 constexpr int16_t P3INDEX = 2;
65 constexpr int16_t P4INDEX = 3;
66 } // namespace
67 
ModifyBorderRadius(float borderRadius,float halfChildHeight)68 float ModifyBorderRadius(float borderRadius, float halfChildHeight)
69 {
70     return GreatOrEqual(borderRadius, halfChildHeight) ? halfChildHeight : borderRadius;
71 }
72 
PaintMask(RSCanvas & canvas,PaintWrapper * paintWrapper)73 void BubblePaintMethod::PaintMask(RSCanvas& canvas, PaintWrapper* paintWrapper)
74 {
75     CHECK_NULL_VOID(paintWrapper);
76     auto paintProperty = DynamicCast<BubbleRenderProperty>(paintWrapper->GetPaintProperty());
77     CHECK_NULL_VOID(paintProperty);
78     auto pipelineContext = PipelineContext::GetCurrentContext();
79     CHECK_NULL_VOID(pipelineContext);
80     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
81     CHECK_NULL_VOID(popupTheme);
82     auto maskColor = paintProperty->GetMaskColor().value_or(popupTheme->GetMaskColor());
83     auto layoutSize = paintWrapper->GetContentSize();
84     canvas.Save();
85     RSBrush brush;
86     brush.SetColor(maskColor.GetValue());
87     brush.SetAntiAlias(true);
88     canvas.AttachBrush(brush);
89     canvas.DrawRect(RSRect(0.0, 0.0, layoutSize.Width(), layoutSize.Height()));
90     canvas.DetachBrush();
91     canvas.Restore();
92 }
93 
PaintBorder(RSCanvas & canvas,PaintWrapper * paintWrapper)94 void BubblePaintMethod::PaintBorder(RSCanvas& canvas, PaintWrapper* paintWrapper)
95 {
96     BorderEdge edge = border_.Left();
97     if (!border_.IsAllEqual()) {
98         edge = border_.GetValidEdge();
99         border_ = Border(edge);
100     }
101     if (!border_.HasValue()) {
102         return;
103     }
104     float borderWidth = edge.GetWidth().ConvertToPx();
105     RSPen paint;
106     paint.SetWidth(borderWidth);
107     paint.SetColor(edge.GetColor().GetValue());
108     paint.SetAntiAlias(true);
109     if (edge.GetBorderStyle() == BorderStyle::DOTTED) {
110 #ifndef USE_ROSEN_DRAWING
111         RSPath dotPath;
112         dotPath.AddCircle(0.0f, 0.0f, borderWidth / 2.0);
113         paint.SetPathEffect(
114             RSPathEffect::CreatePathDashEffect(dotPath, borderWidth * 2.0, 0.0, RSPathDashStyle::ROTATE));
115 #else
116         RSRecordingPath dotPath;
117         dotPath.AddCircle(0.0f, 0.0f, borderWidth / 2.0);
118         paint.SetPathEffect(
119             RSRecordingPathEffect::CreatePathDashEffect(dotPath, borderWidth * 2.0, 0.0, RSPathDashStyle::ROTATE));
120 #endif
121     } else if (edge.GetBorderStyle() == BorderStyle::DASHED) {
122 #ifndef USE_ROSEN_DRAWING
123         const float intervals[] = { borderWidth, borderWidth };
124         paint.SetPathEffect(RSPathEffect::CreateDashPathEffect(intervals, 2, 0.0));
125 #else
126         const RSScalar intervals[] = { borderWidth, borderWidth };
127         paint.SetPathEffect(RSRecordingPathEffect::CreateDashPathEffect(intervals, 2, 0.0));
128 #endif
129         DrawDashedBorder(canvas, paint);
130     } else {
131         paint.SetPathEffect(nullptr);
132     }
133     canvas.Save();
134     canvas.Translate(childOffset_.GetX() + childSize_.Width() / 2.0, childOffset_.GetY() + childSize_.Height() / 2.0);
135     canvas.Scale(1.0 - (borderWidth / childSize_.Width()), 1.0 - (borderWidth / childSize_.Height()));
136     canvas.Translate(
137         -(childOffset_.GetX() + childSize_.Width() / 2.0), -(childOffset_.GetY() + childSize_.Height() / 2.0));
138     canvas.AttachPen(paint);
139     auto rect = MakeRRect();
140     canvas.DrawRoundRect(rect);
141     canvas.DetachPen();
142     canvas.Restore();
143 }
144 
DrawDashedBorder(RSCanvas & canvas,RSPen & paint)145 void BubblePaintMethod::DrawDashedBorder(RSCanvas& canvas, RSPen& paint)
146 {
147     canvas.AttachPen(paint);
148     canvas.DrawPath(path_);
149     canvas.DetachPen();
150 }
151 
PaintBubble(RSCanvas & canvas,PaintWrapper * paintWrapper)152 void BubblePaintMethod::PaintBubble(RSCanvas& canvas, PaintWrapper* paintWrapper)
153 {
154     isPaintBubble_ = true;
155     CHECK_NULL_VOID(paintWrapper);
156     auto paintProperty = DynamicCast<BubbleRenderProperty>(paintWrapper->GetPaintProperty());
157     CHECK_NULL_VOID(paintProperty);
158     useCustom_ = paintProperty->GetUseCustom().value_or(false);
159     enableArrow_ = paintProperty->GetEnableArrow().value_or(true);
160     arrowPlacement_ = paintProperty->GetPlacement().value_or(Placement::BOTTOM);
161     UpdateArrowOffset(paintProperty->GetArrowOffset(), arrowPlacement_);
162     auto pipelineContext = PipelineContext::GetCurrentContext();
163     CHECK_NULL_VOID(pipelineContext);
164     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
165     CHECK_NULL_VOID(popupTheme);
166     backgroundColor_ = paintProperty->GetBackgroundColor().value_or(popupTheme->GetBackgroundColor());
167     border_.SetBorderRadius(popupTheme->GetRadius());
168     padding_ = popupTheme->GetPadding();
169     RSPen paint;
170     paint.SetColor(backgroundColor_.GetValue());
171     paint.SetAntiAlias(true);
172     RSFilter filter;
173     filter.SetMaskFilter(RSMaskFilter::CreateBlurMaskFilter(RSBlurType::SOLID, BLUR_MASK_FILTER));
174     paint.SetFilter(filter);
175     // TODO: color is not correct
176     RSBrush brush;
177     brush.SetColor(static_cast<int>(backgroundColor_.GetValue()));
178     brush.SetAntiAlias(true);
179     brush.SetFilter(filter);
180     canvas.AttachPen(paint);
181     canvas.AttachBrush(brush);
182     if (enableArrow_ && showArrow_) {
183         if (popupTheme->GetPopupDoubleBorderEnable()) {
184             canvas.DetachPen();
185             paint.SetWidth(outerBorderWidth_);
186             paint.SetColor(popupTheme->GetPopupOuterBorderColor().GetValue());
187             canvas.AttachPen(paint);
188         }
189         PaintBubbleWithArrow(canvas, paintWrapper);
190     } else {
191         PaintDefaultBubble(canvas);
192     }
193     canvas.DetachBrush();
194     canvas.DetachPen();
195     if (enableArrow_ && showArrow_ && popupTheme->GetPopupDoubleBorderEnable()) {
196         paint.SetWidth(innerBorderWidth_);
197         paint.SetColor(popupTheme->GetPopupInnerBorderColor().GetValue());
198         canvas.AttachPen(paint);
199         needPaintOuterBorder_ = true;
200         PaintBubbleWithArrow(canvas, paintWrapper);
201         needPaintOuterBorder_ = false;
202         canvas.DetachPen();
203     }
204 }
205 
IsPaintDoubleBorder(PaintWrapper * paintWrapper)206 bool BubblePaintMethod::IsPaintDoubleBorder(PaintWrapper* paintWrapper)
207 {
208     BUBBLE_ARROW_WIDTH = Dimension(arrowWidth_);
209     BUBBLE_ARROW_HEIGHT = Dimension(arrowHeight_);
210     if (!OHOS::Ace::Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) || isPaintBubble_) {
211         return false;
212     }
213     CHECK_NULL_RETURN(paintWrapper, false);
214     auto paintProperty = DynamicCast<BubbleRenderProperty>(paintWrapper->GetPaintProperty());
215     CHECK_NULL_RETURN(paintProperty, false);
216     enableArrow_ = paintProperty->GetEnableArrow().value_or(true);
217     arrowPlacement_ = paintProperty->GetPlacement().value_or(Placement::BOTTOM);
218     UpdateArrowOffset(paintProperty->GetArrowOffset(), arrowPlacement_);
219     auto pipelineContext = PipelineContext::GetCurrentContext();
220     CHECK_NULL_RETURN(pipelineContext, false);
221     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
222     CHECK_NULL_RETURN(popupTheme, false);
223     padding_ = popupTheme->GetPadding();
224     return enableArrow_ && showArrow_ && popupTheme->GetPopupDoubleBorderEnable();
225 }
226 
PaintOuterBorder(RSCanvas & canvas,PaintWrapper * paintWrapper)227 void BubblePaintMethod::PaintOuterBorder(RSCanvas& canvas, PaintWrapper* paintWrapper)
228 {
229     if (!IsPaintDoubleBorder(paintWrapper)) {
230         return;
231     }
232     auto pipelineContext = PipelineContext::GetCurrentContext();
233     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
234     RSPen paint;
235     RSFilter filter;
236     filter.SetMaskFilter(RSMaskFilter::CreateBlurMaskFilter(RSBlurType::SOLID, BLUR_MASK_FILTER));
237     paint.SetFilter(filter);
238     paint.SetAntiAlias(true);
239     paint.SetWidth(outerBorderWidth_);
240     paint.SetColor(popupTheme->GetPopupOuterBorderColor().GetValue());
241     canvas.AttachPen(paint);
242     needPaintOuterBorder_ = true;
243     PaintDoubleBorderWithArrow(canvas, paintWrapper);
244     canvas.DetachPen();
245     needPaintOuterBorder_ = false;
246 }
247 
PaintInnerBorder(RSCanvas & canvas,PaintWrapper * paintWrapper)248 void BubblePaintMethod::PaintInnerBorder(RSCanvas& canvas, PaintWrapper* paintWrapper)
249 {
250     if (!IsPaintDoubleBorder(paintWrapper)) {
251         return;
252     }
253     auto pipelineContext = PipelineContext::GetCurrentContext();
254     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
255     RSPen paint;
256     RSFilter filter;
257     filter.SetMaskFilter(RSMaskFilter::CreateBlurMaskFilter(RSBlurType::SOLID, BLUR_MASK_FILTER));
258     paint.SetFilter(filter);
259     paint.SetAntiAlias(true);
260     paint.SetWidth(innerBorderWidth_);
261     paint.SetColor(popupTheme->GetPopupInnerBorderColor().GetValue());
262     canvas.AttachPen(paint);
263     PaintDoubleBorderWithArrow(canvas, paintWrapper);
264     canvas.DetachPen();
265 }
266 
ClipBubble(PaintWrapper * paintWrapper)267 void BubblePaintMethod::ClipBubble(PaintWrapper* paintWrapper)
268 {
269     CHECK_NULL_VOID(paintWrapper);
270     auto paintProperty = DynamicCast<BubbleRenderProperty>(paintWrapper->GetPaintProperty());
271     CHECK_NULL_VOID(paintProperty);
272     enableArrow_ = paintProperty->GetEnableArrow().value_or(true);
273     auto pipelineContext = PipelineContext::GetCurrentContext();
274     CHECK_NULL_VOID(pipelineContext);
275     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
276     CHECK_NULL_VOID(popupTheme);
277     if (clipFrameNode_) {
278         ClipBubbleWithPath(clipFrameNode_);
279     }
280 }
281 
UpdateArrowOffset(const std::optional<Dimension> & offset,const Placement & placement)282 void BubblePaintMethod::UpdateArrowOffset(const std::optional<Dimension>& offset, const Placement& placement)
283 {
284     if (offset.has_value()) {
285         arrowOffset_ = offset.value();
286         if (arrowOffset_.Unit() == DimensionUnit::PERCENT) {
287             arrowOffset_.SetValue(std::clamp(arrowOffset_.Value(), 0.0, 1.0));
288         }
289         return;
290     }
291     switch (placement) {
292         case Placement::LEFT:
293         case Placement::RIGHT:
294         case Placement::TOP:
295         case Placement::BOTTOM:
296             arrowOffset_ = ARROW_HALF_PERCENT_VALUE;
297             break;
298         case Placement::TOP_LEFT:
299         case Placement::BOTTOM_LEFT:
300         case Placement::LEFT_TOP:
301         case Placement::RIGHT_TOP:
302             arrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
303             break;
304         case Placement::TOP_RIGHT:
305         case Placement::BOTTOM_RIGHT:
306         case Placement::LEFT_BOTTOM:
307         case Placement::RIGHT_BOTTOM:
308             arrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
309             break;
310         default:
311             break;
312     }
313 }
314 
PaintShadow(const RSPath & path,const Shadow & shadow,RSCanvas & canvas)315 void BubblePaintMethod::PaintShadow(const RSPath& path, const Shadow& shadow, RSCanvas& canvas)
316 {
317     canvas.Save();
318 #ifndef USE_ROSEN_DRAWING
319     RSPath rsPath = path;
320 #else
321     RSRecordingPath rsPath;
322     rsPath.AddPath(path);
323 #endif
324     rsPath.Offset(shadow.GetOffset().GetX(), shadow.GetOffset().GetY());
325     RSColor spotColor = ToRSColor(shadow.GetColor());
326     RSPoint3 planeParams = { 0.0f, 0.0f, shadow.GetElevation() };
327 #ifndef USE_ROSEN_DRAWING
328     RSPoint3 lightPos = { rsPath.GetBounds().GetLeft() / 2.0 + rsPath.GetBounds().GetRight() / 2.0,
329         rsPath.GetBounds().GetTop() / 2.0 + rsPath.GetBounds().GetBottom() / 2.0, shadow.GetLightHeight() };
330 #else
331     auto bounds = rsPath.GetBounds();
332     RSPoint3 lightPos = { bounds.GetLeft() / 2.0 + bounds.GetRight() / 2.0,
333         bounds.GetTop() / 2.0 + bounds.GetBottom() / 2.0, shadow.GetLightHeight() };
334 #endif
335     RSColor ambientColor = RSColor(0, 0, 0, 0);
336     canvas.DrawShadow(rsPath, planeParams, lightPos, shadow.GetLightRadius(), ambientColor, spotColor,
337         RSShadowFlags::TRANSPARENT_OCCLUDER);
338     canvas.Restore();
339 }
340 
PaintDefaultBubble(RSCanvas & canvas)341 void BubblePaintMethod::PaintDefaultBubble(RSCanvas& canvas)
342 {
343     auto rect = MakeRRect();
344     path_.AddRoundRect(
345         rect.GetRect(), border_.TopLeftRadius().GetX().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx());
346     canvas.Save();
347     canvas.ClipPath(path_, RSClipOp::DIFFERENCE, true);
348     PaintShadow(path_, ShadowConfig::DefaultShadowM, canvas);
349     canvas.Restore();
350     canvas.DrawRoundRect(rect);
351 }
352 
MakeRRect()353 RSRoundRect BubblePaintMethod::MakeRRect()
354 {
355     auto rect = RSRect(childOffset_.GetX(), childOffset_.GetY(), childOffset_.GetX() + childSize_.Width(),
356         childOffset_.GetY() + childSize_.Height());
357     std::vector<RSPoint> rectRadii;
358     rectRadii.resize(4);
359     rectRadii[RSRoundRect::TOP_LEFT_POS] =
360         RSPoint(border_.TopLeftRadius().GetX().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx());
361     rectRadii[RSRoundRect::TOP_RIGHT_POS] =
362         RSPoint(border_.TopRightRadius().GetX().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx());
363     rectRadii[RSRoundRect::BOTTOM_RIGHT_POS] =
364         RSPoint(border_.BottomRightRadius().GetX().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx());
365     rectRadii[RSRoundRect::BOTTOM_LEFT_POS] =
366         RSPoint(border_.BottomLeftRadius().GetX().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx());
367     return RSRoundRect(rect, rectRadii);
368 }
369 
PaintBubbleWithArrow(RSCanvas & canvas,PaintWrapper * paintWrapper)370 void BubblePaintMethod::PaintBubbleWithArrow(RSCanvas& canvas, PaintWrapper* paintWrapper)
371 {
372     BuildCompletePath(path_);
373     canvas.Save();
374     canvas.ClipPath(path_, RSClipOp::DIFFERENCE, true);
375     if (!needPaintOuterBorder_) {
376         PaintShadow(path_, ShadowConfig::DefaultShadowM, canvas);
377     }
378     canvas.Restore();
379     canvas.DrawPath(path_);
380 }
381 
PaintDoubleBorderWithArrow(RSCanvas & canvas,PaintWrapper * paintWrapper)382 void BubblePaintMethod::PaintDoubleBorderWithArrow(RSCanvas& canvas, PaintWrapper* paintWrapper)
383 {
384     BuildDoubleBorderPath(path_);
385     canvas.Save();
386     canvas.Restore();
387     canvas.DrawPath(path_);
388 }
389 
GetInnerBorderOffset()390 float BubblePaintMethod::GetInnerBorderOffset()
391 {
392     float borderOffset = 0;
393     auto pipeline = PipelineBase::GetCurrentContext();
394     CHECK_NULL_RETURN(pipeline, 0);
395     auto popupTheme = pipeline->GetTheme<PopupTheme>();
396     CHECK_NULL_RETURN(popupTheme, 0);
397     if (popupTheme->GetPopupDoubleBorderEnable() && needPaintOuterBorder_) {
398         borderOffset = outerBorderWidth_;
399     }
400     return borderOffset;
401 }
402 
GetBorderOffset()403 float BubblePaintMethod::GetBorderOffset()
404 {
405     float borderOffset = 0.0f;
406     auto pipeline = PipelineBase::GetCurrentContext();
407     CHECK_NULL_RETURN(pipeline, 0);
408     auto popupTheme = pipeline->GetTheme<PopupTheme>();
409     CHECK_NULL_RETURN(popupTheme, 0);
410     if (popupTheme->GetPopupDoubleBorderEnable()) {
411         if (needPaintOuterBorder_) {
412             borderOffset = -outerBorderWidth_;
413         } else {
414             borderOffset = innerBorderWidth_;
415         }
416     }
417     return borderOffset;
418 }
419 
BuildCompletePath(RSPath & path)420 void BubblePaintMethod::BuildCompletePath(RSPath& path)
421 {
422     float borderOffset = GetBorderOffset();
423     float arrowOffset = GetArrowOffset(arrowPlacement_);
424     auto borderRadius = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Height() / 2);
425     float radiusPx = borderRadius - borderOffset;
426     path.Reset();
427     path.MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY() + borderOffset);
428     BuildTopLinePath(path, arrowOffset, radiusPx);
429     BuildCornerPath(path, Placement::TOP_RIGHT, radiusPx);
430     BuildRightLinePath(path, arrowOffset, radiusPx);
431     BuildCornerPath(path, Placement::BOTTOM_RIGHT, radiusPx);
432     BuildBottomLinePath(path, arrowOffset, radiusPx);
433     BuildCornerPath(path, Placement::BOTTOM_LEFT, radiusPx);
434     BuildLeftLinePath(path, arrowOffset, radiusPx);
435     BuildCornerPath(path, Placement::TOP_LEFT, radiusPx);
436     path.Close();
437 }
438 
BuildDoubleBorderPath(RSPath & path)439 void BubblePaintMethod::BuildDoubleBorderPath(RSPath& path)
440 {
441     float borderOffset = GetBorderOffset();
442     auto borderRadius = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Height() / 2);
443     float radiusPx = borderRadius - borderOffset;
444     path.Reset();
445     path.MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY() + borderOffset);
446     BuildTopDoubleBorderPath(path, radiusPx);
447     BuildCornerPath(path, Placement::TOP_RIGHT, radiusPx);
448     BuildRightDoubleBorderPath(path, radiusPx);
449     BuildCornerPath(path, Placement::BOTTOM_RIGHT, radiusPx);
450     BuildBottomDoubleBorderPath(path, radiusPx);
451     BuildCornerPath(path, Placement::BOTTOM_LEFT, radiusPx);
452     BuildLeftDoubleBorderPath(path, radiusPx);
453     BuildCornerPath(path, Placement::TOP_LEFT, radiusPx);
454     path.Close();
455 }
456 
BuildTopLinePath(RSPath & path,float arrowOffset,float radius)457 void BubblePaintMethod::BuildTopLinePath(RSPath& path, float arrowOffset, float radius)
458 {
459     float borderOffset = GetBorderOffset();
460     float childOffsetY = childOffset_.GetY();
461     float arrowPositionY = arrowPosition_.GetY();
462     auto pipeline = PipelineBase::GetCurrentContext();
463     CHECK_NULL_VOID(pipeline);
464     auto popupTheme = pipeline->GetTheme<PopupTheme>();
465     CHECK_NULL_VOID(popupTheme);
466     auto leftOffset =
467         childOffset_.GetX() + popupTheme->GetRadius().GetX().ConvertToPx() + ARROW_WIDTH.ConvertToPx() / 2;
468     auto rightOffset = childOffset_.GetX() + childSize_.Width() - popupTheme->GetRadius().GetX().ConvertToPx() -
469                        ARROW_WIDTH.ConvertToPx() / 2;
470     auto arrowTopOffset = std::clamp(
471         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
472     switch (arrowPlacement_) {
473         case Placement::BOTTOM:
474         case Placement::BOTTOM_LEFT:
475         case Placement::BOTTOM_RIGHT:
476             path.LineTo(arrowTopOffset - BEZIER_WIDTH_HALF.ConvertToPx() + borderOffset,
477                 childOffsetY + borderOffset);
478             path.QuadTo(arrowTopOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
479                 arrowPositionY + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
480                 arrowTopOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
481                 arrowPositionY + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset);
482             path.QuadTo(arrowTopOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
483                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
484                 arrowTopOffset, arrowPositionY + borderOffset);
485             path.QuadTo(arrowTopOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
486                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
487                 arrowTopOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
488                 arrowPositionY + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset);
489             path.QuadTo(arrowTopOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
490                 arrowPositionY + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
491                 arrowTopOffset + BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx(),
492                 arrowPositionY + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset);
493             break;
494         default:
495             break;
496     }
497     path.LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY + borderOffset);
498 }
499 
BuildTopDoubleBorderPath(RSPath & path,float radius)500 void BubblePaintMethod::BuildTopDoubleBorderPath(RSPath& path, float radius)
501 {
502     float borderOffset = 0.0f;
503     if (needPaintOuterBorder_) {
504         borderOffset = -outerBorderWidth_;
505     } else {
506         borderOffset = innerBorderWidth_ / HALF;
507     }
508     float childOffsetY = childOffset_.GetY();
509     float arrowTopOffset = childOffset_.GetX() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
510     switch (arrowPlacement_) {
511         case Placement::BOTTOM:
512         case Placement::BOTTOM_LEFT:
513         case Placement::BOTTOM_RIGHT:
514             borderOffset = GetBorderOffset();
515             path.LineTo(arrowTopOffset + arrowOffsetsFromClip_[P1INDEX][0] + borderOffset / HALF,
516                 childOffsetY + borderOffset);
517             path.LineTo(arrowTopOffset + arrowOffsetsFromClip_[P2INDEX][0] + borderOffset,
518                 childOffsetY + arrowOffsetsFromClip_[P2INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx());
519             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
520                 RSPathDirection::CW_DIRECTION, arrowTopOffset + arrowOffsetsFromClip_[P3INDEX][0],
521                 childOffsetY + arrowOffsetsFromClip_[P3INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx() + borderOffset);
522             path.LineTo(arrowTopOffset + arrowOffsetsFromClip_[P4INDEX][0],
523                 childOffsetY + borderOffset);
524             break;
525         default:
526             break;
527     }
528     path.LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY + borderOffset);
529 }
530 
BuildCornerPath(RSPath & path,const Placement & placement,float radius)531 void BubblePaintMethod::BuildCornerPath(RSPath& path, const Placement& placement, float radius)
532 {
533     float borderOffset = GetBorderOffset();
534     float childOffsetY = childOffset_.GetY();
535     switch (placement) {
536         case Placement::TOP_LEFT:
537             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
538                 childOffset_.GetX() + radius, childOffsetY + borderOffset);
539             break;
540         case Placement::TOP_RIGHT:
541             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
542                 childOffset_.GetX() + childSize_.Width() - borderOffset, childOffsetY + radius);
543             break;
544         case Placement::BOTTOM_RIGHT:
545             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
546                 childOffset_.GetX() + childSize_.Width() - radius,
547                 childOffsetY + childSize_.Height() - borderOffset);
548             break;
549         case Placement::BOTTOM_LEFT:
550             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
551                 childOffset_.GetX() + borderOffset,
552                 childOffsetY + childSize_.Height() - radius - borderOffset);
553             break;
554         default:
555             break;
556     }
557 }
558 
BuildRightLinePath(RSPath & path,float arrowOffset,float radius)559 void BubblePaintMethod::BuildRightLinePath(RSPath& path, float arrowOffset, float radius)
560 {
561     float borderOffset = GetBorderOffset();
562     float childOffsetY = childOffset_.GetY();
563     float arrowPositionY = arrowPosition_.GetY();
564     switch (arrowPlacement_) {
565         case Placement::LEFT:
566         case Placement::LEFT_TOP:
567         case Placement::LEFT_BOTTOM:
568             path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
569                 arrowPositionY + arrowOffset - BEZIER_WIDTH_HALF.ConvertToPx() + borderOffset);
570             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
571                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
572                 arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset,
573                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
574             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
575                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
576                 arrowPosition_.GetX() - borderOffset, arrowPositionY + arrowOffset);
577             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
578                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
579                 arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset,
580                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
581             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
582                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
583                 arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
584                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx());
585             break;
586         default:
587             break;
588     }
589     path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
590         childOffsetY + childSize_.Height() - radius - borderOffset);
591 }
592 
BuildRightDoubleBorderPath(RSPath & path,float radius)593 void BubblePaintMethod::BuildRightDoubleBorderPath(RSPath& path, float radius)
594 {
595     float borderOffset = GetBorderOffset();
596     float childOffsetY = childOffset_.GetY();
597     float arrowRightOffset = childOffset_.GetY() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
598     switch (arrowPlacement_) {
599         case Placement::LEFT:
600         case Placement::LEFT_TOP:
601         case Placement::LEFT_BOTTOM:
602             path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
603                 arrowRightOffset + arrowOffsetsFromClip_[P1INDEX][1] + borderOffset / HALF);
604             path.LineTo(childOffset_.GetX() + arrowOffsetsFromClip_[P2INDEX][0] - BUBBLE_ARROW_HEIGHT.ConvertToPx()
605                 - borderOffset, arrowRightOffset + arrowOffsetsFromClip_[P2INDEX][1]);
606             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
607                 RSPathDirection::CW_DIRECTION, childOffset_.GetX() + arrowOffsetsFromClip_[P3INDEX][0]
608                 - BUBBLE_ARROW_HEIGHT.ConvertToPx() - borderOffset,
609                 arrowRightOffset + arrowOffsetsFromClip_[P3INDEX][1]);
610             path.LineTo(childOffset_.GetX()+ childSize_.Width() - borderOffset,
611                 arrowRightOffset + arrowOffsetsFromClip_[P4INDEX][1] - borderOffset / HALF);
612             break;
613         default:
614             break;
615     }
616     if (childOffsetY + childSize_.Height() - radius < childOffset_.GetY() + radius) {
617         path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
618             childOffset_.GetY() + radius);
619     } else {
620         path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
621             childOffsetY + childSize_.Height() - radius);
622     }
623 }
624 
BuildBottomLinePath(RSPath & path,float arrowOffset,float radius)625 void BubblePaintMethod::BuildBottomLinePath(RSPath& path, float arrowOffset, float radius)
626 {
627     float borderOffset = GetBorderOffset();
628     float childOffsetY = childOffset_.GetY();
629     float arrowPositionY = arrowPosition_.GetY();
630     auto pipeline = PipelineBase::GetCurrentContext();
631     CHECK_NULL_VOID(pipeline);
632     auto popupTheme = pipeline->GetTheme<PopupTheme>();
633     CHECK_NULL_VOID(popupTheme);
634     auto leftOffset =
635         childOffset_.GetX() + popupTheme->GetRadius().GetX().ConvertToPx() + ARROW_WIDTH.ConvertToPx() / HALF;
636     auto rightOffset = childOffset_.GetX() + childSize_.Width() - popupTheme->GetRadius().GetX().ConvertToPx() -
637                        ARROW_WIDTH.ConvertToPx() / HALF;
638     auto arrowBottomOffset = std::clamp(
639         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
640     switch (arrowPlacement_) {
641         case Placement::TOP:
642         case Placement::TOP_LEFT:
643         case Placement::TOP_RIGHT:
644             path.LineTo(arrowBottomOffset + BEZIER_WIDTH_HALF.ConvertToPx() - borderOffset,
645                 childOffsetY + childSize_.Height() - borderOffset);
646             path.QuadTo(arrowBottomOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
647                 arrowPositionY - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
648                 arrowBottomOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
649                 arrowPositionY - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset);
650             path.QuadTo(arrowBottomOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
651                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
652                 arrowBottomOffset, arrowPositionY - borderOffset);
653             path.QuadTo(arrowBottomOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
654                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
655                 arrowBottomOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
656                 arrowPositionY - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset);
657             path.QuadTo(arrowBottomOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
658                 arrowPositionY - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
659                 arrowBottomOffset - BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx(),
660                 arrowPositionY - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset);
661             break;
662         default:
663             break;
664     }
665     path.LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height() - borderOffset);
666 }
667 
BuildBottomDoubleBorderPath(RSPath & path,float radius)668 void BubblePaintMethod::BuildBottomDoubleBorderPath(RSPath& path, float radius)
669 {
670     float borderOffset = GetBorderOffset();
671     float childOffsetY = childOffset_.GetY();
672     float arrowBottomOffset = childOffset_.GetX() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
673     switch (arrowPlacement_) {
674         case Placement::TOP:
675         case Placement::TOP_LEFT:
676         case Placement::TOP_RIGHT:
677             path.LineTo(arrowBottomOffset + arrowOffsetsFromClip_[P1INDEX][0],
678                 childOffsetY + childSize_.Height() - borderOffset);
679             path.LineTo(arrowBottomOffset + arrowOffsetsFromClip_[P2INDEX][0],
680                 childOffsetY + arrowOffsetsFromClip_[P2INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx() - borderOffset);
681             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
682                 RSPathDirection::CW_DIRECTION, arrowBottomOffset + arrowOffsetsFromClip_[P3INDEX][0],
683                 childOffsetY + arrowOffsetsFromClip_[P3INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx() - borderOffset);
684             path.LineTo(arrowBottomOffset + arrowOffsetsFromClip_[P4INDEX][0],
685                 childOffsetY + childSize_.Height() - borderOffset);
686             break;
687         default:
688             break;
689     }
690     path.LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height() - borderOffset);
691 }
692 
BuildLeftDoubleBorderPath(RSPath & path,float radius)693 void BubblePaintMethod::BuildLeftDoubleBorderPath(RSPath& path, float radius)
694 {
695     float borderOffset = GetBorderOffset();
696     float childOffsetY = childOffset_.GetY();
697     float arrowLeftOffset = childOffset_.GetY() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
698     switch (arrowPlacement_) {
699         case Placement::RIGHT:
700         case Placement::RIGHT_TOP:
701         case Placement::RIGHT_BOTTOM:
702             path.LineTo(childOffset_.GetX() + borderOffset,
703                 arrowLeftOffset + arrowOffsetsFromClip_[P1INDEX][1]);
704             path.LineTo(childOffset_.GetX() + arrowOffsetsFromClip_[P2INDEX][0] - BUBBLE_ARROW_HEIGHT.ConvertToPx()
705                 + borderOffset, arrowLeftOffset + arrowOffsetsFromClip_[P2INDEX][1]);
706             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
707                 RSPathDirection::CW_DIRECTION, childOffset_.GetX() + arrowOffsetsFromClip_[P3INDEX][0]
708                 - BUBBLE_ARROW_HEIGHT.ConvertToPx() + borderOffset,
709                 arrowLeftOffset + arrowOffsetsFromClip_[P3INDEX][1]);
710             path.LineTo(childOffset_.GetX() + borderOffset, arrowLeftOffset + arrowOffsetsFromClip_[P4INDEX][1]);
711             break;
712         default:
713             break;
714     }
715     path.LineTo(childOffset_.GetX() + borderOffset, childOffsetY + radius + borderOffset);
716 }
717 
BuildLeftLinePath(RSPath & path,float arrowOffset,float radius)718 void BubblePaintMethod::BuildLeftLinePath(RSPath& path, float arrowOffset, float radius)
719 {
720     float borderOffset = GetBorderOffset();
721     float childOffsetY = childOffset_.GetY();
722     float arrowPositionY = arrowPosition_.GetY();
723     switch (arrowPlacement_) {
724         case Placement::RIGHT:
725         case Placement::RIGHT_TOP:
726         case Placement::RIGHT_BOTTOM:
727             path.LineTo(childOffset_.GetX() + borderOffset,
728                 arrowPositionY + arrowOffset + BEZIER_WIDTH_HALF.ConvertToPx() - borderOffset);
729             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
730                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
731                 arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset,
732                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
733             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
734                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx() + borderOffset,
735                 arrowPosition_.GetX(), arrowPositionY + arrowOffset);
736             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
737                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
738                 arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset,
739                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
740             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
741                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
742                 arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
743                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx());
744             break;
745         default:
746             break;
747     }
748     path.LineTo(childOffset_.GetX() + borderOffset, childOffsetY + radius + borderOffset);
749 }
750 
GetArrowOffset(const Placement & placement)751 float BubblePaintMethod::GetArrowOffset(const Placement& placement)
752 {
753     double motionRange = 0.0;
754     Edge edge;
755     InitEdgeSize(edge);
756     switch (placement) {
757         case Placement::TOP_LEFT:
758         case Placement::TOP_RIGHT:
759             motionRange = childSize_.Width() - edge.Top().Value() - ARROW_WIDTH.ConvertToPx();
760             break;
761         case Placement::TOP:
762             motionRange = childSize_.Width() - edge.Top().Value() - ARROW_WIDTH.ConvertToPx();
763             break;
764         case Placement::BOTTOM:
765             motionRange = childSize_.Width() - edge.Bottom().Value() - ARROW_WIDTH.ConvertToPx();
766             break;
767         case Placement::LEFT:
768         case Placement::LEFT_TOP:
769         case Placement::LEFT_BOTTOM:
770             motionRange = childSize_.Height() - edge.Left().Value() - ARROW_WIDTH.ConvertToPx();
771             break;
772         case Placement::RIGHT:
773         case Placement::RIGHT_TOP:
774         case Placement::RIGHT_BOTTOM:
775             motionRange = childSize_.Height() - edge.Right().Value() - ARROW_WIDTH.ConvertToPx();
776             break;
777         case Placement::BOTTOM_LEFT:
778         case Placement::BOTTOM_RIGHT:
779             motionRange = childSize_.Width() - edge.Bottom().Value() - ARROW_WIDTH.ConvertToPx();
780             break;
781         default:
782             break;
783     }
784     return std::clamp(
785         arrowOffset_.Unit() == DimensionUnit::PERCENT ? arrowOffset_.Value() * motionRange : arrowOffset_.ConvertToPx(),
786         0.0, motionRange);
787 }
788 
InitEdgeSize(Edge & edge)789 void BubblePaintMethod::InitEdgeSize(Edge& edge)
790 {
791     edge.SetTop(Dimension(std::max(padding_.Left().ConvertToPx(), border_.TopLeftRadius().GetX().ConvertToPx()) +
792                           std::max(padding_.Right().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx())));
793     edge.SetBottom(
794         Dimension(std::max(padding_.Left().ConvertToPx(), border_.BottomLeftRadius().GetX().ConvertToPx()) +
795                   std::max(padding_.Right().ConvertToPx(), border_.BottomRightRadius().GetX().ConvertToPx())));
796     edge.SetLeft(
797         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx()) +
798                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx())));
799     edge.SetRight(
800         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx()) +
801                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx())));
802 }
803 
ClipBubbleWithPath(const RefPtr<FrameNode> & frameNode)804 void BubblePaintMethod::ClipBubbleWithPath(const RefPtr<FrameNode>& frameNode)
805 {
806     auto geometryNode = frameNode->GetGeometryNode();
807     CHECK_NULL_VOID(geometryNode);
808     auto frameNodeSize = geometryNode->GetFrameSize();
809     CHECK_NULL_VOID(frameNodeSize.IsPositive());
810     auto path = AceType::MakeRefPtr<Path>();
811     path->SetValue(clipPath_);
812     path->SetBasicShapeType(BasicShapeType::PATH);
813     auto renderContext = frameNode->GetRenderContext();
814     CHECK_NULL_VOID(renderContext);
815     renderContext->UpdateClipShape(path);
816 }
817 
818 } // namespace OHOS::Ace::NG
819