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
20 namespace OHOS {
~UIChart()21 UIChart::~UIChart()
22 {
23 if (mixData_ != nullptr) {
24 UIFree(mixData_);
25 mixData_ = nullptr;
26 }
27 ClearDataSerial();
28 Remove(&xAxis_);
29 Remove(&yAxis_);
30 }
31
SetHeight(int16_t height)32 void 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
SetWidth(int16_t width)49 void 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
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)58 void UIChart::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
59 {
60 UIViewGroup::OnDraw(gfxDstBuffer, invalidatedArea);
61 DrawDataSerials(gfxDstBuffer, invalidatedArea);
62 }
63
AddDataSerial(UIChartDataSerial * dataSerial)64 bool 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
DeleteDataSerial(UIChartDataSerial * dataSerial)82 bool 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
ClearDataSerial()103 void 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
UIChartDataSerial()119 UIChartDataSerial::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
SetMaxDataCount(uint16_t maxCount)151 bool 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
ModifyPoint(uint16_t index,const Point & point)179 bool 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
GetPoint(uint16_t index,Point & point)209 bool 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
GetOriginalPoint(uint16_t index,Point & point)222 bool 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
PointArrayDup(Point ** pointArrayBack)231 bool 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
HidePoint(uint16_t index,uint16_t count)238 void UIChartDataSerial::HidePoint(uint16_t index, uint16_t count)
239 {
240 hideIndex_ = index;
241 hideCount_ = count;
242 RefreshInvalidateRect(hideIndex_, hideIndex_ + hideCount_);
243 }
244
RefreshInvalidateRect(uint16_t pointIndex,const PointStyle & style)245 void 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
RefreshInvalidateRect(uint16_t startIndex,uint16_t endIndex)259 void 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
UpdatePeakAndValley(uint16_t startPos,uint16_t endPos)276 bool 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
AddPoints(const Point * data,uint16_t count)309 bool 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
ClearData()332 void 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
DoDrawPoint(BufferInfo & gfxDstBuffer,const Point & center,const PointStyle & style,const Rect & mask)346 void 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
DrawPoint(BufferInfo & gfxDstBuffer,const Rect & mask)377 void 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
Refresh()400 void 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
RefreshChart()419 void 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
DrawDataSerials(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)448 void 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
RefreshChart()481 void 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
ReMeasure()494 void 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
DrawDataSerials(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)521 void 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
Smooth(uint16_t startPos,Point & start,Point & end,Point & current,UIChartDataSerial * data,uint16_t & slope,uint16_t & preSlope)557 bool 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
GetDataBySmooth(uint16_t startIndex,uint16_t endIndex,UIChartDataSerial * data)577 void 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
DrawSmoothPolyLine(BufferInfo & gfxDstBuffer,uint16_t startIndex,uint16_t endIndex,const Rect & invalidatedArea,UIChartDataSerial * data)607 void 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
DrawPolyLine(BufferInfo & gfxDstBuffer,uint16_t startIndex,uint16_t endIndex,const Rect & invalidatedArea,UIChartDataSerial * data)657 void 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
GetLineCrossPoint(const Point & p1,const Point & p2,const Point & p3,const Point & p4,Point & cross)706 bool 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
FindCrossPoints(const ChartLine & line,const ChartLine & polyLine,CrossPointSet & cross)752 void 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
DrawGradientColor(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,UIChartDataSerial * data,const ChartLine & linePoints,const ChartLine & limitPoints,int16_t startY)794 void 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
SetDrawLineCross(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,UIChartDataSerial * data,CrossPointSet & cross,BaseGfxEngine * baseGfxEngine,int16_t startY,int16_t mixScale)847 void 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
CalcVerticalInfo(int16_t top,int16_t bottom,int16_t start,int16_t end,int16_t & y,int16_t & yHeight)865 void 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
GradientColor(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,UIChartDataSerial * data)884 void 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