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