• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     border_.SetBorderRadius(popupTheme->GetRadius());
224     padding_ = popupTheme->GetPadding();
225     return enableArrow_ && showArrow_ && popupTheme->GetPopupDoubleBorderEnable();
226 }
227 
PaintOuterBorder(RSCanvas & canvas,PaintWrapper * paintWrapper)228 void BubblePaintMethod::PaintOuterBorder(RSCanvas& canvas, PaintWrapper* paintWrapper)
229 {
230     if (!IsPaintDoubleBorder(paintWrapper)) {
231         return;
232     }
233     auto pipelineContext = PipelineContext::GetCurrentContext();
234     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
235     RSPen paint;
236     RSFilter filter;
237     filter.SetMaskFilter(RSMaskFilter::CreateBlurMaskFilter(RSBlurType::SOLID, BLUR_MASK_FILTER));
238     paint.SetFilter(filter);
239     paint.SetAntiAlias(true);
240     paint.SetWidth(outerBorderWidth_);
241     paint.SetColor(popupTheme->GetPopupOuterBorderColor().GetValue());
242     canvas.AttachPen(paint);
243     needPaintOuterBorder_ = true;
244     PaintDoubleBorderWithArrow(canvas, paintWrapper);
245     canvas.DetachPen();
246     needPaintOuterBorder_ = false;
247 }
248 
PaintInnerBorder(RSCanvas & canvas,PaintWrapper * paintWrapper)249 void BubblePaintMethod::PaintInnerBorder(RSCanvas& canvas, PaintWrapper* paintWrapper)
250 {
251     if (!IsPaintDoubleBorder(paintWrapper)) {
252         return;
253     }
254     auto pipelineContext = PipelineContext::GetCurrentContext();
255     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
256     RSPen paint;
257     RSFilter filter;
258     filter.SetMaskFilter(RSMaskFilter::CreateBlurMaskFilter(RSBlurType::SOLID, BLUR_MASK_FILTER));
259     paint.SetFilter(filter);
260     paint.SetAntiAlias(true);
261     paint.SetWidth(innerBorderWidth_);
262     paint.SetColor(popupTheme->GetPopupInnerBorderColor().GetValue());
263     canvas.AttachPen(paint);
264     PaintDoubleBorderWithArrow(canvas, paintWrapper);
265     canvas.DetachPen();
266 }
267 
ClipBubble(PaintWrapper * paintWrapper)268 void BubblePaintMethod::ClipBubble(PaintWrapper* paintWrapper)
269 {
270     CHECK_NULL_VOID(paintWrapper);
271     auto paintProperty = DynamicCast<BubbleRenderProperty>(paintWrapper->GetPaintProperty());
272     CHECK_NULL_VOID(paintProperty);
273     enableArrow_ = paintProperty->GetEnableArrow().value_or(true);
274     auto pipelineContext = PipelineContext::GetCurrentContext();
275     CHECK_NULL_VOID(pipelineContext);
276     auto popupTheme = pipelineContext->GetTheme<PopupTheme>();
277     CHECK_NULL_VOID(popupTheme);
278     border_.SetBorderRadius(popupTheme->GetRadius());
279     if (clipFrameNode_) {
280         ClipBubbleWithPath(clipFrameNode_);
281     }
282 }
283 
UpdateArrowOffset(const std::optional<Dimension> & offset,const Placement & placement)284 void BubblePaintMethod::UpdateArrowOffset(const std::optional<Dimension>& offset, const Placement& placement)
285 {
286     if (offset.has_value()) {
287         arrowOffset_ = offset.value();
288         if (arrowOffset_.Unit() == DimensionUnit::PERCENT) {
289             arrowOffset_.SetValue(std::clamp(arrowOffset_.Value(), 0.0, 1.0));
290         }
291         return;
292     }
293     switch (placement) {
294         case Placement::LEFT:
295         case Placement::RIGHT:
296         case Placement::TOP:
297         case Placement::BOTTOM:
298             arrowOffset_ = ARROW_HALF_PERCENT_VALUE;
299             break;
300         case Placement::TOP_LEFT:
301         case Placement::BOTTOM_LEFT:
302         case Placement::LEFT_TOP:
303         case Placement::RIGHT_TOP:
304             arrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
305             break;
306         case Placement::TOP_RIGHT:
307         case Placement::BOTTOM_RIGHT:
308         case Placement::LEFT_BOTTOM:
309         case Placement::RIGHT_BOTTOM:
310             arrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
311             break;
312         default:
313             break;
314     }
315 }
316 
PaintShadow(const RSPath & path,const Shadow & shadow,RSCanvas & canvas)317 void BubblePaintMethod::PaintShadow(const RSPath& path, const Shadow& shadow, RSCanvas& canvas)
318 {
319     canvas.Save();
320 #ifndef USE_ROSEN_DRAWING
321     RSPath rsPath = path;
322 #else
323     RSRecordingPath rsPath;
324     rsPath.AddPath(path);
325 #endif
326     rsPath.Offset(shadow.GetOffset().GetX(), shadow.GetOffset().GetY());
327     RSColor spotColor = ToRSColor(shadow.GetColor());
328     RSPoint3 planeParams = { 0.0f, 0.0f, shadow.GetElevation() };
329 #ifndef USE_ROSEN_DRAWING
330     RSPoint3 lightPos = { rsPath.GetBounds().GetLeft() / 2.0 + rsPath.GetBounds().GetRight() / 2.0,
331         rsPath.GetBounds().GetTop() / 2.0 + rsPath.GetBounds().GetBottom() / 2.0, shadow.GetLightHeight() };
332 #else
333     auto bounds = rsPath.GetBounds();
334     RSPoint3 lightPos = { bounds.GetLeft() / 2.0 + bounds.GetRight() / 2.0,
335         bounds.GetTop() / 2.0 + bounds.GetBottom() / 2.0, shadow.GetLightHeight() };
336 #endif
337     RSColor ambientColor = RSColor(0, 0, 0, 0);
338     canvas.DrawShadow(rsPath, planeParams, lightPos, shadow.GetLightRadius(), ambientColor, spotColor,
339         RSShadowFlags::TRANSPARENT_OCCLUDER);
340     canvas.Restore();
341 }
342 
PaintDefaultBubble(RSCanvas & canvas)343 void BubblePaintMethod::PaintDefaultBubble(RSCanvas& canvas)
344 {
345     auto rect = MakeRRect();
346     path_.AddRoundRect(
347         rect.GetRect(), border_.TopLeftRadius().GetX().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx());
348     canvas.Save();
349     canvas.ClipPath(path_, RSClipOp::DIFFERENCE, true);
350     PaintShadow(path_, ShadowConfig::DefaultShadowM, canvas);
351     canvas.Restore();
352     canvas.DrawRoundRect(rect);
353 }
354 
MakeRRect()355 RSRoundRect BubblePaintMethod::MakeRRect()
356 {
357     auto rect = RSRect(childOffset_.GetX(), childOffset_.GetY(), childOffset_.GetX() + childSize_.Width(),
358         childOffset_.GetY() + childSize_.Height());
359     std::vector<RSPoint> rectRadii;
360     rectRadii.resize(4);
361     rectRadii[RSRoundRect::TOP_LEFT_POS] =
362         RSPoint(border_.TopLeftRadius().GetX().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx());
363     rectRadii[RSRoundRect::TOP_RIGHT_POS] =
364         RSPoint(border_.TopRightRadius().GetX().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx());
365     rectRadii[RSRoundRect::BOTTOM_RIGHT_POS] =
366         RSPoint(border_.BottomRightRadius().GetX().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx());
367     rectRadii[RSRoundRect::BOTTOM_LEFT_POS] =
368         RSPoint(border_.BottomLeftRadius().GetX().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx());
369     return RSRoundRect(rect, rectRadii);
370 }
371 
PaintBubbleWithArrow(RSCanvas & canvas,PaintWrapper * paintWrapper)372 void BubblePaintMethod::PaintBubbleWithArrow(RSCanvas& canvas, PaintWrapper* paintWrapper)
373 {
374     BuildCompletePath(path_);
375     canvas.Save();
376     canvas.ClipPath(path_, RSClipOp::DIFFERENCE, true);
377     if (!needPaintOuterBorder_) {
378         PaintShadow(path_, ShadowConfig::DefaultShadowM, canvas);
379     }
380     canvas.Restore();
381     canvas.DrawPath(path_);
382 }
383 
PaintDoubleBorderWithArrow(RSCanvas & canvas,PaintWrapper * paintWrapper)384 void BubblePaintMethod::PaintDoubleBorderWithArrow(RSCanvas& canvas, PaintWrapper* paintWrapper)
385 {
386     BuildDoubleBorderPath(path_);
387     canvas.Save();
388     canvas.Restore();
389     canvas.DrawPath(path_);
390 }
391 
GetInnerBorderOffset()392 float BubblePaintMethod::GetInnerBorderOffset()
393 {
394     float borderOffset = 0;
395     auto pipeline = PipelineBase::GetCurrentContext();
396     CHECK_NULL_RETURN(pipeline, 0);
397     auto popupTheme = pipeline->GetTheme<PopupTheme>();
398     CHECK_NULL_RETURN(popupTheme, 0);
399     if (popupTheme->GetPopupDoubleBorderEnable() && needPaintOuterBorder_) {
400         borderOffset = outerBorderWidth_;
401     }
402     return borderOffset;
403 }
404 
GetBorderOffset()405 float BubblePaintMethod::GetBorderOffset()
406 {
407     float borderOffset = 0.0f;
408     auto pipeline = PipelineBase::GetCurrentContext();
409     CHECK_NULL_RETURN(pipeline, 0);
410     auto popupTheme = pipeline->GetTheme<PopupTheme>();
411     CHECK_NULL_RETURN(popupTheme, 0);
412     if (popupTheme->GetPopupDoubleBorderEnable()) {
413         if (needPaintOuterBorder_) {
414             borderOffset = -outerBorderWidth_;
415         } else {
416             borderOffset = innerBorderWidth_;
417         }
418     }
419     return borderOffset;
420 }
421 
BuildCompletePath(RSPath & path)422 void BubblePaintMethod::BuildCompletePath(RSPath& path)
423 {
424     float borderOffset = GetBorderOffset();
425     float arrowOffset = GetArrowOffset(arrowPlacement_);
426     auto borderRadius = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Height() / 2);
427     float radiusPx = borderRadius - borderOffset;
428     path.Reset();
429     path.MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY() + borderOffset);
430     BuildTopLinePath(path, arrowOffset, radiusPx);
431     BuildCornerPath(path, Placement::TOP_RIGHT, radiusPx);
432     BuildRightLinePath(path, arrowOffset, radiusPx);
433     BuildCornerPath(path, Placement::BOTTOM_RIGHT, radiusPx);
434     BuildBottomLinePath(path, arrowOffset, radiusPx);
435     BuildCornerPath(path, Placement::BOTTOM_LEFT, radiusPx);
436     BuildLeftLinePath(path, arrowOffset, radiusPx);
437     BuildCornerPath(path, Placement::TOP_LEFT, radiusPx);
438     path.Close();
439 }
440 
BuildDoubleBorderPath(RSPath & path)441 void BubblePaintMethod::BuildDoubleBorderPath(RSPath& path)
442 {
443     float borderOffset = GetBorderOffset();
444     auto borderRadius = ModifyBorderRadius(border_.BottomLeftRadius().GetY().ConvertToPx(), childSize_.Height() / 2);
445     float radiusPx = borderRadius - borderOffset;
446     path.Reset();
447     path.MoveTo(childOffset_.GetX() + radiusPx, childOffset_.GetY() + borderOffset);
448     BuildTopDoubleBorderPath(path, radiusPx);
449     BuildCornerPath(path, Placement::TOP_RIGHT, radiusPx);
450     BuildRightDoubleBorderPath(path, radiusPx);
451     BuildCornerPath(path, Placement::BOTTOM_RIGHT, radiusPx);
452     BuildBottomDoubleBorderPath(path, radiusPx);
453     BuildCornerPath(path, Placement::BOTTOM_LEFT, radiusPx);
454     BuildLeftDoubleBorderPath(path, radiusPx);
455     BuildCornerPath(path, Placement::TOP_LEFT, radiusPx);
456     path.Close();
457 }
458 
BuildTopLinePath(RSPath & path,float arrowOffset,float radius)459 void BubblePaintMethod::BuildTopLinePath(RSPath& path, float arrowOffset, float radius)
460 {
461     float borderOffset = GetBorderOffset();
462     float childOffsetY = childOffset_.GetY();
463     float arrowPositionY = arrowPosition_.GetY();
464     auto pipeline = PipelineBase::GetCurrentContext();
465     CHECK_NULL_VOID(pipeline);
466     auto popupTheme = pipeline->GetTheme<PopupTheme>();
467     CHECK_NULL_VOID(popupTheme);
468     auto leftOffset =
469         childOffset_.GetX() + popupTheme->GetRadius().GetX().ConvertToPx() + ARROW_WIDTH.ConvertToPx() / 2;
470     auto rightOffset = childOffset_.GetX() + childSize_.Width() - popupTheme->GetRadius().GetX().ConvertToPx() -
471                        ARROW_WIDTH.ConvertToPx() / 2;
472     auto arrowTopOffset = std::clamp(
473         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
474     switch (arrowPlacement_) {
475         case Placement::BOTTOM:
476         case Placement::BOTTOM_LEFT:
477         case Placement::BOTTOM_RIGHT:
478             path.LineTo(arrowTopOffset - BEZIER_WIDTH_HALF.ConvertToPx() + borderOffset,
479                 childOffsetY + borderOffset);
480             path.QuadTo(arrowTopOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
481                 arrowPositionY + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
482                 arrowTopOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
483                 arrowPositionY + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset);
484             path.QuadTo(arrowTopOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
485                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
486                 arrowTopOffset, arrowPositionY + borderOffset);
487             path.QuadTo(arrowTopOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
488                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
489                 arrowTopOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
490                 arrowPositionY + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset);
491             path.QuadTo(arrowTopOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
492                 arrowPositionY + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
493                 arrowTopOffset + BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx(),
494                 arrowPositionY + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset);
495             break;
496         default:
497             break;
498     }
499     path.LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY + borderOffset);
500 }
501 
BuildTopDoubleBorderPath(RSPath & path,float radius)502 void BubblePaintMethod::BuildTopDoubleBorderPath(RSPath& path, float radius)
503 {
504     float borderOffset = 0.0f;
505     if (needPaintOuterBorder_) {
506         borderOffset = -outerBorderWidth_;
507     } else {
508         borderOffset = innerBorderWidth_ / HALF;
509     }
510     float childOffsetY = childOffset_.GetY();
511     float arrowTopOffset = childOffset_.GetX() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
512     switch (arrowPlacement_) {
513         case Placement::BOTTOM:
514         case Placement::BOTTOM_LEFT:
515         case Placement::BOTTOM_RIGHT:
516             borderOffset = GetBorderOffset();
517             path.LineTo(arrowTopOffset + arrowOffsetsFromClip_[P1INDEX][0] + borderOffset / HALF,
518                 childOffsetY + borderOffset);
519             path.LineTo(arrowTopOffset + arrowOffsetsFromClip_[P2INDEX][0] + borderOffset,
520                 childOffsetY + arrowOffsetsFromClip_[P2INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx());
521             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
522                 RSPathDirection::CW_DIRECTION, arrowTopOffset + arrowOffsetsFromClip_[P3INDEX][0],
523                 childOffsetY + arrowOffsetsFromClip_[P3INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx() + borderOffset);
524             path.LineTo(arrowTopOffset + arrowOffsetsFromClip_[P4INDEX][0],
525                 childOffsetY + borderOffset);
526             break;
527         default:
528             break;
529     }
530     path.LineTo(childOffset_.GetX() + childSize_.Width() - radius, childOffsetY + borderOffset);
531 }
532 
BuildCornerPath(RSPath & path,const Placement & placement,float radius)533 void BubblePaintMethod::BuildCornerPath(RSPath& path, const Placement& placement, float radius)
534 {
535     float borderOffset = GetBorderOffset();
536     float childOffsetY = childOffset_.GetY();
537     switch (placement) {
538         case Placement::TOP_LEFT:
539             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
540                 childOffset_.GetX() + radius, childOffsetY + borderOffset);
541             break;
542         case Placement::TOP_RIGHT:
543             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
544                 childOffset_.GetX() + childSize_.Width() - borderOffset, childOffsetY + radius);
545             break;
546         case Placement::BOTTOM_RIGHT:
547             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
548                 childOffset_.GetX() + childSize_.Width() - radius,
549                 childOffsetY + childSize_.Height() - borderOffset);
550             break;
551         case Placement::BOTTOM_LEFT:
552             path.ArcTo(radius, radius, 0.0f, RSPathDirection::CW_DIRECTION,
553                 childOffset_.GetX() + borderOffset,
554                 childOffsetY + childSize_.Height() - radius - borderOffset);
555             break;
556         default:
557             break;
558     }
559 }
560 
BuildRightLinePath(RSPath & path,float arrowOffset,float radius)561 void BubblePaintMethod::BuildRightLinePath(RSPath& path, float arrowOffset, float radius)
562 {
563     float borderOffset = GetBorderOffset();
564     float childOffsetY = childOffset_.GetY();
565     float arrowPositionY = arrowPosition_.GetY();
566     switch (arrowPlacement_) {
567         case Placement::LEFT:
568         case Placement::LEFT_TOP:
569         case Placement::LEFT_BOTTOM:
570             path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
571                 arrowPositionY + arrowOffset - BEZIER_WIDTH_HALF.ConvertToPx() + borderOffset);
572             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
573                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
574                 arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset,
575                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
576             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
577                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
578                 arrowPosition_.GetX() - borderOffset, arrowPositionY + arrowOffset);
579             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
580                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
581                 arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset,
582                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
583             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
584                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
585                 arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
586                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx());
587             break;
588         default:
589             break;
590     }
591     path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
592         childOffsetY + childSize_.Height() - radius - borderOffset);
593 }
594 
BuildRightDoubleBorderPath(RSPath & path,float radius)595 void BubblePaintMethod::BuildRightDoubleBorderPath(RSPath& path, float radius)
596 {
597     float borderOffset = GetBorderOffset();
598     float childOffsetY = childOffset_.GetY();
599     float arrowRightOffset = childOffset_.GetY() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
600     switch (arrowPlacement_) {
601         case Placement::LEFT:
602         case Placement::LEFT_TOP:
603         case Placement::LEFT_BOTTOM:
604             path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
605                 arrowRightOffset + arrowOffsetsFromClip_[P1INDEX][1] + borderOffset / HALF);
606             path.LineTo(childOffset_.GetX() + arrowOffsetsFromClip_[P2INDEX][0] - BUBBLE_ARROW_HEIGHT.ConvertToPx()
607                 - borderOffset, arrowRightOffset + arrowOffsetsFromClip_[P2INDEX][1]);
608             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
609                 RSPathDirection::CW_DIRECTION, childOffset_.GetX() + arrowOffsetsFromClip_[P3INDEX][0]
610                 - BUBBLE_ARROW_HEIGHT.ConvertToPx() - borderOffset,
611                 arrowRightOffset + arrowOffsetsFromClip_[P3INDEX][1]);
612             path.LineTo(childOffset_.GetX()+ childSize_.Width() - borderOffset,
613                 arrowRightOffset + arrowOffsetsFromClip_[P4INDEX][1] - borderOffset / HALF);
614             break;
615         default:
616             break;
617     }
618     if (childOffsetY + childSize_.Height() - radius < childOffset_.GetY() + radius) {
619         path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
620             childOffset_.GetY() + radius);
621     } else {
622         path.LineTo(childOffset_.GetX() + childSize_.Width() - borderOffset,
623             childOffsetY + childSize_.Height() - radius);
624     }
625 }
626 
BuildBottomLinePath(RSPath & path,float arrowOffset,float radius)627 void BubblePaintMethod::BuildBottomLinePath(RSPath& path, float arrowOffset, float radius)
628 {
629     float borderOffset = GetBorderOffset();
630     float childOffsetY = childOffset_.GetY();
631     float arrowPositionY = arrowPosition_.GetY();
632     auto pipeline = PipelineBase::GetCurrentContext();
633     CHECK_NULL_VOID(pipeline);
634     auto popupTheme = pipeline->GetTheme<PopupTheme>();
635     CHECK_NULL_VOID(popupTheme);
636     auto leftOffset =
637         childOffset_.GetX() + popupTheme->GetRadius().GetX().ConvertToPx() + ARROW_WIDTH.ConvertToPx() / HALF;
638     auto rightOffset = childOffset_.GetX() + childSize_.Width() - popupTheme->GetRadius().GetX().ConvertToPx() -
639                        ARROW_WIDTH.ConvertToPx() / HALF;
640     auto arrowBottomOffset = std::clamp(
641         arrowPosition_.GetX() + arrowOffset, static_cast<float>(leftOffset), static_cast<float>(rightOffset));
642     switch (arrowPlacement_) {
643         case Placement::TOP:
644         case Placement::TOP_LEFT:
645         case Placement::TOP_RIGHT:
646             path.LineTo(arrowBottomOffset + BEZIER_WIDTH_HALF.ConvertToPx() - borderOffset,
647                 childOffsetY + childSize_.Height() - borderOffset);
648             path.QuadTo(arrowBottomOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
649                 arrowPositionY - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
650                 arrowBottomOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
651                 arrowPositionY - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset);
652             path.QuadTo(arrowBottomOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
653                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
654                 arrowBottomOffset, arrowPositionY - borderOffset);
655             path.QuadTo(arrowBottomOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
656                 arrowPositionY - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() - borderOffset,
657                 arrowBottomOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx(),
658                 arrowPositionY - BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() - borderOffset);
659             path.QuadTo(arrowBottomOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
660                 arrowPositionY - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset,
661                 arrowBottomOffset - BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx(),
662                 arrowPositionY - BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() - borderOffset);
663             break;
664         default:
665             break;
666     }
667     path.LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height() - borderOffset);
668 }
669 
BuildBottomDoubleBorderPath(RSPath & path,float radius)670 void BubblePaintMethod::BuildBottomDoubleBorderPath(RSPath& path, float radius)
671 {
672     float borderOffset = GetBorderOffset();
673     float childOffsetY = childOffset_.GetY();
674     float arrowBottomOffset = childOffset_.GetX() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
675     switch (arrowPlacement_) {
676         case Placement::TOP:
677         case Placement::TOP_LEFT:
678         case Placement::TOP_RIGHT:
679             path.LineTo(arrowBottomOffset + arrowOffsetsFromClip_[P1INDEX][0],
680                 childOffsetY + childSize_.Height() - borderOffset);
681             path.LineTo(arrowBottomOffset + arrowOffsetsFromClip_[P2INDEX][0],
682                 childOffsetY + arrowOffsetsFromClip_[P2INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx() - borderOffset);
683             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
684                 RSPathDirection::CW_DIRECTION, arrowBottomOffset + arrowOffsetsFromClip_[P3INDEX][0],
685                 childOffsetY + arrowOffsetsFromClip_[P3INDEX][1] - BUBBLE_ARROW_HEIGHT.ConvertToPx() - borderOffset);
686             path.LineTo(arrowBottomOffset + arrowOffsetsFromClip_[P4INDEX][0],
687                 childOffsetY + childSize_.Height() - borderOffset);
688             break;
689         default:
690             break;
691     }
692     path.LineTo(childOffset_.GetX() + radius, childOffsetY + childSize_.Height() - borderOffset);
693 }
694 
BuildLeftDoubleBorderPath(RSPath & path,float radius)695 void BubblePaintMethod::BuildLeftDoubleBorderPath(RSPath& path, float radius)
696 {
697     float borderOffset = GetBorderOffset();
698     float childOffsetY = childOffset_.GetY();
699     float arrowLeftOffset = childOffset_.GetY() - BUBBLE_ARROW_WIDTH.ConvertToPx() / HALF;
700     switch (arrowPlacement_) {
701         case Placement::RIGHT:
702         case Placement::RIGHT_TOP:
703         case Placement::RIGHT_BOTTOM:
704             path.LineTo(childOffset_.GetX() + borderOffset,
705                 arrowLeftOffset + arrowOffsetsFromClip_[P1INDEX][1]);
706             path.LineTo(childOffset_.GetX() + arrowOffsetsFromClip_[P2INDEX][0] - BUBBLE_ARROW_HEIGHT.ConvertToPx()
707                 + borderOffset, arrowLeftOffset + arrowOffsetsFromClip_[P2INDEX][1]);
708             path.ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f,
709                 RSPathDirection::CW_DIRECTION, childOffset_.GetX() + arrowOffsetsFromClip_[P3INDEX][0]
710                 - BUBBLE_ARROW_HEIGHT.ConvertToPx() + borderOffset,
711                 arrowLeftOffset + arrowOffsetsFromClip_[P3INDEX][1]);
712             path.LineTo(childOffset_.GetX() + borderOffset, arrowLeftOffset + arrowOffsetsFromClip_[P4INDEX][1]);
713             break;
714         default:
715             break;
716     }
717     path.LineTo(childOffset_.GetX() + borderOffset, childOffsetY + radius + borderOffset);
718 }
719 
BuildLeftLinePath(RSPath & path,float arrowOffset,float radius)720 void BubblePaintMethod::BuildLeftLinePath(RSPath& path, float arrowOffset, float radius)
721 {
722     float borderOffset = GetBorderOffset();
723     float childOffsetY = childOffset_.GetY();
724     float arrowPositionY = arrowPosition_.GetY();
725     switch (arrowPlacement_) {
726         case Placement::RIGHT:
727         case Placement::RIGHT_TOP:
728         case Placement::RIGHT_BOTTOM:
729             path.LineTo(childOffset_.GetX() + borderOffset,
730                 arrowPositionY + arrowOffset + BEZIER_WIDTH_HALF.ConvertToPx() - borderOffset);
731             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
732                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
733                 arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset,
734                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
735             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
736                 arrowPositionY + arrowOffset + BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx() + borderOffset,
737                 arrowPosition_.GetX(), arrowPositionY + arrowOffset);
738             path.QuadTo(arrowPosition_.GetX() - BEZIER_VERTICAL_OFFSET_FIRST.ConvertToPx() + borderOffset,
739                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_FIRST.ConvertToPx(),
740                 arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_SECOND.ConvertToPx() + borderOffset,
741                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_SECOND.ConvertToPx());
742             path.QuadTo(arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
743                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_THIRD.ConvertToPx(),
744                 arrowPosition_.GetX() + BEZIER_VERTICAL_OFFSET_THIRD.ConvertToPx() + borderOffset,
745                 arrowPositionY + arrowOffset - BEZIER_HORIZON_OFFSET_FOURTH.ConvertToPx());
746             break;
747         default:
748             break;
749     }
750     path.LineTo(childOffset_.GetX() + borderOffset, childOffsetY + radius + borderOffset);
751 }
752 
GetArrowOffset(const Placement & placement)753 float BubblePaintMethod::GetArrowOffset(const Placement& placement)
754 {
755     double motionRange = 0.0;
756     Edge edge;
757     InitEdgeSize(edge);
758     switch (placement) {
759         case Placement::TOP_LEFT:
760         case Placement::TOP_RIGHT:
761             motionRange = childSize_.Width() - edge.Top().Value() - ARROW_WIDTH.ConvertToPx();
762             break;
763         case Placement::TOP:
764             motionRange = childSize_.Width() - edge.Top().Value() - ARROW_WIDTH.ConvertToPx();
765             break;
766         case Placement::BOTTOM:
767             motionRange = childSize_.Width() - edge.Bottom().Value() - ARROW_WIDTH.ConvertToPx();
768             break;
769         case Placement::LEFT:
770         case Placement::LEFT_TOP:
771         case Placement::LEFT_BOTTOM:
772             motionRange = childSize_.Height() - edge.Left().Value() - ARROW_WIDTH.ConvertToPx();
773             break;
774         case Placement::RIGHT:
775         case Placement::RIGHT_TOP:
776         case Placement::RIGHT_BOTTOM:
777             motionRange = childSize_.Height() - edge.Right().Value() - ARROW_WIDTH.ConvertToPx();
778             break;
779         case Placement::BOTTOM_LEFT:
780         case Placement::BOTTOM_RIGHT:
781             motionRange = childSize_.Width() - edge.Bottom().Value() - ARROW_WIDTH.ConvertToPx();
782             break;
783         default:
784             break;
785     }
786     return std::clamp(
787         arrowOffset_.Unit() == DimensionUnit::PERCENT ? arrowOffset_.Value() * motionRange : arrowOffset_.ConvertToPx(),
788         0.0, motionRange);
789 }
790 
InitEdgeSize(Edge & edge)791 void BubblePaintMethod::InitEdgeSize(Edge& edge)
792 {
793     edge.SetTop(Dimension(std::max(padding_.Left().ConvertToPx(), border_.TopLeftRadius().GetX().ConvertToPx()) +
794                           std::max(padding_.Right().ConvertToPx(), border_.TopRightRadius().GetX().ConvertToPx())));
795     edge.SetBottom(
796         Dimension(std::max(padding_.Left().ConvertToPx(), border_.BottomLeftRadius().GetX().ConvertToPx()) +
797                   std::max(padding_.Right().ConvertToPx(), border_.BottomRightRadius().GetX().ConvertToPx())));
798     edge.SetLeft(
799         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopRightRadius().GetY().ConvertToPx()) +
800                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomRightRadius().GetY().ConvertToPx())));
801     edge.SetRight(
802         Dimension(std::max(padding_.Top().ConvertToPx(), border_.TopLeftRadius().GetY().ConvertToPx()) +
803                   std::max(padding_.Bottom().ConvertToPx(), border_.BottomLeftRadius().GetY().ConvertToPx())));
804 }
805 
ClipBubbleWithPath(const RefPtr<FrameNode> & frameNode)806 void BubblePaintMethod::ClipBubbleWithPath(const RefPtr<FrameNode>& frameNode)
807 {
808     auto path = AceType::MakeRefPtr<Path>();
809     path->SetValue(clipPath_);
810     path->SetBasicShapeType(BasicShapeType::PATH);
811     auto renderContext = frameNode->GetRenderContext();
812     renderContext->UpdateClipShape(path);
813 }
814 
815 } // namespace OHOS::Ace::NG
816