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