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 
16 #include "render/rs_border.h"
17 
18 #include "draw/path.h"
19 #include "platform/common/rs_log.h"
20 
21 namespace OHOS {
22 namespace Rosen {
23 namespace {
24 constexpr int PARAM_DOUBLE = 2;
25 constexpr int32_t DASHED_LINE_LENGTH = 3;
26 constexpr float SWEEP_ANGLE = 60.0f;
27 constexpr float TOP_END = 270.0f;
28 constexpr float TOP_START = TOP_END - SWEEP_ANGLE;
29 constexpr float RIGHT_END = 0.0f;
30 constexpr float RIGHT_START = 360.0f - SWEEP_ANGLE;
31 constexpr float BOTTOM_END = 90.0f;
32 constexpr float BOTTOM_START = BOTTOM_END - SWEEP_ANGLE;
33 constexpr float LEFT_END = 180.0f;
34 constexpr float LEFT_START = LEFT_END - SWEEP_ANGLE;
35 } // namespace
36 
37 // defines short names for widths/half widths of each borders
38 #define LEFTW GetWidth(RSBorder::LEFT)
39 #define LEFTW2 GetWidth(RSBorder::LEFT) / 2.f
40 #define RIGHTW GetWidth(RSBorder::RIGHT)
41 #define RIGHTW2 GetWidth(RSBorder::RIGHT) / 2.f
42 #define TOPW GetWidth(RSBorder::TOP)
43 #define TOPW2 GetWidth(RSBorder::TOP) / 2.f
44 #define BOTTOMW GetWidth(RSBorder::BOTTOM)
45 #define BOTTOMW2 GetWidth(RSBorder::BOTTOM) / 2.f
46 
RSBorder(const bool & isOutline)47 RSBorder::RSBorder(const bool& isOutline)
48 {
49     if (isOutline) {
50         SetStyle(BorderStyle::SOLID);
51         SetColor(Color(0, 0, 0));
52     }
53 }
54 
SetColor(Color color)55 void RSBorder::SetColor(Color color)
56 {
57     colors_.clear();
58     colors_.push_back(color);
59 }
60 
SetWidth(float width)61 void RSBorder::SetWidth(float width)
62 {
63     widths_.clear();
64     widths_.push_back(width);
65 }
66 
SetStyle(BorderStyle style)67 void RSBorder::SetStyle(BorderStyle style)
68 {
69     styles_.clear();
70     styles_.push_back(style);
71 }
72 
SetDashWidth(float dashWidth)73 void RSBorder::SetDashWidth(float dashWidth)
74 {
75     dashWidth_.clear();
76     dashWidth_.push_back(dashWidth);
77 }
78 
SetDashGap(float dashGap)79 void RSBorder::SetDashGap(float dashGap)
80 {
81     dashGap_.clear();
82     dashGap_.push_back(dashGap);
83 }
84 
GetColor(int idx) const85 Color RSBorder::GetColor(int idx) const
86 {
87     if (colors_.empty()) {
88         return RgbPalette::Transparent();
89     } else if (colors_.size() == 1) {
90         return colors_.front();
91     } else {
92         return colors_.at(idx);
93     }
94 }
95 
GetWidth(int idx) const96 float RSBorder::GetWidth(int idx) const
97 {
98     if (widths_.empty()) {
99         return 0.f;
100     } else if (widths_.size() == 1) {
101         return widths_.front();
102     } else {
103         return widths_.at(idx);
104     }
105 }
106 
GetStyle(int idx) const107 BorderStyle RSBorder::GetStyle(int idx) const
108 {
109     if (styles_.empty()) {
110         return BorderStyle::NONE;
111     } else if (styles_.size() == 1) {
112         return styles_.front();
113     } else {
114         return styles_.at(idx);
115     }
116 }
117 
GetDashWidth(int idx) const118 float RSBorder::GetDashWidth(int idx) const
119 {
120     if (dashWidth_.empty()) {
121         // if dashWidth is not set, return -1 and the value will be calculated
122         return -1.f;
123     } else if (dashWidth_.size() == 1) {
124         return dashWidth_.front();
125     } else {
126         return dashWidth_.at(idx);
127     }
128 }
129 
GetDashGap(int idx) const130 float RSBorder::GetDashGap(int idx) const
131 {
132     if (dashGap_.empty()) {
133         // if dashGap is not set, return -1 and the value will be calculated
134         return -1.f;
135     } else if (dashGap_.size() == 1) {
136         return dashGap_.front();
137     } else {
138         return dashGap_.at(idx);
139     }
140 }
141 
SetColorFour(const Vector4<Color> & color)142 void RSBorder::SetColorFour(const Vector4<Color>& color)
143 {
144     if (color.x_ == color.y_ && color.x_ == color.z_ && color.x_ == color.w_) {
145         return SetColor(color.x_);
146     }
147     colors_ = { color.x_, color.y_, color.z_, color.w_ };
148 }
149 
SetWidthFour(const Vector4f & width)150 void RSBorder::SetWidthFour(const Vector4f& width)
151 {
152     if (width.x_ == width.y_ && width.x_ == width.z_ && width.x_ == width.w_) {
153         return SetWidth(width.x_);
154     }
155     widths_ = { width.x_, width.y_, width.z_, width.w_ };
156 }
157 
SetStyleFour(const Vector4<uint32_t> & style)158 void RSBorder::SetStyleFour(const Vector4<uint32_t>& style)
159 {
160     if (style.x_ == style.y_ && style.x_ == style.z_ && style.x_ == style.w_) {
161         return SetStyle(static_cast<BorderStyle>(style.x_));
162     }
163     styles_ = { static_cast<BorderStyle>(style.x_), static_cast<BorderStyle>(style.y_),
164                 static_cast<BorderStyle>(style.z_), static_cast<BorderStyle>(style.w_) };
165 }
166 
SetRadiusFour(const Vector4f & radius)167 void RSBorder::SetRadiusFour(const Vector4f& radius)
168 {
169     radius_ = { radius.x_, radius.y_, radius.z_, radius.w_ };
170 }
171 
SetDashWidthFour(const Vector4f & dashWidth)172 void RSBorder::SetDashWidthFour(const Vector4f& dashWidth)
173 {
174     if (dashWidth.x_ == dashWidth.y_ && dashWidth.x_ == dashWidth.z_ && dashWidth.x_ == dashWidth.w_) {
175         return SetDashWidth(dashWidth.x_);
176     }
177     dashWidth_ = { dashWidth.x_, dashWidth.y_, dashWidth.z_, dashWidth.w_ };
178 }
179 
SetDashGapFour(const Vector4f & dashGap)180 void RSBorder::SetDashGapFour(const Vector4f& dashGap)
181 {
182     if (dashGap.x_ == dashGap.y_ && dashGap.x_ == dashGap.z_ && dashGap.x_ == dashGap.w_) {
183         return SetDashGap(dashGap.x_);
184     }
185     dashGap_ = { dashGap.x_, dashGap.y_, dashGap.z_, dashGap.w_ };
186 }
187 
GetColorFour() const188 Vector4<Color> RSBorder::GetColorFour() const
189 {
190     if (colors_.size() == 4) {
191         return Vector4<Color>(colors_[0], colors_[1], colors_[2], colors_[3]);
192     } else {
193         return Vector4<Color>(GetColor());
194     }
195 }
196 
GetWidthFour() const197 Vector4f RSBorder::GetWidthFour() const
198 {
199     if (widths_.size() == 4) {
200         return Vector4f(widths_[0], widths_[1], widths_[2], widths_[3]);
201     } else {
202         return Vector4f(GetWidth());
203     }
204 }
205 
GetStyleFour() const206 Vector4<uint32_t> RSBorder::GetStyleFour() const
207 {
208     if (styles_.size() == 4) {
209         return Vector4<uint32_t>(static_cast<uint32_t>(styles_[0]), static_cast<uint32_t>(styles_[1]),
210                                  static_cast<uint32_t>(styles_[2]), static_cast<uint32_t>(styles_[3]));
211     } else {
212         return Vector4<uint32_t>(static_cast<uint32_t>(GetStyle()));
213     }
214 }
215 
GetRadiusFour() const216 Vector4f RSBorder::GetRadiusFour() const
217 {
218     return radius_;
219 }
220 
GetDashWidthFour() const221 Vector4f RSBorder::GetDashWidthFour() const
222 {
223     if (dashWidth_.size() <= 1) {
224         return Vector4f(GetDashWidth());
225     } else {
226         return Vector4f(GetDashWidth(BorderType::LEFT), GetDashWidth(BorderType::TOP),
227                         GetDashWidth(BorderType::RIGHT), GetDashWidth(BorderType::BOTTOM));
228     }
229 }
230 
GetDashGapFour() const231 Vector4f RSBorder::GetDashGapFour() const
232 {
233     if (dashGap_.size() <= 1) {
234         return Vector4f(GetDashGap());
235     } else {
236         return Vector4f(GetDashGap(BorderType::LEFT), GetDashGap(BorderType::TOP),
237                         GetDashGap(BorderType::RIGHT), GetDashGap(BorderType::BOTTOM));
238     }
239 }
240 
SetBorderEffect(Drawing::Pen & pen,int idx,float spaceBetweenDot,float borderLength) const241 void RSBorder::SetBorderEffect(Drawing::Pen& pen, int idx, float spaceBetweenDot, float borderLength) const
242 {
243     float width = GetWidth(idx);
244     if (ROSEN_EQ(width, 0.f)) {
245         return;
246     }
247     BorderStyle style = GetStyle(idx);
248     if (style == BorderStyle::DOTTED) {
249         Drawing::Path dotPath;
250         if (ROSEN_EQ(spaceBetweenDot, 0.f)) {
251             spaceBetweenDot = width * PARAM_DOUBLE;
252         }
253         dotPath.AddCircle(0.0f, 0.0f, width / PARAM_DOUBLE);
254         pen.SetPathEffect(Drawing::PathEffect::CreatePathDashEffect(dotPath, spaceBetweenDot, 0.0,
255             Drawing::PathDashStyle::ROTATE));
256         return;
257     }
258     if (style == BorderStyle::DASHED) {
259         float dashWidth = GetDashWidth(idx);
260         float dashGap = GetDashGap(idx);
261         bool bothZero = ROSEN_EQ(dashWidth, 0.f) && ROSEN_EQ(dashGap, 0.f);
262         if (dashWidth >= 0.f && dashGap >= 0.f) {
263             // Set fake gap for the case when dashWidth and dashGap params are both zero to prevent solid border line
264             float intervals[] = { dashWidth, bothZero ? 1.f : dashGap };
265             pen.SetPathEffect(
266                 Drawing::PathEffect::CreateDashPathEffect(intervals, sizeof(intervals)/sizeof(float), 0.0));
267             return;
268         }
269         double addLen = 0.0; // When left < 2 * gap, splits left to gaps.
270         double delLen = 0.0; // When left > 2 * gap, add one dash and shortening them.
271         if (!ROSEN_EQ(borderLength, 0.f) && width > 0) {
272             float count = borderLength / width;
273             float leftLen = fmod((count - DASHED_LINE_LENGTH), (DASHED_LINE_LENGTH + 1));
274             if (leftLen > DASHED_LINE_LENGTH - 1) {
275                 delLen = (DASHED_LINE_LENGTH + 1 - leftLen) * width /
276                          static_cast<int>((count - DASHED_LINE_LENGTH) / (DASHED_LINE_LENGTH + 1) + PARAM_DOUBLE);
277             } else {
278                 addLen = leftLen * width / static_cast<int>((count - DASHED_LINE_LENGTH) / (DASHED_LINE_LENGTH + 1));
279             }
280         }
281         float intervals[] = {
282             (dashWidth >= 0.f ? dashWidth : width * DASHED_LINE_LENGTH - delLen),
283             (dashGap >= 0.f ? dashGap : width + addLen) };
284         pen.SetPathEffect(Drawing::PathEffect::CreateDashPathEffect(intervals, sizeof(intervals)/sizeof(float), 0.0));
285         return;
286     }
287     pen.SetPathEffect(nullptr);
288 }
289 
ApplyFillStyle(Drawing::Brush & brush) const290 bool RSBorder::ApplyFillStyle(Drawing::Brush& brush) const
291 {
292     if (colors_.size() != 1) {
293         return false;
294     }
295     if (styles_.size() != 1 || GetStyle() != BorderStyle::SOLID) {
296         return false;
297     }
298     for (const float& width : widths_) {
299         if (ROSEN_LE(width, 0.f)) {
300             return false;
301         }
302     }
303     brush.SetColor(GetColor().AsArgbInt());
304     return true;
305 }
306 
ApplyPathStyle(Drawing::Pen & pen) const307 bool RSBorder::ApplyPathStyle(Drawing::Pen& pen) const
308 {
309     if (colors_.size() != 1 || widths_.size() != 1 || styles_.size() != 1 ||
310         dashWidth_.size() != 1 || dashGap_.size() != 1) {
311         return false;
312     }
313     pen.SetWidth(widths_.front());
314     pen.SetColor(colors_.front().AsArgbInt());
315     SetBorderEffect(pen, BorderType::LEFT, 0.f, 0.f);
316     return true;
317 }
318 
ApplyFourLine(Drawing::Pen & pen) const319 bool RSBorder::ApplyFourLine(Drawing::Pen& pen) const
320 {
321     if (colors_.size() != 1 || styles_.size() != 1) {
322         return false;
323     }
324     return true;
325 }
326 
ApplyLineStyle(Drawing::Pen & pen,int borderIdx,float length) const327 bool RSBorder::ApplyLineStyle(Drawing::Pen& pen, int borderIdx, float length) const
328 {
329     if (GetWidth(borderIdx) <= 0.0f) {
330         return false;
331     }
332     float borderWidth = GetWidth(borderIdx);
333     BorderStyle borderStyle = GetStyle(borderIdx);
334     float addLen = (borderStyle != BorderStyle::DOTTED) ? 0.0f : 0.5f;
335     auto borderLength = length - borderWidth * addLen * PARAM_DOUBLE;
336     int32_t rawNumber = borderLength / (PARAM_DOUBLE * borderWidth);
337     if (borderStyle == BorderStyle::DOTTED && rawNumber == 0) {
338         return false;
339     }
340 
341     pen.SetWidth(GetWidth(borderIdx));
342     Color color = GetColor(borderIdx);
343     pen.SetColor(color.AsArgbInt());
344     SetBorderEffect(pen, borderIdx, borderLength / rawNumber, borderLength);
345     return true;
346 }
347 
ApplySimpleBorder(const RRect & rrect) const348 bool RSBorder::ApplySimpleBorder(const RRect& rrect) const
349 {
350     if (!(colors_.size() == 1 && widths_.size() == 1 && styles_.size() == 1)) {
351         return false;
352     }
353     constexpr uint32_t NUM_OF_CORNERS_IN_RECT = 4u;
354     for (uint32_t i = 1; i < NUM_OF_CORNERS_IN_RECT; i++) {
355         if (rrect.radius_[0].x_ != rrect.radius_[i].x_) {
356             return false;
357         }
358     }
359     if (styles_.front() == BorderStyle::SOLID) {
360         return true;
361     }
362     // To avoid artefacts at corner - corner radius should be more than half the stroke width
363     return rrect.radius_[0].x_ > widths_.front() / PARAM_DOUBLE;
364 }
365 
PaintFourLine(Drawing::Canvas & canvas,Drawing::Pen & pen,RectF rect) const366 void RSBorder::PaintFourLine(Drawing::Canvas& canvas, Drawing::Pen& pen, RectF rect) const
367 {
368     float borderLeftWidth = GetWidth(RSBorder::LEFT);
369     float borderRightWidth = GetWidth(RSBorder::RIGHT);
370     float borderTopWidth = GetWidth(RSBorder::TOP);
371     float borderBottomWidth = GetWidth(RSBorder::BOTTOM);
372     if (ApplyLineStyle(pen, RSBorder::LEFT, rect.height_)) {
373         float addLen = (GetStyle(RSBorder::LEFT) != BorderStyle::DOTTED) ? 0.0f : 0.5f;
374         canvas.AttachPen(pen);
375         canvas.DrawLine(
376             Drawing::Point(rect.left_ + borderLeftWidth / PARAM_DOUBLE, rect.top_ + addLen * borderTopWidth),
377             Drawing::Point(rect.left_ + borderLeftWidth / PARAM_DOUBLE, rect.GetBottom() - borderBottomWidth));
378         canvas.DetachPen();
379     }
380     if (ApplyLineStyle(pen, RSBorder::RIGHT, rect.height_)) {
381         float addLen = (GetStyle(RSBorder::RIGHT) != BorderStyle::DOTTED) ? 0.0f : 0.5f;
382         canvas.AttachPen(pen);
383         canvas.DrawLine(
384             Drawing::Point(rect.GetRight() - borderRightWidth / PARAM_DOUBLE,
385                 rect.GetBottom() - addLen * borderBottomWidth),
386             Drawing::Point(rect.GetRight() - borderRightWidth / PARAM_DOUBLE, rect.top_ + borderTopWidth));
387         canvas.DetachPen();
388     }
389     if (ApplyLineStyle(pen, RSBorder::TOP, rect.width_)) {
390         float addLen = (GetStyle(RSBorder::TOP) != BorderStyle::DOTTED) ? 0.0f : 0.5f;
391         canvas.AttachPen(pen);
392         canvas.DrawLine(
393             Drawing::Point(rect.GetRight() - addLen * borderRightWidth, rect.top_ + borderTopWidth / PARAM_DOUBLE),
394             Drawing::Point(rect.left_ + borderLeftWidth, rect.top_ + borderTopWidth / PARAM_DOUBLE));
395         canvas.DetachPen();
396     }
397     if (ApplyLineStyle(pen, RSBorder::BOTTOM, rect.width_)) {
398         float addLen = (GetStyle(RSBorder::BOTTOM) != BorderStyle::DOTTED) ? 0.0f : 0.5f;
399         canvas.AttachPen(pen);
400         canvas.DrawLine(
401             Drawing::Point(rect.left_ + addLen * borderLeftWidth, rect.GetBottom() - borderBottomWidth / PARAM_DOUBLE),
402             Drawing::Point(rect.GetRight() - borderRightWidth, rect.GetBottom() - borderBottomWidth / PARAM_DOUBLE));
403         canvas.DetachPen();
404     }
405 }
406 
DrawBorders(Drawing::Canvas & canvas,Drawing::Pen & pen,RSBorderGeo & borderGeo) const407 void RSBorder::DrawBorders(Drawing::Canvas& canvas, Drawing::Pen& pen, RSBorderGeo& borderGeo) const
408 {
409     CalcBorderPath(borderGeo);
410 
411     int sameColorFlag = (CanBeCombined(RSBorder::LEFT, RSBorder::TOP) ? 1 << 3 : 0) |
412                         (CanBeCombined(RSBorder::TOP, RSBorder::RIGHT) ? 1 << 2 : 0) |
413                         (CanBeCombined(RSBorder::RIGHT, RSBorder::BOTTOM) ? 1 << 1 : 0) |
414                         (CanBeCombined(RSBorder::BOTTOM, RSBorder::LEFT) ? 1 : 0);
415 
416     switch (sameColorFlag) {
417         case 0b0000: // all different
418             for (int borderIdx = 0; borderIdx < MAX_BORDER_NUM; borderIdx++) {
419                 DrawBorderImpl(canvas, pen, borderGeo, borderIdx);
420             }
421             break;
422 
423         case 0b1000: // LT same
424             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::RIGHT);
425             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::BOTTOM);
426             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::LEFT, RSBorder::TOP);
427             break;
428 
429         case 0b0100: // TR same
430             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::BOTTOM);
431             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::LEFT);
432             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::TOP, RSBorder::RIGHT);
433             break;
434 
435         case 0b0010: // RB same
436             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::TOP);
437             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::LEFT);
438             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::RIGHT, RSBorder::BOTTOM);
439             break;
440 
441         case 0b0001: // BL same
442             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::TOP);
443             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::RIGHT);
444             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::BOTTOM, RSBorder::LEFT);
445             break;
446 
447         case 0b0101: // RB same, LT same
448             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::TOP, RSBorder::RIGHT);
449             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::BOTTOM, RSBorder::LEFT);
450             break;
451 
452         case 0b1010: // LT same, RB same
453             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::LEFT, RSBorder::TOP);
454             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::RIGHT, RSBorder::BOTTOM);
455             break;
456 
457         case 0b0110: // only LEFT different
458             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::LEFT);
459             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::TOP, RSBorder::RIGHT, RSBorder::BOTTOM);
460             break;
461 
462         case 0b0011: // only TOP different
463             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::TOP);
464             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::RIGHT, RSBorder::BOTTOM, RSBorder::LEFT);
465             break;
466 
467         case 0b1001: // only RIGHT different
468             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::RIGHT);
469             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::BOTTOM, RSBorder::LEFT, RSBorder::TOP);
470             break;
471 
472         case 0b1100: // only BOTTOM different
473             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::BOTTOM);
474             DrawBorderImpl(canvas, pen, borderGeo, RSBorder::LEFT, RSBorder::TOP, RSBorder::RIGHT);
475             break;
476 
477         default:
478             ROSEN_LOGE("DrawBorders, style error: %{public}d, draw no border", sameColorFlag);
479             break;
480     }
481 
482     return;
483 }
484 
DrawBorderImpl(Drawing::Canvas & canvas,Drawing::Pen & pen,RSBorderGeo & borderGeo,int idx) const485 void RSBorder::DrawBorderImpl(
486     Drawing::Canvas& canvas, Drawing::Pen& pen, RSBorderGeo& borderGeo, int idx) const
487 {
488     Drawing::AutoCanvasRestore acr(canvas, true);
489     canvas.ClipPath(borderGeo.pathVec[idx], Drawing::ClipOp::INTERSECT, true);
490     if (GetStyle(idx) == BorderStyle::SOLID) {
491         uint32_t color = GetColor(idx).AsArgbInt();
492         DrawNestedRoundRect(canvas, borderGeo, color);
493     } else {
494         canvas.ClipRoundRect(borderGeo.rrect, Drawing::ClipOp::INTERSECT, true);
495         canvas.ClipRoundRect(borderGeo.innerRRect, Drawing::ClipOp::DIFFERENCE, true);
496         float width = borderGeo.rrect.GetRect().GetWidth();
497         ApplyLineStyle(pen, idx, width);
498         if (GetStyle(RSBorder::TOP) != BorderStyle::DOTTED) {
499             pen.SetWidth(std::max(std::max(LEFTW, TOPW), std::max(RIGHTW, BOTTOMW)));
500         }
501         switch (idx) {
502             case RSBorder::LEFT:
503                 DrawLeftBorder(canvas, pen, borderGeo);
504                 break;
505 
506             case RSBorder::TOP:
507                 DrawTopBorder(canvas, pen, borderGeo);
508                 break;
509 
510             case RSBorder::RIGHT:
511                 DrawRightBorder(canvas, pen, borderGeo);
512                 break;
513 
514             case RSBorder::BOTTOM:
515                 DrawBottomBorder(canvas, pen, borderGeo);
516                 break;
517 
518             default:
519                 break;
520         }
521     }
522 }
523 
DrawBorderImpl(Drawing::Canvas & canvas,Drawing::Pen & pen,RSBorderGeo & borderGeo,int idx1,int idx2) const524 void RSBorder::DrawBorderImpl(
525     Drawing::Canvas& canvas, Drawing::Pen& pen, RSBorderGeo& borderGeo, int idx1, int idx2) const
526 {
527     Drawing::AutoCanvasRestore acr(canvas, true);
528     Drawing::Path clipPath;
529     clipPath.Op(borderGeo.pathVec[idx1], borderGeo.pathVec[idx2], Drawing::PathOp::UNION);
530     canvas.ClipPath(clipPath, Drawing::ClipOp::INTERSECT, true);
531     DrawNestedRoundRect(canvas, borderGeo, GetColor(idx1).AsArgbInt());
532 }
533 
DrawBorderImpl(Drawing::Canvas & canvas,Drawing::Pen & pen,RSBorderGeo & borderGeo,int idx1,int idx2,int idx3) const534 void RSBorder::DrawBorderImpl(
535     Drawing::Canvas& canvas, Drawing::Pen& pen, RSBorderGeo& borderGeo, int idx1, int idx2, int idx3) const
536 {
537     Drawing::AutoCanvasRestore acr(canvas, true);
538     Drawing::Path clipPath;
539     clipPath.Op(borderGeo.pathVec[idx1], borderGeo.pathVec[idx2], Drawing::PathOp::UNION);
540     clipPath.Op(borderGeo.pathVec[idx3], clipPath, Drawing::PathOp::UNION);
541     canvas.ClipPath(clipPath, Drawing::ClipOp::INTERSECT, true);
542     DrawNestedRoundRect(canvas, borderGeo, GetColor(idx1).AsArgbInt());
543     return;
544 }
545 
CanBeCombined(int idx1,int idx2) const546 bool RSBorder::CanBeCombined(int idx1, int idx2) const
547 {
548     // same color, both solid style, both width > 0
549     if (GetColor(idx1).AsArgbInt() != GetColor(idx2).AsArgbInt() || GetStyle(idx1) != BorderStyle::SOLID ||
550         GetStyle(idx2) != BorderStyle::SOLID || ROSEN_LE(GetWidth(idx1), 0.f) ||
551         ROSEN_LE(GetWidth(idx2), 0.f)) {
552         return false;
553     }
554     return true;
555 }
556 
CalcBorderPath(RSBorderGeo & borderGeo) const557 void RSBorder::CalcBorderPath(RSBorderGeo& borderGeo) const
558 {
559     float offsetX = borderGeo.rrect.GetRect().GetLeft();
560     float offsetY = borderGeo.rrect.GetRect().GetTop();
561     float height = borderGeo.rrect.GetRect().GetHeight();
562     float width = borderGeo.rrect.GetRect().GetWidth();
563 
564     // calc top-left, top-right, bottom-right, top-right intersection point with innerRect center
565     Drawing::Point tlip = GetTLIP(borderGeo.rrect, borderGeo.center);
566     Drawing::Point trip = GetTRIP(borderGeo.rrect, borderGeo.center);
567     Drawing::Point brip = GetBRIP(borderGeo.rrect, borderGeo.center);
568     Drawing::Point blip = GetBLIP(borderGeo.rrect, borderGeo.center);
569 
570     if (TOPW > 0.f) {
571         borderGeo.pathVec[RSBorder::TOP].MoveTo(offsetX, offsetY);
572         borderGeo.pathVec[RSBorder::TOP].LineTo(tlip.GetX(), tlip.GetY());
573         borderGeo.pathVec[RSBorder::TOP].LineTo(trip.GetX(), trip.GetY());
574         borderGeo.pathVec[RSBorder::TOP].LineTo(offsetX + width, offsetY);
575         borderGeo.pathVec[RSBorder::TOP].Close();
576     }
577 
578     if (RIGHTW > 0.f) {
579         borderGeo.pathVec[RSBorder::RIGHT].MoveTo(offsetX + width, offsetY);
580         borderGeo.pathVec[RSBorder::RIGHT].LineTo(trip.GetX(), trip.GetY());
581         borderGeo.pathVec[RSBorder::RIGHT].LineTo(brip.GetX(), brip.GetY());
582         borderGeo.pathVec[RSBorder::RIGHT].LineTo(offsetX + width, offsetY + height);
583         borderGeo.pathVec[RSBorder::RIGHT].Close();
584     }
585 
586     if (BOTTOMW > 0.f) {
587         borderGeo.pathVec[RSBorder::BOTTOM].MoveTo(offsetX, offsetY + borderGeo.rrect.GetRect().GetHeight());
588         borderGeo.pathVec[RSBorder::BOTTOM].LineTo(blip.GetX(), blip.GetY());
589         borderGeo.pathVec[RSBorder::BOTTOM].LineTo(brip.GetX(), brip.GetY());
590         borderGeo.pathVec[RSBorder::BOTTOM].LineTo(offsetX + width, offsetY + borderGeo.rrect.GetRect().GetHeight());
591         borderGeo.pathVec[RSBorder::BOTTOM].Close();
592     }
593 
594     if (LEFTW > 0.f) {
595         borderGeo.pathVec[RSBorder::LEFT].MoveTo(offsetX, offsetY);
596         borderGeo.pathVec[RSBorder::LEFT].LineTo(tlip.GetX(), tlip.GetY());
597         borderGeo.pathVec[RSBorder::LEFT].LineTo(blip.GetX(), blip.GetY());
598         borderGeo.pathVec[RSBorder::LEFT].LineTo(offsetX, offsetY + height);
599         borderGeo.pathVec[RSBorder::LEFT].Close();
600     }
601     return;
602 }
603 
DrawNestedRoundRect(Drawing::Canvas & canvas,const RSBorderGeo & borderGeo,uint32_t color) const604 void RSBorder::DrawNestedRoundRect(Drawing::Canvas& canvas, const RSBorderGeo& borderGeo, uint32_t color) const
605 {
606     Drawing::Brush brush;
607     brush.SetColor(color);
608     canvas.AttachBrush(brush);
609     canvas.DrawNestedRoundRect(borderGeo.rrect, borderGeo.innerRRect);
610     canvas.DetachBrush();
611 }
612 
DrawTopBorder(Drawing::Canvas & canvas,Drawing::Pen & pen,const RSBorderGeo & borderGeo) const613 void RSBorder::DrawTopBorder(Drawing::Canvas& canvas, Drawing::Pen& pen, const RSBorderGeo& borderGeo) const
614 {
615     float width = borderGeo.rrect.GetRect().GetWidth();
616     float offsetX = borderGeo.rrect.GetRect().GetLeft();
617     float offsetY = borderGeo.rrect.GetRect().GetTop();
618     float x = offsetX + LEFTW2;
619     float y = offsetY + TOPW2;
620     Drawing::Point tlRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::TOP_LEFT_POS);
621     Drawing::Point trRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::TOP_RIGHT_POS);
622 
623     // calc rectangles to draw left top and right top arcs according to radii values
624     float startArcWidth = std::min(width - LEFTW, tlRad.GetX() * 2.f);
625     float endArcWidth = std::min(width - RIGHTW, trRad.GetX() * 2.f);
626     float startArcHeight = std::min(borderGeo.rrect.GetRect().GetHeight() - TOPW, tlRad.GetY() * 2.f);
627     float endArcHeight = std::min(borderGeo.rrect.GetRect().GetHeight() - TOPW, trRad.GetY() * 2.f);
628     auto rs = Drawing::Rect(x, y, x + startArcWidth, y + startArcHeight);
629     auto re = Drawing::Rect(
630         offsetX + width - RIGHTW / 2.f - endArcWidth, y, offsetX + width - RIGHTW / 2.f, y + endArcHeight);
631     // create drawing path from left top corner to right top corner
632     Drawing::Path topBorder;
633     topBorder.MoveTo(std::min(x, offsetX + tlRad.GetX() / 2.f), y + tlRad.GetY());
634     topBorder.ArcTo(rs.GetLeft(), rs.GetTop(), rs.GetRight(), rs.GetBottom(), TOP_START, SWEEP_ANGLE);
635     topBorder.ArcTo(re.GetLeft(), re.GetTop(), re.GetRight(), re.GetBottom(), TOP_END, SWEEP_ANGLE);
636     topBorder.LineTo(std::max(offsetX + width - RIGHTW2, offsetX + width - trRad.GetX() / 2.f), y + trRad.GetY());
637     canvas.AttachPen(pen);
638     canvas.DrawPath(topBorder);
639 }
640 
DrawRightBorder(Drawing::Canvas & canvas,Drawing::Pen & pen,const RSBorderGeo & borderGeo) const641 void RSBorder::DrawRightBorder(Drawing::Canvas& canvas, Drawing::Pen& pen, const RSBorderGeo& borderGeo) const
642 {
643     float width = borderGeo.rrect.GetRect().GetWidth();
644     float height = borderGeo.rrect.GetRect().GetHeight();
645     float offsetX = borderGeo.rrect.GetRect().GetLeft();
646     float offsetY = borderGeo.rrect.GetRect().GetTop();
647     float x = offsetX + width - RIGHTW2;
648     float y = offsetY + TOPW2;
649     Drawing::Point trRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::TOP_RIGHT_POS);
650     Drawing::Point brRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_RIGHT_POS);
651     // calc rectangles to draw right top and right bottom arcs according to radii values
652     float startArcWidth = std::min(width - RIGHTW, trRad.GetX() * 2.f);
653     float endArcWidth = std::min(width - RIGHTW, brRad.GetX() * 2.f);
654     float startArcHeight = std::min(height - TOPW, trRad.GetY() * 2.f);
655     float endArcHeight = std::min(height - BOTTOMW, brRad.GetY() * 2.f);
656     auto rs = Drawing::Rect(x - startArcWidth, y, x, y + startArcHeight);
657     auto re = Drawing::Rect(x - endArcWidth, height - BOTTOMW2 - endArcHeight, x, height - BOTTOMW2);
658     // create drawing path from right top corner to right bottom corner
659     Drawing::Path rightBorder;
660     rightBorder.MoveTo(x - trRad.GetX(), std::min(y, offsetY + trRad.GetY() / 2.f));
661     rightBorder.ArcTo(rs.GetLeft(), rs.GetTop(), rs.GetRight(), rs.GetBottom(), RIGHT_START, SWEEP_ANGLE);
662     rightBorder.ArcTo(re.GetLeft(), re.GetTop(), re.GetRight(), re.GetBottom(), RIGHT_END, SWEEP_ANGLE);
663     rightBorder.LineTo(x - brRad.GetX(), std::max(offsetY + height - BOTTOMW2, offsetY + height - brRad.GetY() / 2.f));
664     canvas.AttachPen(pen);
665     canvas.DrawPath(rightBorder);
666     canvas.DetachPen();
667 }
668 
DrawBottomBorder(Drawing::Canvas & canvas,Drawing::Pen & pen,const RSBorderGeo & borderGeo) const669 void RSBorder::DrawBottomBorder(Drawing::Canvas& canvas, Drawing::Pen& pen, const RSBorderGeo& borderGeo) const
670 {
671     float width = borderGeo.rrect.GetRect().GetWidth();
672     float offsetX = borderGeo.rrect.GetRect().GetLeft();
673     float offsetY = borderGeo.rrect.GetRect().GetTop();
674     float x = offsetX + LEFTW2;
675     float y = offsetY + borderGeo.rrect.GetRect().GetHeight() - BOTTOMW2;
676     Drawing::Point brRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_RIGHT_POS);
677     Drawing::Point blRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_LEFT_POS);
678     // calc rectangles to draw right bottom and left bottom arcs according to radii values
679     float startArcWidth = std::min(width - RIGHTW, brRad.GetX() * 2.f);
680     float endArcWidth = std::min(width - LEFTW, blRad.GetX() * 2.f);
681     float startArcHeight = std::min(borderGeo.rrect.GetRect().GetHeight() - BOTTOMW, brRad.GetY() * 2.f);
682     float endArcHeight = std::min(borderGeo.rrect.GetRect().GetHeight() - BOTTOMW, blRad.GetY() * 2.f);
683     auto rs =
684         Drawing::Rect(offsetX + width - RIGHTW2 - startArcWidth, y - startArcHeight, offsetX + width - RIGHTW2, y);
685     auto re = Drawing::Rect(x, y - endArcHeight, x + endArcWidth, y);
686     // create drawing path from right bottom corner to left bottom corner
687     Drawing::Path bottomBorder;
688     bottomBorder.MoveTo(std::max(offsetX + width - RIGHTW2, offsetY + width - brRad.GetX() / 2.f), y - brRad.GetY());
689     bottomBorder.ArcTo(rs.GetLeft(), rs.GetTop(), rs.GetRight(), rs.GetBottom(), BOTTOM_START, SWEEP_ANGLE);
690     bottomBorder.ArcTo(re.GetLeft(), re.GetTop(), re.GetRight(), re.GetBottom(), BOTTOM_END, SWEEP_ANGLE);
691     bottomBorder.LineTo(std::min(x, offsetX + blRad.GetX() / 2.f), y - blRad.GetY());
692     canvas.AttachPen(pen);
693     canvas.DrawPath(bottomBorder);
694     canvas.DetachPen();
695 }
696 
DrawLeftBorder(Drawing::Canvas & canvas,Drawing::Pen & pen,const RSBorderGeo & borderGeo) const697 void RSBorder::DrawLeftBorder(Drawing::Canvas& canvas, Drawing::Pen& pen, const RSBorderGeo& borderGeo) const
698 {
699     float offsetX = borderGeo.rrect.GetRect().GetLeft();
700     float offsetY = borderGeo.rrect.GetRect().GetTop();
701     float x = offsetX + LEFTW2;
702     float y = offsetY + TOPW2;
703     float height = borderGeo.rrect.GetRect().GetHeight();
704     Drawing::Point tlRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::TOP_LEFT_POS);
705     Drawing::Point blRad = borderGeo.rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_LEFT_POS);
706     // calc rectangles to draw left bottom and left top arcs according to radii values
707     float startArcWidth = std::min(borderGeo.rrect.GetRect().GetWidth() - LEFTW, blRad.GetX() * 2.f);
708     float endArcWidth = std::min(borderGeo.rrect.GetRect().GetWidth() - LEFTW, tlRad.GetX() * 2.f);
709     float startArcHeight = std::min(height - BOTTOMW, blRad.GetY() * 2.f);
710     float endArcHeight = std::min(height - TOPW, tlRad.GetY() * 2.f);
711     auto rs =
712         Drawing::Rect(x, offsetY + height - BOTTOMW2 - startArcHeight, x + startArcWidth, offsetY + height - BOTTOMW2);
713     auto re = Drawing::Rect(x, y, x + endArcWidth, y + endArcHeight);
714     // create drawing path from left bottom corner to left top corner
715     Drawing::Path leftBorder;
716     leftBorder.MoveTo(x + blRad.GetX(), std::max(offsetY + height - BOTTOMW2, offsetY + height - blRad.GetY() / 2.f));
717     leftBorder.ArcTo(rs.GetLeft(), rs.GetTop(), rs.GetRight(), rs.GetBottom(), LEFT_START, SWEEP_ANGLE);
718     leftBorder.ArcTo(re.GetLeft(), re.GetTop(), re.GetRight(), re.GetBottom(), LEFT_END, SWEEP_ANGLE);
719     leftBorder.LineTo(x + tlRad.GetX(), std::min(y, offsetY + tlRad.GetY() / 2.f));
720     canvas.AttachPen(pen);
721     canvas.DrawPath(leftBorder);
722     canvas.DetachPen();
723 }
724 
725 // Return top left intersection pos for clipping
GetTLIP(const Drawing::RoundRect & rrect,const Drawing::Point & center) const726 Drawing::Point RSBorder::GetTLIP(const Drawing::RoundRect& rrect, const Drawing::Point& center) const
727 {
728     // inner round rect center point
729     float x = center.GetX();
730     float y = center.GetY();
731     // width/height of inner round rect
732     float width = rrect.GetRect().GetWidth() - LEFTW - RIGHTW;
733     float height = rrect.GetRect().GetHeight() - TOPW - BOTTOMW;
734     if (width > 0) {
735         // kc is linear ratio of inner rect
736         float kc = height / width;
737         if (LEFTW > 0) {
738             // k is linear ratio for external rect (cutting angle at top left corner)
739             float k = TOPW / LEFTW;
740             // raduii values of external round rect for calculating clipping point
741             Drawing::Point tlRad = rrect.GetCornerRadius(Drawing::RoundRect::TOP_LEFT_POS);
742             Drawing::Point trRad = rrect.GetCornerRadius(Drawing::RoundRect::TOP_RIGHT_POS);
743             Drawing::Point blRad = rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_LEFT_POS);
744             // shows what center axis of inner round rect we intersect fist (x or y)
745             if (k <= kc) {
746                 // top left and right raduii for inner round rect
747                 float dlx = std::max(tlRad.GetX() - LEFTW, 0.f);
748                 float drx = std::max(trRad.GetX() - RIGHTW, 0.f);
749                 // calc delta to prevent overlapping of top right corner
750                 x = (dlx > 0) ? (x + std::min(dlx, width / 2.f - drx)) : (x - width / 2.f);
751                 y = rrect.GetRect().GetTop() + (x - rrect.GetRect().GetLeft()) * k;
752             } else {
753                 // left top and bottom raduii for inner round rect
754                 float dty = std::max(tlRad.GetY() - TOPW, 0.f);
755                 float dby = std::max(blRad.GetY() - BOTTOMW, 0.f);
756                 // calc delta to prevent overlapping of bottom left corner
757                 y = (dty > 0) ? (y = y + std::min(dty, height / 2.f - dby)) : (y - height / 2.f);
758                 x = rrect.GetRect().GetLeft() + (y - rrect.GetRect().GetTop()) / k;
759             }
760         } else {
761             x = rrect.GetRect().GetLeft();
762             y = std::max(y - height / 2.f,
763                          std::min(rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() / 2.f,
764                                   rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() - BOTTOMW));
765         }
766     } else {
767         y = rrect.GetRect().GetTop() + TOPW;
768     }
769     return Drawing::Point(x, y);
770 }
771 
772 // Return top right intersection pos for clipping
GetTRIP(const Drawing::RoundRect & rrect,const Drawing::Point & center) const773 Drawing::Point RSBorder::GetTRIP(const Drawing::RoundRect& rrect, const Drawing::Point& center) const
774 {
775     // inner round rect center point
776     float x = center.GetX();
777     float y = center.GetY();
778     // width/height of inner round rect
779     float width = rrect.GetRect().GetWidth() - LEFTW - RIGHTW;
780     float height = rrect.GetRect().GetHeight() - TOPW - BOTTOMW;
781     if (width > 0) {
782         // kc is linear ratio of inner rect
783         float kc = height / width;
784         if (RIGHTW > 0) {
785             // k is linear ratio for external rect (cutting angle at top right corner)
786             float k = TOPW / RIGHTW;
787             // raduii values of external round rect for calculating clipping point
788             Drawing::Point trRad = rrect.GetCornerRadius(Drawing::RoundRect::TOP_RIGHT_POS);
789             Drawing::Point tlRad = rrect.GetCornerRadius(Drawing::RoundRect::TOP_LEFT_POS);
790             Drawing::Point brRad = rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_RIGHT_POS);
791             // shows what center axis of inner round rect we intersect fist (x or y)
792             if (k <= kc) {
793                 // top left and right raduii for inner round rect
794                 float drx = std::max(trRad.GetX() - RIGHTW, 0.f);
795                 float dlx = std::max(tlRad.GetX() - LEFTW, 0.f);
796                 // calc delta to prevent overlapping of top left corner
797                 x = (drx > 0) ? (x - std::min(drx, width / 2.f - dlx)) : (x + width / 2.f);
798                 y = rrect.GetRect().GetTop() + (rrect.GetRect().GetRight() - x) * k;
799             } else {
800                 // right top and bottom raduii for inner round rect
801                 float dty = std::max(trRad.GetY() - TOPW, 0.f);
802                 float dby = std::max(brRad.GetY() - BOTTOMW, 0.f);
803                 // calc delta to prevent overlapping of bottom right corner
804                 y = (dty > 0) ? (y = y + std::min(dty, height / 2.f - dby)) : (y - height / 2.f);
805                 x = rrect.GetRect().GetRight() - (y - rrect.GetRect().GetTop()) / k;
806             }
807         } else {
808             x = rrect.GetRect().GetLeft() + rrect.GetRect().GetWidth();
809             y = std::max(y - height / 2.f,
810                          std::min(rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() / 2.f,
811                                   rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() - BOTTOMW));
812         }
813     } else {
814         y = rrect.GetRect().GetTop() + TOPW;
815     }
816     return Drawing::Point(x, y);
817 }
818 
819 // Return bottom left intersection pos for clipping
GetBLIP(const Drawing::RoundRect & rrect,const Drawing::Point & center) const820 Drawing::Point RSBorder::GetBLIP(const Drawing::RoundRect& rrect, const Drawing::Point& center) const
821 {
822     // inner round rect center point
823     float x = center.GetX();
824     float y = center.GetY();
825     // width/height of inner round rect
826     float width = rrect.GetRect().GetWidth() - LEFTW - RIGHTW;
827     float height = rrect.GetRect().GetHeight() - TOPW - BOTTOMW;
828     if (width > 0) {
829         // kc is linear ratio of inner rect
830         float kc = height / width;
831         if (LEFTW > 0) {
832             // k is linear ratio for external rect (cutting angle at bottom left corner)
833             float k = BOTTOMW / LEFTW;
834             // raduii values of external round rect for calculating clipping point
835             Drawing::Point blRad = rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_LEFT_POS);
836             Drawing::Point tlRad = rrect.GetCornerRadius(Drawing::RoundRect::TOP_LEFT_POS);
837             Drawing::Point brRad = rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_RIGHT_POS);
838             // shows what center axis of inner round rect we intersect fist (x or y)
839             if (k <= kc) {
840                 // bottom left and right raduii for inner round rect
841                 float dlx = std::max(blRad.GetX() - LEFTW, 0.f);
842                 float drx = std::max(brRad.GetX() - RIGHTW, 0.f);
843                 // calc delta to prevent overlapping of bottom right corner
844                 x = (dlx > 0) ? (x + std::min(dlx, width / 2.f - drx)) : (x - width / 2.f);
845                 y = rrect.GetRect().GetBottom() - (x - rrect.GetRect().GetLeft()) * k;
846             } else {
847                 // left bottom and top raduii for inner round rect
848                 float dby = std::max(blRad.GetY() - BOTTOMW, 0.f);
849                 float dty = std::max(tlRad.GetY() - TOPW, 0.f);
850                 // calc delta to prevent overlapping of top left corner
851                 y = (dby > 0) ? (y = y - std::min(dby, height / 2.f - dty)) : (y + height / 2.f);
852                 x = rrect.GetRect().GetLeft() + (rrect.GetRect().GetBottom() - y) / k;
853             }
854         } else {
855             x = rrect.GetRect().GetLeft();
856             y = std::min(y + height / 2.f,
857                          std::max(rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() / 2.f,
858                                   rrect.GetRect().GetTop() + TOPW));
859         }
860     } else {
861         y = rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() - BOTTOMW;
862     }
863     return Drawing::Point(x, y);
864 }
865 
866 // Return bottom right intersection pos for clipping
GetBRIP(const Drawing::RoundRect & rrect,const Drawing::Point & center) const867 Drawing::Point RSBorder::GetBRIP(const Drawing::RoundRect& rrect, const Drawing::Point& center) const
868 {
869     // inner round rect center point
870     float x = center.GetX();
871     float y = center.GetY();
872     // width/height of inner round rect
873     float width = rrect.GetRect().GetWidth() - LEFTW - RIGHTW;
874     float height = rrect.GetRect().GetHeight() - TOPW - BOTTOMW;
875     if (width > 0) {
876         // kc is linear ratio of inner rect
877         float kc = height / width;
878         if (RIGHTW > 0) {
879             // k is linear ratio for external rect (cutting angle at bottom right corner)
880             float k = BOTTOMW / RIGHTW;
881             // raduii values of external round rect for calculating clipping point
882             Drawing::Point brRad = rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_RIGHT_POS);
883             Drawing::Point blRad = rrect.GetCornerRadius(Drawing::RoundRect::BOTTOM_LEFT_POS);
884             Drawing::Point trRad = rrect.GetCornerRadius(Drawing::RoundRect::TOP_RIGHT_POS);
885             // shows what center axis of inner round rect we intersect fist (x or y)
886             if (k <= kc) {
887                 // bottom left and right raduii for inner round rect
888                 float drx = std::max(brRad.GetX() - RIGHTW, 0.f);
889                 float dlx = std::max(blRad.GetX() - LEFTW, 0.f);
890                 // calc delta to prevent overlapping of bottom left corner
891                 x = (drx > 0) ? (x - std::min(drx, width / 2.f - dlx)) : (x + width / 2.f);
892                 y = rrect.GetRect().GetBottom() - (rrect.GetRect().GetRight() - x) * k;
893             } else {
894                 // right bottom and top raduii for inner round rect
895                 float dby = std::max(brRad.GetY() - BOTTOMW, 0.f);
896                 float dty = std::max(trRad.GetY() - TOPW, 0.f);
897                 // calc delta to prevent overlapping of top right corner
898                 y = (dby > 0) ? (y = y - std::min(dby, height / 2.f - dty)) : (y + height / 2.f);
899                 x = rrect.GetRect().GetRight() - (rrect.GetRect().GetBottom() - y) / k;
900             }
901         } else {
902             x = rrect.GetRect().GetLeft() + rrect.GetRect().GetWidth();
903             y = std::min(y + height / 2.f,
904                          std::max(rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() / 2.f,
905                                   rrect.GetRect().GetTop() + TOPW));
906         }
907     } else {
908         y = rrect.GetRect().GetTop() + rrect.GetRect().GetHeight() - BOTTOMW;
909     }
910     return Drawing::Point(x, y);
911 }
912 
ToString() const913 std::string RSBorder::ToString() const
914 {
915     std::stringstream ss;
916     if (colors_.size() > 0) {
917         ss << "colors: ";
918     }
919     for (auto color : colors_) {
920         ss << color.AsArgbInt() << ", ";
921     }
922     if (widths_.size() > 0) {
923         ss << "widths: ";
924     }
925     for (auto width : widths_) {
926         ss << width << ", ";
927     }
928     if (styles_.size() > 0) {
929         ss << "styles: ";
930     }
931     for (auto style : styles_) {
932         ss << static_cast<uint32_t>(style) << ", ";
933     }
934     std::string output = ss.str();
935     return output;
936 }
937 
HasBorder() const938 bool RSBorder::HasBorder() const
939 {
940     return !colors_.empty() && !widths_.empty() && !styles_.empty() &&
941         !std::all_of(colors_.begin(), colors_.end(), [](const Color& color) { return color.GetAlpha() == 0; }) &&
942         !std::all_of(widths_.begin(), widths_.end(), [](const float& width) { return width <= 0.f; }) &&
943         !std::all_of(
944             styles_.begin(), styles_.end(), [](const BorderStyle& style) { return style == BorderStyle::NONE; });
945 }
946 } // namespace Rosen
947 } // namespace OHOS
948