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