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