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