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