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