1/* 2 * Copyright (c) 2020-2021 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 "components/ui_chart.h" 17#include "engines/gfx/gfx_engine_manager.h" 18#include "securec.h" 19 20namespace OHOS { 21UIChart::~UIChart() 22{ 23 if (mixData_ != nullptr) { 24 UIFree(mixData_); 25 mixData_ = nullptr; 26 } 27 ClearDataSerial(); 28 Remove(&xAxis_); 29 Remove(&yAxis_); 30} 31 32void UIChart::SetHeight(int16_t height) 33{ 34 if (GetHeight() == height) { 35 return; 36 } 37 38 if (height > 0) { 39 needRefresh_ = true; 40 } 41 42 UIView::SetHeight(height); 43 xAxis_.SetHeight(height); 44 xAxis_.UpdateAxis(); 45 yAxis_.SetHeight(height); 46 yAxis_.UpdateAxis(); 47} 48 49void UIChart::SetWidth(int16_t width) 50{ 51 UIView::SetWidth(width); 52 xAxis_.SetWidth(width); 53 yAxis_.SetWidth(width); 54 xAxis_.UpdateAxis(); 55 yAxis_.UpdateAxis(); 56} 57 58void UIChart::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea) 59{ 60 UIViewGroup::OnDraw(gfxDstBuffer, invalidatedArea); 61 DrawDataSerials(gfxDstBuffer, invalidatedArea); 62} 63 64bool UIChart::AddDataSerial(UIChartDataSerial* dataSerial) 65{ 66 if (dataSerial == nullptr) { 67 return false; 68 } 69 70 ListNode<UIChartDataSerial*>* serialNode = list_.Head(); 71 while (serialNode != list_.End()) { 72 if (serialNode->data_ == dataSerial) { 73 return false; 74 } 75 serialNode = serialNode->next_; 76 } 77 list_.PushBack(dataSerial); 78 dataSerial->BindToChart(this); 79 return true; 80} 81 82bool UIChart::DeleteDataSerial(UIChartDataSerial* dataSerial) 83{ 84 if ((dataSerial == nullptr) || list_.IsEmpty()) { 85 return false; 86 } 87 88 bool findSerial = false; 89 ListNode<UIChartDataSerial*>* serialNode = list_.Head(); 90 while (serialNode != list_.End()) { 91 if (serialNode->data_ == dataSerial) { 92 dataSerial->BindToChart(nullptr); 93 list_.Remove(serialNode); 94 findSerial = true; 95 break; 96 } 97 serialNode = serialNode->next_; 98 } 99 100 return findSerial; 101} 102 103void UIChart::ClearDataSerial() 104{ 105 if (list_.IsEmpty()) { 106 return; 107 } 108 109 ListNode<UIChartDataSerial*>* serialNode = list_.Head(); 110 while (serialNode != list_.End()) { 111 serialNode->data_->BindToChart(nullptr); 112 ListNode<UIChartDataSerial*>* tempNode = serialNode; 113 serialNode = serialNode->next_; 114 list_.Remove(tempNode); 115 } 116 list_.Clear(); 117} 118 119UIChartDataSerial::UIChartDataSerial() 120 : maxCount_(0), 121 pointArray_(nullptr), 122 serialColor_(Color::White()), 123 fillColor_(Color::White()), 124 dataCount_(0), 125 peakPointIndex_(0), 126 peakData_(0), 127 valleyData_(0), 128 valleyPointIndex_(0), 129 lastPointIndex_(0), 130 latestIndex_(0), 131 hideIndex_(0), 132 hideCount_(0), 133 smooth_(false), 134 enableGradient_(false), 135 enableHeadPoint_(false), 136 enableTopPoint_(false), 137 enableBottomPoint_(false), 138 chart_(nullptr), 139 invalidateRect_(0, 0, 0, 0) 140{ 141 PointStyle style; 142 style.radius = DEFAULT_POINT_RADIUS; 143 style.strokeWidth = 1; 144 style.fillColor = Color::White(); 145 style.strokeColor = Color::White(); 146 topPointStyle_ = style; 147 bottomPointStyle_ = style; 148 headPointStyle_ = style; 149} 150 151bool UIChartDataSerial::SetMaxDataCount(uint16_t maxCount) 152{ 153 if (maxCount > MAX_POINTS_COUNT) { 154 maxCount = MAX_POINTS_COUNT; 155 } 156 157 if (maxCount == maxCount_) { 158 return true; 159 } 160 161 if (pointArray_ != nullptr) { 162 UIFree(pointArray_); 163 pointArray_ = nullptr; 164 } 165 166 maxCount_ = maxCount; 167 if (maxCount_ == 0) { 168 return true; 169 } 170 171 pointArray_ = static_cast<Point*>(UIMalloc(sizeof(Point) * maxCount_)); 172 if (pointArray_ == nullptr) { 173 maxCount_ = 0; 174 return false; 175 } 176 return true; 177} 178 179bool UIChartDataSerial::ModifyPoint(uint16_t index, const Point& point) 180{ 181 if ((index >= maxCount_) || (pointArray_ == nullptr)) { 182 return false; 183 } 184 185 pointArray_[index].x = point.x; 186 pointArray_[index].y = point.y; 187 if (point.y > peakData_) { 188 if (enableTopPoint_) { 189 RefreshInvalidateRect(peakPointIndex_, topPointStyle_); 190 } 191 peakPointIndex_ = index; 192 peakData_ = point.y; 193 } else if (point.y < valleyData_) { 194 if (enableBottomPoint_) { 195 RefreshInvalidateRect(valleyPointIndex_, bottomPointStyle_); 196 } 197 valleyPointIndex_ = index; 198 valleyData_ = point.y; 199 } else if ((index == peakPointIndex_) || (index == valleyPointIndex_)) { 200 UpdatePeakAndValley(0, dataCount_); 201 } 202 203 latestIndex_ = index; 204 uint16_t startIndex = (index == 0) ? index : (index - 1); 205 RefreshInvalidateRect(startIndex, index + 1); 206 return true; 207} 208 209bool UIChartDataSerial::GetPoint(uint16_t index, Point& point) 210{ 211 if ((index >= dataCount_) || (pointArray_ == nullptr)) { 212 return false; 213 } 214 point = pointArray_[index]; 215 if (chart_ != nullptr) { 216 chart_->GetXAxis().TranslateToPixel(point.x); 217 chart_->GetYAxis().TranslateToPixel(point.y); 218 } 219 return true; 220} 221 222bool UIChartDataSerial::GetOriginalPoint(uint16_t index, Point& point) 223{ 224 if ((index >= dataCount_) || (pointArray_ == nullptr)) { 225 return false; 226 } 227 point = pointArray_[index]; 228 return true; 229} 230 231bool UIChartDataSerial::PointArrayDup(Point** pointArrayBack) 232{ 233 *pointArrayBack = pointArray_; 234 pointArray_ = static_cast<Point*>(UIMalloc(sizeof(Point) * maxCount_)); 235 return memcpy_s(pointArray_, dataCount_ * sizeof(Point), *pointArrayBack, dataCount_ * sizeof(Point)) != EOK; 236} 237 238void UIChartDataSerial::HidePoint(uint16_t index, uint16_t count) 239{ 240 hideIndex_ = index; 241 hideCount_ = count; 242 RefreshInvalidateRect(hideIndex_, hideIndex_ + hideCount_); 243} 244 245void UIChartDataSerial::RefreshInvalidateRect(uint16_t pointIndex, const PointStyle& style) 246{ 247 Point point; 248 if (GetPoint(pointIndex, point)) { 249 uint16_t width = style.radius + style.strokeWidth; 250 Rect refresh(point.x - width, 0, point.x + width, 0); 251 if ((invalidateRect_.GetLeft() == 0) && (invalidateRect_.GetRight() == 0)) { 252 invalidateRect_ = refresh; 253 } else { 254 invalidateRect_.Join(invalidateRect_, refresh); 255 } 256 } 257} 258 259void UIChartDataSerial::RefreshInvalidateRect(uint16_t startIndex, uint16_t endIndex) 260{ 261 Point start; 262 GetPoint(startIndex, start); 263 Point end; 264 endIndex = (endIndex >= dataCount_) ? (dataCount_ - 1) : endIndex; 265 GetPoint(endIndex, end); 266 int16_t xMin = MATH_MIN(start.x, end.x); 267 int16_t xMax = MATH_MAX(start.x, end.x); 268 Rect refresh(xMin, 0, xMax, 0); 269 if ((invalidateRect_.GetLeft() == 0) && (invalidateRect_.GetRight() == 0)) { 270 invalidateRect_ = refresh; 271 return; 272 } 273 invalidateRect_.Join(invalidateRect_, refresh); 274} 275 276bool UIChartDataSerial::UpdatePeakAndValley(uint16_t startPos, uint16_t endPos) 277{ 278 if ((startPos >= endPos) || (endPos > dataCount_) || (pointArray_ == nullptr)) { 279 return false; 280 } 281 282 if (startPos == 0) { 283 peakData_ = pointArray_[startPos].y; 284 valleyData_ = pointArray_[startPos].y; 285 } 286 287 for (uint16_t i = startPos; i < endPos; i++) { 288 if (pointArray_[i].y > peakData_) { 289 if (enableTopPoint_) { 290 RefreshInvalidateRect(peakPointIndex_, topPointStyle_); 291 RefreshInvalidateRect(i, topPointStyle_); 292 } 293 peakPointIndex_ = i; 294 peakData_ = pointArray_[i].y; 295 } 296 297 if (pointArray_[i].y < valleyData_) { 298 if (enableBottomPoint_) { 299 RefreshInvalidateRect(valleyPointIndex_, bottomPointStyle_); 300 RefreshInvalidateRect(i, bottomPointStyle_); 301 } 302 valleyPointIndex_ = i; 303 valleyData_ = pointArray_[i].y; 304 } 305 } 306 return true; 307} 308 309bool UIChartDataSerial::AddPoints(const Point* data, uint16_t count) 310{ 311 if ((maxCount_ <= dataCount_) || (count == 0) || (pointArray_ == nullptr) || (data == nullptr)) { 312 return false; 313 } 314 315 if (count > (maxCount_ - dataCount_)) { 316 count = maxCount_ - dataCount_; 317 } 318 319 Point* current = pointArray_ + dataCount_; 320 if (memcpy_s(current, (maxCount_ - dataCount_) * sizeof(Point), data, count * sizeof(Point)) != EOK) { 321 return false; 322 } 323 uint16_t i = dataCount_; 324 dataCount_ += count; 325 UpdatePeakAndValley(i, dataCount_); 326 latestIndex_ = dataCount_ - 1; 327 uint16_t startIndex = (i == 0) ? i : (i - 1); 328 RefreshInvalidateRect(startIndex, latestIndex_); 329 return true; 330} 331 332void UIChartDataSerial::ClearData() 333{ 334 RefreshInvalidateRect(0, dataCount_ - 1); 335 if (pointArray_ != nullptr) { 336 if (memset_s(pointArray_, maxCount_ * sizeof(Point), 0, maxCount_ * sizeof(Point)) != EOK) { 337 return; 338 } 339 } 340 dataCount_ = 0; 341 valleyPointIndex_ = 0; 342 peakPointIndex_ = 0; 343 latestIndex_ = 0; 344} 345 346void UIChartDataSerial::DoDrawPoint(BufferInfo& gfxDstBuffer, 347 const Point& center, 348 const PointStyle& style, 349 const Rect& mask) 350{ 351 Style drawStyle = StyleDefault::GetDefaultStyle(); 352 drawStyle.lineOpa_ = OPA_OPAQUE; 353 drawStyle.lineColor_ = style.fillColor; 354 355 ArcInfo arcinfo = {{0}}; 356 arcinfo.center = center; 357 arcinfo.imgPos = Point{0, 0}; 358 arcinfo.radius = style.radius + style.strokeWidth; 359 arcinfo.startAngle = 0; 360 arcinfo.endAngle = CIRCLE_IN_DEGREE; 361 BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance(); 362 if (style.fillColor.full == style.strokeColor.full) { 363 drawStyle.lineWidth_ = style.radius + style.strokeWidth; 364 baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE); 365 return; 366 } 367 drawStyle.lineWidth_ = style.radius; 368 arcinfo.radius = style.radius; 369 baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE); 370 371 drawStyle.lineWidth_ = style.strokeWidth; 372 drawStyle.lineColor_ = style.strokeColor; 373 arcinfo.radius = style.radius + style.strokeWidth; 374 baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE); 375} 376 377void UIChartDataSerial::DrawPoint(BufferInfo& gfxDstBuffer, const Rect& mask) 378{ 379 Point center; 380 if (enableTopPoint_) { 381 if (GetPoint(peakPointIndex_, center)) { 382 DoDrawPoint(gfxDstBuffer, center, topPointStyle_, mask); 383 } 384 } 385 386 if (enableBottomPoint_) { 387 if (GetPoint(valleyPointIndex_, center)) { 388 DoDrawPoint(gfxDstBuffer, center, bottomPointStyle_, mask); 389 } 390 } 391 392 if (enableHeadPoint_) { 393 if (GetPoint(latestIndex_, center)) { 394 DoDrawPoint(gfxDstBuffer, center, headPointStyle_, mask); 395 lastPointIndex_ = latestIndex_; 396 } 397 } 398} 399 400void UIChartDataSerial::Refresh() 401{ 402 if (chart_ != nullptr) { 403 Rect refresh = chart_->GetContentRect(); 404 refresh.SetLeft(invalidateRect_.GetLeft() - headPointStyle_.radius - headPointStyle_.strokeWidth); 405 refresh.SetRight(invalidateRect_.GetRight() + headPointStyle_.radius + headPointStyle_.strokeWidth); 406 invalidateRect_.SetRect(0, 0, 0, 0); 407 chart_->InvalidateRect(refresh); 408 409 if (enableHeadPoint_ && (lastPointIndex_ != latestIndex_)) { 410 RefreshInvalidateRect(lastPointIndex_, headPointStyle_); 411 refresh.SetLeft(invalidateRect_.GetLeft()); 412 refresh.SetRight(invalidateRect_.GetRight()); 413 chart_->InvalidateRect(refresh); 414 invalidateRect_.SetRect(0, 0, 0, 0); 415 } 416 } 417} 418 419void UIChartPillar::RefreshChart() 420{ 421 ListNode<UIChartDataSerial*>* iter = list_.Begin(); 422 Rect rect = GetContentRect(); 423 for (; iter != list_.End(); iter = iter->next_) { 424 UIChartDataSerial* data = iter->data_; 425 if (data == nullptr) { 426 break; 427 } 428 uint16_t dataCount = data->GetDataCount(); 429 if (dataCount <= 1) { 430 break; 431 } 432 433 uint16_t index = data->GetLastPointIndex(); 434 if (index >= dataCount) { 435 break; 436 } 437 438 Point current; 439 data->GetPoint(index, current); 440 Point last; 441 data->GetPoint(dataCount - 1, last); 442 Rect refresh(current.x, rect.GetTop(), last.x, rect.GetBottom()); 443 InvalidateRect(refresh); 444 data->SetLastPointIndex(dataCount - 1); 445 } 446} 447 448void UIChartPillar::DrawDataSerials(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea) 449{ 450 xAxis_.UpdateAxisPoints(); 451 yAxis_.UpdateAxisPoints(); 452 uint16_t minXStep = static_cast<uint16_t>(xAxis_.GetMarkInterval()); 453 Point xStart = xAxis_.GetStartPoint(); 454 uint16_t dataSerialCount = list_.Size(); 455 if (dataSerialCount == 0) { 456 return; 457 } 458 uint16_t width = minXStep / dataSerialCount; 459 uint8_t dataSerialIndex = 0; 460 uint16_t barWidth = static_cast<uint16_t>(width - DEFAULT_MARK_PERCENTAGE * (width << 1)); 461 462 for (ListNode<UIChartDataSerial*>* iter = list_.Begin(); iter != list_.End(); iter = iter->next_) { 463 UIChartDataSerial* data = iter->data_; 464 uint16_t dataSerialWidth = width * dataSerialIndex; 465 int16_t x = dataSerialWidth + (width >> 1); 466 for (uint16_t index = 0; index < data->GetDataCount(); index++) { 467 Point current; 468 data->GetPoint(index, current); 469 if (current.y == xStart.y) { 470 continue; 471 } 472 current.x += x; 473 xStart.x = current.x; 474 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, current, xStart, invalidatedArea, barWidth, 475 data->GetFillColor(), style_->lineOpa_); 476 } 477 dataSerialIndex++; 478 } 479} 480 481void UIChartPolyline::RefreshChart() 482{ 483 ListNode<UIChartDataSerial*>* iter = list_.Begin(); 484 for (; iter != list_.End(); iter = iter->next_) { 485 UIChartDataSerial* data = iter->data_; 486 uint16_t dataCount = data->GetDataCount(); 487 if (dataCount == 1) { 488 break; 489 } 490 data->Refresh(); 491 } 492} 493 494void UIChartPolyline::ReMeasure() 495{ 496 if (!needRefresh_) { 497 return; 498 } 499 needRefresh_ = false; 500 int16_t height = GetHeight(); 501 if (mixData_ != nullptr) { 502 UIFree(mixData_); 503 mixData_ = nullptr; 504 } 505 if (height <= 0) { 506 return; 507 } 508 if (height > COORD_MAX) { 509 height = COORD_MAX; 510 } 511 mixData_ = static_cast<uint8_t*>(UIMalloc(height)); 512 if (mixData_ == nullptr) { 513 return; 514 } 515 int16_t opa = maxOpa_ - minOpa_; 516 for (int16_t y = 0; y < height; y++) { 517 mixData_[y] = static_cast<uint8_t>(y * opa / height + minOpa_); 518 } 519} 520 521void UIChartPolyline::DrawDataSerials(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea) 522{ 523 xAxis_.UpdateAxisPoints(); 524 yAxis_.UpdateAxisPoints(); 525 ListNode<UIChartDataSerial*>* iter = list_.Begin(); 526 for (; iter != list_.End(); iter = iter->next_) { 527 UIChartDataSerial* data = iter->data_; 528 uint16_t dataCount = data->GetDataCount(); 529 if (dataCount <= 1) { 530 continue; 531 } 532 if (data->IsGradient()) { 533 if (data->IsSmooth()) { 534 Point* pointArrayBack = nullptr; 535 data->PointArrayDup(&pointArrayBack); 536 GetDataBySmooth(0, dataCount - 1, data); 537 GradientColor(gfxDstBuffer, invalidatedArea, data); 538 data->ClearData(); 539 data->AddPoints(pointArrayBack, dataCount); 540 UIFree(pointArrayBack); 541 } else { 542 GradientColor(gfxDstBuffer, invalidatedArea, data); 543 } 544 } 545 if (data->GetHideCount() != 0) { 546 uint16_t hideIndex = data->GetHideIndex(); 547 DrawPolyLine(gfxDstBuffer, 0, hideIndex, invalidatedArea, data); 548 DrawPolyLine(gfxDstBuffer, hideIndex + data->GetHideCount(), dataCount - 1, invalidatedArea, data); 549 } else { 550 DrawPolyLine(gfxDstBuffer, 0, dataCount - 1, invalidatedArea, data); 551 } 552 553 data->DrawPoint(gfxDstBuffer, invalidatedArea); 554 } 555} 556 557bool UIChartPolyline::Smooth(uint16_t startPos, 558 Point& start, 559 Point& end, 560 Point& current, 561 UIChartDataSerial* data, 562 uint16_t& slope, 563 uint16_t& preSlope) 564{ 565 data->GetPoint(startPos + 1, current); 566 if (((end.y - start.y <= 0) && (current.y - end.y <= 0)) || ((end.y - start.y >= 0) && (current.y - end.y >= 0))) { 567 slope = (current.x == start.x) ? QUARTER_IN_DEGREE : FastAtan2(current.x - start.x, current.y - start.y); 568 if (MATH_ABS(slope - preSlope) < SMOOTH_SLOPE_ANGLE) { 569 end = current; 570 return false; 571 } 572 } 573 preSlope = (current.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(current.x - end.x, current.y - end.y); 574 return true; 575} 576 577void UIChartPolyline::GetDataBySmooth(uint16_t startIndex, uint16_t endIndex, UIChartDataSerial* data) 578{ 579 if (data == nullptr) { 580 return; 581 } 582 Point* pointArray = static_cast<Point*>(UIMalloc(sizeof(Point) * data->GetDataCount())); 583 Point start; 584 Point end; 585 586 uint16_t slope; 587 data->GetPoint(startIndex, start); 588 data->GetOriginalPoint(startIndex, pointArray[0]); 589 int count = 1; 590 data->GetPoint(startIndex + 1, end); 591 uint16_t preSlope = (start.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(end.x - start.x, end.y - start.y); 592 Point current; 593 for (uint16_t i = startIndex; i < endIndex; i++) { 594 if (!Smooth(i, start, end, current, data, slope, preSlope)) { 595 continue; 596 } 597 data->GetOriginalPoint(i, pointArray[count++]); 598 start = end; 599 end = current; 600 } 601 data->GetOriginalPoint(endIndex, pointArray[count++]); 602 data->ClearData(); 603 data->AddPoints(pointArray, count); 604 UIFree(pointArray); 605} 606 607void UIChartPolyline::DrawSmoothPolyLine(BufferInfo& gfxDstBuffer, 608 uint16_t startIndex, 609 uint16_t endIndex, 610 const Rect& invalidatedArea, 611 UIChartDataSerial* data) 612{ 613 if (data == nullptr) { 614 return; 615 } 616 Point start; 617 Point end; 618 ColorType color = data->GetLineColor(); 619 Style style = *style_; 620 style.lineColor_ = color; 621 style.lineOpa_ = OPA_OPAQUE; 622 uint16_t slope; 623 data->GetPoint(startIndex, start); 624 data->GetPoint(startIndex + 1, end); 625 uint16_t preSlope = (start.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(end.x - start.x, end.y - start.y); 626 Point current; 627 BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance(); 628 for (uint16_t i = startIndex; i < endIndex; i++) { 629 if (!Smooth(i, start, end, current, data, slope, preSlope)) { 630 continue; 631 } 632 Rect rect; 633 rect.SetLeft(MATH_MIN(start.x, end.x) - style_->lineWidth_); 634 rect.SetRight(MATH_MAX(start.x, end.x) + style_->lineWidth_); 635 rect.SetTop(MATH_MIN(start.y, end.y) - style_->lineWidth_); 636 rect.SetBottom(MATH_MAX(start.y, end.y) + style_->lineWidth_); 637 if (!invalidatedArea.IsIntersect(rect)) { 638 start = end; 639 end = current; 640 continue; 641 } 642 baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE); 643 ArcInfo arcinfo = {{0}}; 644 arcinfo.center = end; 645 arcinfo.imgPos = Point{0, 0}; 646 arcinfo.radius = (style_->lineWidth_ + 1) >> 1; 647 arcinfo.startAngle = 0; 648 arcinfo.endAngle = CIRCLE_IN_DEGREE; 649 650 baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, invalidatedArea, style, OPA_OPAQUE, CapType::CAP_NONE); 651 start = end; 652 end = current; 653 } 654 baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE); 655} 656 657void UIChartPolyline::DrawPolyLine(BufferInfo& gfxDstBuffer, 658 uint16_t startIndex, 659 uint16_t endIndex, 660 const Rect& invalidatedArea, 661 UIChartDataSerial* data) 662{ 663 if ((startIndex >= endIndex) || (data == nullptr)) { 664 return; 665 } 666 667 if (data->IsSmooth()) { 668 DrawSmoothPolyLine(gfxDstBuffer, startIndex, endIndex, invalidatedArea, data); 669 return; 670 } 671 Point start; 672 Point end; 673 ColorType color = data->GetLineColor(); 674 Style style = *style_; 675 style.lineColor_ = color; 676 style.lineOpa_ = OPA_OPAQUE; 677 ArcInfo arcinfo = {{0}}; 678 arcinfo.imgPos = Point{0, 0}; 679 arcinfo.radius = (style_->lineWidth_ + 1) >> 1; 680 arcinfo.startAngle = 0; 681 arcinfo.endAngle = CIRCLE_IN_DEGREE; 682 BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance(); 683 for (uint16_t i = startIndex; i < endIndex - 1; i++) { 684 data->GetPoint(i, start); 685 data->GetPoint(i + 1, end); 686 Rect rect; 687 rect.SetLeft(MATH_MIN(start.x, end.x) - style_->lineWidth_); 688 rect.SetRight(MATH_MAX(start.x, end.x) + style_->lineWidth_); 689 rect.SetTop(MATH_MIN(start.y, end.y) - style_->lineWidth_); 690 rect.SetBottom(MATH_MAX(start.y, end.y) + style_->lineWidth_); 691 if (!invalidatedArea.IsIntersect(rect)) { 692 continue; 693 } 694 695 baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE); 696 if (style_->lineWidth_ >= LINE_JOIN_WIDTH) { 697 arcinfo.center = end; 698 baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, invalidatedArea, style, OPA_OPAQUE, CapType::CAP_NONE); 699 } 700 } 701 data->GetPoint(endIndex - 1, start); 702 data->GetPoint(endIndex, end); 703 baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE); 704} 705 706bool UIChartPolyline::GetLineCrossPoint(const Point& p1, 707 const Point& p2, 708 const Point& p3, 709 const Point& p4, 710 Point& cross) 711{ 712 /* Rectangular ranges of line segments must intersect. */ 713 if ((MATH_MIN(p1.x, p2.x) <= MATH_MAX(p3.x, p4.x)) && (MATH_MIN(p3.x, p4.x) <= MATH_MAX(p1.x, p2.x)) && 714 (MATH_MIN(p1.y, p2.y) <= MATH_MAX(p3.y, p4.y)) && (MATH_MIN(p3.y, p4.y) <= MATH_MAX(p1.y, p2.y))) { 715 /* Check whether the lines are parallel. If the lines are collinear, there is no intersection point. */ 716 if ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y) != 0) { 717 /* 718 * (y1 - y2)x + (x2 - x1)y = x2y1 - x1y2 -> ax + by = c 719 * (y3 - y4)x + (x4 - x3)y = x4y3 - x3y4 -> dx + ey = f 720 */ 721 int64_t a = p1.y - p2.y; 722 int64_t b = p2.x - p1.x; 723 int64_t c = p2.x * p1.y - p1.x * p2.y; 724 int64_t d = p3.y - p4.y; 725 int64_t e = p4.x - p3.x; 726 int64_t f = p4.x * p3.y - p3.x * p4.y; 727 int64_t left = a * e - b * d; 728 int64_t right = c * e - b * f; 729 if (left == 0) { 730 return false; 731 } 732 cross.x = static_cast<int16_t>(right / left); 733 left = b * d - a * e; 734 right = c * d - a * f; 735 if (left == 0) { 736 return false; 737 } 738 cross.y = static_cast<int16_t>(right / left); 739 if ((cross.x >= MATH_MIN(p1.x, p2.x)) && (cross.x <= MATH_MAX(p1.x, p2.x)) && 740 (cross.x >= MATH_MIN(p3.x, p4.x)) && (cross.x <= MATH_MAX(p3.x, p4.x))) { 741 return true; 742 } 743 } 744 } 745 if ((MATH_MIN(p1.x, p2.x) <= MATH_MAX(p3.x, p4.x)) && (MATH_MIN(p3.x, p4.x) <= MATH_MAX(p1.x, p2.x)) && 746 (MATH_MIN(p1.y, p2.y) >= MATH_MAX(p3.y, p4.y)) && (MATH_MIN(p3.y, p4.y) <= MATH_MAX(p1.y, p2.y))) { 747 return enableReverse_ ? true : false; 748 } 749 return false; 750} 751 752void UIChartPolyline::FindCrossPoints(const ChartLine& line, const ChartLine& polyLine, CrossPointSet& cross) 753{ 754 if (GetLineCrossPoint(line.start, line.end, polyLine.start, polyLine.end, cross.nextFirst)) { 755 if (enableReverse_ && (MATH_MIN(line.start.y, line.end.y) >= MATH_MAX(polyLine.start.y, polyLine.end.y))) { 756 if (!cross.firstFind) { 757 if (polyLine.start.y < polyLine.end.y) { 758 cross.first = cross.nextFirst; 759 cross.firstFind = false; 760 } 761 } else if (!cross.secondFind) { 762 if ((cross.first.x != cross.nextFirst.x) || (cross.first.y != cross.nextFirst.y)) { 763 cross.second = cross.nextFirst; 764 cross.secondFind = true; 765 return; 766 } 767 if (polyLine.start.y > polyLine.end.y) { 768 cross.firstFind = true; 769 } 770 } 771 return; 772 } 773 if (!cross.firstFind) { 774 /* first corss must on the line like "/" */ 775 if (polyLine.start.y < polyLine.end.y) { 776 cross.first = cross.nextFirst; 777 cross.firstFind = true; 778 } 779 } else if (!cross.secondFind) { 780 /* second corss can't be same with first cross. */ 781 if ((cross.first.x != cross.nextFirst.x) || (cross.first.y != cross.nextFirst.y)) { 782 cross.second = cross.nextFirst; 783 cross.secondFind = true; 784 return; 785 } 786 /* second corss must on the line like "\", otherwise skip those crosss. */ 787 if (polyLine.start.y > polyLine.end.y) { 788 cross.firstFind = false; 789 } 790 } 791 } 792} 793 794void UIChartPolyline::DrawGradientColor(BufferInfo& gfxDstBuffer, 795 const Rect& invalidatedArea, 796 UIChartDataSerial* data, 797 const ChartLine& linePoints, 798 const ChartLine& limitPoints, 799 int16_t startY) 800{ 801 if (data == nullptr) { 802 return; 803 } 804 Rect currentRect = GetContentRect(); 805 CrossPointSet cross = {{0}}; 806 ChartLine polyLine = {{0}}; 807 uint16_t pointCount = data->GetDataCount() - 1; 808 int16_t y = enableReverse_ ? (linePoints.start.y + startY) : (startY - linePoints.start.y); 809 int16_t mixScale = !enableReverse_ ? (currentRect.GetBottom() - y) : (y - currentRect.GetTop()); 810 if ((mixScale < 0) || (mixScale >= currentRect.GetHeight())) { 811 return; 812 } 813 bool onVerticalLine = enableReverse_ ? (y <= limitPoints.start.y) : (y >= limitPoints.start.y); 814 if (onVerticalLine) { 815 cross.first.x = limitPoints.start.x; 816 cross.first.y = enableReverse_ ? (y - startY) : (startY - y); 817 cross.firstFind = true; 818 } 819 Point start; 820 Point end; 821 BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance(); 822 for (uint16_t i = 0; i < pointCount; i++) { 823 data->GetPoint(i, start); 824 data->GetPoint(i + 1, end); 825 if (start.y == end.y) { 826 int16_t tmpY = enableReverse_ ? (start.y + startY) : (startY - start.y); 827 if (tmpY == linePoints.start.y) { 828 cross.firstFind = false; 829 cross.secondFind = false; 830 } 831 continue; 832 } 833 start.y = enableReverse_ ? (start.y - startY) : (startY - start.y); 834 end.y = enableReverse_ ? (end.y - startY) : (startY - end.y); 835 polyLine = {start, end}; 836 FindCrossPoints(linePoints, polyLine, cross); 837 SetDrawLineCross(gfxDstBuffer, invalidatedArea, data, cross, baseGfxEngine, startY, mixScale); 838 } 839 if (cross.firstFind && !cross.secondFind) { 840 cross.second = {limitPoints.end.x, y}; 841 cross.first.y = y; 842 baseGfxEngine->DrawLine(gfxDstBuffer, cross.first, cross.second, invalidatedArea, 1, data->GetFillColor(), 843 mixData_[mixScale]); 844 } 845} 846 847void UIChartPolyline::SetDrawLineCross(BufferInfo& gfxDstBuffer, 848 const Rect& invalidatedArea, 849 UIChartDataSerial* data, 850 CrossPointSet& cross, 851 BaseGfxEngine* baseGfxEngine, 852 int16_t startY, 853 int16_t mixScale) 854{ 855 if (cross.firstFind && cross.secondFind) { 856 cross.first.y = enableReverse_ ? (cross.first.y + startY) : (startY - cross.first.y); 857 cross.second.y = enableReverse_ ? (cross.second.y + startY) : (startY - cross.second.y); 858 baseGfxEngine->DrawLine(gfxDstBuffer, cross.first, cross.second, invalidatedArea, 1, data->GetFillColor(), 859 mixData_[mixScale]); 860 cross.firstFind = false; 861 cross.secondFind = false; 862 } 863} 864 865void UIChartPolyline::CalcVerticalInfo(int16_t top, 866 int16_t bottom, 867 int16_t start, 868 int16_t end, 869 int16_t& y, 870 int16_t& yHeight) 871{ 872 if ((top < start) && (bottom > start)) { 873 y = start; 874 yHeight = top; 875 } else if ((bottom <= start) && (top >= end)) { 876 y = bottom; 877 yHeight = top; 878 } else if ((top < end) && (bottom > end)) { 879 y = bottom; 880 yHeight = end; 881 } 882} 883 884void UIChartPolyline::GradientColor(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, UIChartDataSerial* data) 885{ 886 if (data == nullptr) { 887 return; 888 } 889 int16_t bottom = invalidatedArea.GetBottom(); 890 int16_t top = invalidatedArea.GetTop(); 891 Point yStart = yAxis_.GetStartPoint(); 892 yStart.y = enableReverse_ ? (yStart.y + gradientBottom_) : (yStart.y - gradientBottom_); 893 int16_t topY = enableReverse_ ? data->GetValleyData() : data->GetPeakData(); 894 int16_t bottomY = enableReverse_ ? data->GetPeakData() : data->GetValleyData(); 895 yAxis_.TranslateToPixel(topY); 896 yAxis_.TranslateToPixel(bottomY); 897 int16_t valleyY = enableReverse_ ? topY : bottomY; 898 int16_t startY = enableReverse_ ? topY : yStart.y; 899 int16_t endY = enableReverse_ ? yStart.y : topY; 900 if ((bottom < endY) || (top > startY)) { 901 return; 902 } 903 904 int16_t y = 0; 905 int16_t yHeight = 0; 906 CalcVerticalInfo(top, bottom, startY, endY, y, yHeight); 907 908 ChartLine limitPoints = {{0}}; 909 data->GetPoint(0, limitPoints.start); 910 data->GetPoint(data->GetDataCount() - 1, limitPoints.end); 911 ChartLine linePoints = {{0}}; 912 linePoints.start.x = limitPoints.start.x; 913 linePoints.end.x = limitPoints.end.x; 914 Rect currentRect = GetContentRect(); 915 BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance(); 916 while (y >= yHeight) { 917 linePoints.start.y = enableReverse_ ? (y - endY) : (startY - y); 918 linePoints.end.y = linePoints.start.y; 919 if (y <= valleyY) { 920 int16_t baseY = enableReverse_ ? endY : startY; 921 DrawGradientColor(gfxDstBuffer, invalidatedArea, data, linePoints, limitPoints, baseY); 922 } else { 923 int16_t mixScale = enableReverse_ ? (linePoints.start.y + endY - currentRect.GetTop()) : 924 (currentRect.GetBottom() - (startY - linePoints.start.y)); 925 if ((mixScale < 0) || (mixScale >= currentRect.GetHeight())) { 926 y--; 927 continue; 928 } 929 Point start = {limitPoints.start.x, y}; 930 Point end = {limitPoints.end.x, y}; 931 baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, 1, data->GetFillColor(), 932 mixData_[mixScale]); 933 } 934 y--; 935 } 936} 937} // namespace OHOS 938