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
HidePoint(uint16_t index,uint16_t count)222 void UIChartDataSerial::HidePoint(uint16_t index, uint16_t count)
223 {
224 hideIndex_ = index;
225 hideCount_ = count;
226 RefreshInvalidateRect(hideIndex_, hideIndex_ + hideCount_);
227 }
228
RefreshInvalidateRect(uint16_t pointIndex,const PointStyle & style)229 void UIChartDataSerial::RefreshInvalidateRect(uint16_t pointIndex, const PointStyle& style)
230 {
231 Point point;
232 if (GetPoint(pointIndex, point)) {
233 uint16_t width = style.radius + style.strokeWidth;
234 Rect refresh(point.x - width, 0, point.x + width, 0);
235 if ((invalidateRect_.GetLeft() == 0) && (invalidateRect_.GetRight() == 0)) {
236 invalidateRect_ = refresh;
237 } else {
238 invalidateRect_.Join(invalidateRect_, refresh);
239 }
240 }
241 }
242
RefreshInvalidateRect(uint16_t startIndex,uint16_t endIndex)243 void UIChartDataSerial::RefreshInvalidateRect(uint16_t startIndex, uint16_t endIndex)
244 {
245 Point start;
246 GetPoint(startIndex, start);
247 Point end;
248 endIndex = (endIndex >= dataCount_) ? (dataCount_ - 1) : endIndex;
249 GetPoint(endIndex, end);
250 int16_t xMin = MATH_MIN(start.x, end.x);
251 int16_t xMax = MATH_MAX(start.x, end.x);
252 Rect refresh(xMin, 0, xMax, 0);
253 if ((invalidateRect_.GetLeft() == 0) && (invalidateRect_.GetRight() == 0)) {
254 invalidateRect_ = refresh;
255 return;
256 }
257 invalidateRect_.Join(invalidateRect_, refresh);
258 }
259
UpdatePeakAndValley(uint16_t startPos,uint16_t endPos)260 bool UIChartDataSerial::UpdatePeakAndValley(uint16_t startPos, uint16_t endPos)
261 {
262 if ((startPos >= endPos) || (endPos > dataCount_) || (pointArray_ == nullptr)) {
263 return false;
264 }
265
266 if (startPos == 0) {
267 peakData_ = pointArray_[startPos].y;
268 valleyData_ = pointArray_[startPos].y;
269 }
270
271 for (uint16_t i = startPos; i < endPos; i++) {
272 if (pointArray_[i].y > peakData_) {
273 if (enableTopPoint_) {
274 RefreshInvalidateRect(peakPointIndex_, topPointStyle_);
275 RefreshInvalidateRect(i, topPointStyle_);
276 }
277 peakPointIndex_ = i;
278 peakData_ = pointArray_[i].y;
279 }
280
281 if (pointArray_[i].y < valleyData_) {
282 if (enableBottomPoint_) {
283 RefreshInvalidateRect(valleyPointIndex_, bottomPointStyle_);
284 RefreshInvalidateRect(i, bottomPointStyle_);
285 }
286 valleyPointIndex_ = i;
287 valleyData_ = pointArray_[i].y;
288 }
289 }
290 return true;
291 }
292
AddPoints(const Point * data,uint16_t count)293 bool UIChartDataSerial::AddPoints(const Point* data, uint16_t count)
294 {
295 if ((maxCount_ <= dataCount_) || (count == 0) || (pointArray_ == nullptr) || (data == nullptr)) {
296 return false;
297 }
298
299 if (count > (maxCount_ - dataCount_)) {
300 count = maxCount_ - dataCount_;
301 }
302
303 Point* current = pointArray_ + dataCount_;
304 if (memcpy_s(current, (maxCount_ - dataCount_) * sizeof(Point), data, count * sizeof(Point)) != EOK) {
305 return false;
306 }
307 uint16_t i = dataCount_;
308 dataCount_ += count;
309 UpdatePeakAndValley(i, dataCount_);
310 latestIndex_ = dataCount_ - 1;
311 uint16_t startIndex = (i == 0) ? i : (i - 1);
312 RefreshInvalidateRect(startIndex, latestIndex_);
313 return true;
314 }
315
ClearData()316 void UIChartDataSerial::ClearData()
317 {
318 RefreshInvalidateRect(0, dataCount_ - 1);
319 if (pointArray_ != nullptr) {
320 if (memset_s(pointArray_, maxCount_ * sizeof(Point), 0, maxCount_ * sizeof(Point)) != EOK) {
321 return;
322 }
323 }
324 dataCount_ = 0;
325 valleyPointIndex_ = 0;
326 peakPointIndex_ = 0;
327 latestIndex_ = 0;
328 }
329
DoDrawPoint(BufferInfo & gfxDstBuffer,const Point & center,const PointStyle & style,const Rect & mask)330 void UIChartDataSerial::DoDrawPoint(BufferInfo& gfxDstBuffer, const Point& center,
331 const PointStyle& style, const Rect& mask)
332 {
333 Style drawStyle = StyleDefault::GetDefaultStyle();
334 drawStyle.lineOpa_ = OPA_OPAQUE;
335 drawStyle.lineColor_ = style.fillColor;
336
337 ArcInfo arcinfo = {{0}};
338 arcinfo.center = center;
339 arcinfo.imgPos = Point{0, 0};
340 arcinfo.radius = style.radius + style.strokeWidth;
341 arcinfo.startAngle = 0;
342 arcinfo.endAngle = CIRCLE_IN_DEGREE;
343
344 if (style.fillColor.full == style.strokeColor.full) {
345 drawStyle.lineWidth_ = style.radius + style.strokeWidth;
346 BaseGfxEngine::GetInstance()->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE);
347 return;
348 }
349 drawStyle.lineWidth_ = style.radius;
350 arcinfo.radius = style.radius;
351 BaseGfxEngine::GetInstance()->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE);
352
353 drawStyle.lineWidth_ = style.strokeWidth;
354 drawStyle.lineColor_ = style.strokeColor;
355 arcinfo.radius = style.radius + style.strokeWidth;
356 BaseGfxEngine::GetInstance()->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE);
357 }
358
DrawPoint(BufferInfo & gfxDstBuffer,const Rect & mask)359 void UIChartDataSerial::DrawPoint(BufferInfo& gfxDstBuffer, const Rect& mask)
360 {
361 Point center;
362 if (enableTopPoint_) {
363 if (GetPoint(peakPointIndex_, center)) {
364 DoDrawPoint(gfxDstBuffer, center, topPointStyle_, mask);
365 }
366 }
367
368 if (enableBottomPoint_) {
369 if (GetPoint(valleyPointIndex_, center)) {
370 DoDrawPoint(gfxDstBuffer, center, bottomPointStyle_, mask);
371 }
372 }
373
374 if (enableHeadPoint_) {
375 if (GetPoint(latestIndex_, center)) {
376 DoDrawPoint(gfxDstBuffer, center, headPointStyle_, mask);
377 lastPointIndex_ = latestIndex_;
378 }
379 }
380 }
381
Refresh()382 void UIChartDataSerial::Refresh()
383 {
384 if (chart_ != nullptr) {
385 Rect refresh = chart_->GetContentRect();
386 refresh.SetLeft(invalidateRect_.GetLeft() - headPointStyle_.radius - headPointStyle_.strokeWidth);
387 refresh.SetRight(invalidateRect_.GetRight() + headPointStyle_.radius + headPointStyle_.strokeWidth);
388 invalidateRect_.SetRect(0, 0, 0, 0);
389 chart_->InvalidateRect(refresh);
390
391 if (enableHeadPoint_ && (lastPointIndex_ != latestIndex_)) {
392 RefreshInvalidateRect(lastPointIndex_, headPointStyle_);
393 refresh.SetLeft(invalidateRect_.GetLeft());
394 refresh.SetRight(invalidateRect_.GetRight());
395 chart_->InvalidateRect(refresh);
396 invalidateRect_.SetRect(0, 0, 0, 0);
397 }
398 }
399 }
400
RefreshChart()401 void UIChartPillar::RefreshChart()
402 {
403 ListNode<UIChartDataSerial*>* iter = list_.Begin();
404 Rect rect = GetContentRect();
405 for (; iter != list_.End(); iter = iter->next_) {
406 UIChartDataSerial* data = iter->data_;
407 if (data == nullptr) {
408 break;
409 }
410 uint16_t dataCount = data->GetDataCount();
411 if (dataCount <= 1) {
412 break;
413 }
414
415 uint16_t index = data->GetLastPointIndex();
416 if (index >= dataCount) {
417 break;
418 }
419
420 Point current;
421 data->GetPoint(index, current);
422 Point last;
423 data->GetPoint(dataCount - 1, last);
424 Rect refresh(current.x, rect.GetTop(), last.x, rect.GetBottom());
425 InvalidateRect(refresh);
426 data->SetLastPointIndex(dataCount - 1);
427 }
428 }
429
DrawDataSerials(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)430 void UIChartPillar::DrawDataSerials(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
431 {
432 xAxis_.UpdateAxisPoints();
433 yAxis_.UpdateAxisPoints();
434 uint16_t minXStep = static_cast<uint16_t>(xAxis_.GetMarkInterval());
435 Point xStart = xAxis_.GetStartPoint();
436 uint16_t dataSerialCount = list_.Size();
437 if (dataSerialCount == 0) {
438 return;
439 }
440 uint16_t width = minXStep / dataSerialCount;
441 uint8_t dataSerialIndex = 0;
442 uint16_t barWidth = static_cast<uint16_t>(width - DEFAULT_MARK_PERCENTAGE * (width << 1));
443
444 for (ListNode<UIChartDataSerial*>* iter = list_.Begin(); iter != list_.End(); iter = iter->next_) {
445 UIChartDataSerial* data = iter->data_;
446 uint16_t dataSerialWidth = width * dataSerialIndex;
447 int16_t x = dataSerialWidth + (width >> 1);
448 for (uint16_t index = 0; index < data->GetDataCount(); index++) {
449 Point current;
450 data->GetPoint(index, current);
451 if (current.y == xStart.y) {
452 continue;
453 }
454 current.x += x;
455 xStart.x = current.x;
456 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, current, xStart, invalidatedArea, barWidth,
457 data->GetFillColor(), style_->lineOpa_);
458 }
459 dataSerialIndex++;
460 }
461 }
462
RefreshChart()463 void UIChartPolyline::RefreshChart()
464 {
465 ListNode<UIChartDataSerial*>* iter = list_.Begin();
466 for (; iter != list_.End(); iter = iter->next_) {
467 UIChartDataSerial* data = iter->data_;
468 uint16_t dataCount = data->GetDataCount();
469 if (dataCount == 1) {
470 break;
471 }
472 data->Refresh();
473 }
474 }
475
ReMeasure()476 void UIChartPolyline::ReMeasure()
477 {
478 if (!needRefresh_) {
479 return;
480 }
481 needRefresh_ = false;
482 int16_t height = GetHeight();
483 if (mixData_ != nullptr) {
484 UIFree(mixData_);
485 mixData_ = nullptr;
486 }
487 if (height <= 0) {
488 return;
489 }
490 if (height > COORD_MAX) {
491 height = COORD_MAX;
492 }
493 mixData_ = static_cast<uint8_t*>(UIMalloc(height));
494 if (mixData_ == nullptr) {
495 return;
496 }
497 int16_t opa = maxOpa_ - minOpa_;
498 for (int16_t y = 0; y < height; y++) {
499 mixData_[y] = static_cast<uint8_t>(y * opa / height + minOpa_);
500 }
501 }
502
DrawDataSerials(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)503 void UIChartPolyline::DrawDataSerials(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
504 {
505 xAxis_.UpdateAxisPoints();
506 yAxis_.UpdateAxisPoints();
507 ListNode<UIChartDataSerial*>* iter = list_.Begin();
508 for (; iter != list_.End(); iter = iter->next_) {
509 UIChartDataSerial* data = iter->data_;
510 uint16_t dataCount = data->GetDataCount();
511 if (dataCount <= 1) {
512 continue;
513 }
514 if (data->IsGradient()) {
515 GradientColor(gfxDstBuffer, invalidatedArea, data);
516 }
517 if (data->GetHideCount() != 0) {
518 uint16_t hideIndex = data->GetHideIndex();
519 DrawPolyLine(gfxDstBuffer, 0, hideIndex, invalidatedArea, data);
520 DrawPolyLine(gfxDstBuffer, hideIndex + data->GetHideCount(), dataCount - 1, invalidatedArea, data);
521 } else {
522 DrawPolyLine(gfxDstBuffer, 0, dataCount - 1, invalidatedArea, data);
523 }
524
525 data->DrawPoint(gfxDstBuffer, invalidatedArea);
526 }
527 }
528
DrawSmoothPolyLine(BufferInfo & gfxDstBuffer,uint16_t startIndex,uint16_t endIndex,const Rect & invalidatedArea,UIChartDataSerial * data)529 void UIChartPolyline::DrawSmoothPolyLine(BufferInfo& gfxDstBuffer,
530 uint16_t startIndex,
531 uint16_t endIndex,
532 const Rect& invalidatedArea,
533 UIChartDataSerial* data)
534 {
535 if (data == nullptr) {
536 return;
537 }
538 Point start;
539 Point end;
540 ColorType color = data->GetLineColor();
541 Style style = *style_;
542 style.lineColor_ = color;
543 style.lineOpa_ = OPA_OPAQUE;
544
545 uint16_t slope;
546 data->GetPoint(startIndex, start);
547 data->GetPoint(startIndex + 1, end);
548 uint16_t preSlope = (start.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(end.x - start.x, end.y - start.y);
549 Point current;
550 for (uint16_t i = startIndex; i < endIndex; i++) {
551 data->GetPoint(i + 1, current);
552 if (((end.y - start.y <= 0) && (current.y - end.y <= 0)) ||
553 ((end.y - start.y >= 0) && (current.y - end.y >= 0))) {
554 slope = (current.x == start.x) ? QUARTER_IN_DEGREE : FastAtan2(current.x - start.x, current.y - start.y);
555 if (MATH_ABS(slope - preSlope) < SMOOTH_SLOPE_ANGLE) {
556 end = current;
557 continue;
558 }
559 }
560 preSlope = (current.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(current.x - end.x, current.y - end.y);
561 Rect rect;
562 rect.SetLeft(MATH_MIN(start.x, end.x) - style_->lineWidth_);
563 rect.SetRight(MATH_MAX(start.x, end.x) + style_->lineWidth_);
564 rect.SetTop(MATH_MIN(start.y, end.y) - style_->lineWidth_);
565 rect.SetBottom(MATH_MAX(start.y, end.y) + style_->lineWidth_);
566 if (!invalidatedArea.IsIntersect(rect)) {
567 start = end;
568 end = current;
569 continue;
570 }
571
572 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, start, end, invalidatedArea,
573 style_->lineWidth_, color, OPA_OPAQUE);
574 ArcInfo arcinfo = {{0}};
575 arcinfo.center = end;
576 arcinfo.imgPos = Point{0, 0};
577 arcinfo.radius = (style_->lineWidth_ + 1) >> 1;
578 arcinfo.startAngle = 0;
579 arcinfo.endAngle = CIRCLE_IN_DEGREE;
580
581 BaseGfxEngine::GetInstance()->DrawArc(gfxDstBuffer, arcinfo, invalidatedArea,
582 style, OPA_OPAQUE, CapType::CAP_NONE);
583
584 start = end;
585 end = current;
586 }
587 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, start, end, invalidatedArea,
588 style_->lineWidth_, color, OPA_OPAQUE);
589 }
590
DrawPolyLine(BufferInfo & gfxDstBuffer,uint16_t startIndex,uint16_t endIndex,const Rect & invalidatedArea,UIChartDataSerial * data)591 void UIChartPolyline::DrawPolyLine(BufferInfo& gfxDstBuffer,
592 uint16_t startIndex,
593 uint16_t endIndex,
594 const Rect& invalidatedArea,
595 UIChartDataSerial* data)
596 {
597 if ((startIndex >= endIndex) || (data == nullptr)) {
598 return;
599 }
600
601 if (data->IsSmooth()) {
602 DrawSmoothPolyLine(gfxDstBuffer, startIndex, endIndex, invalidatedArea, data);
603 return;
604 }
605 Point start;
606 Point end;
607 ColorType color = data->GetLineColor();
608 Style style = *style_;
609 style.lineColor_ = color;
610 style.lineOpa_ = OPA_OPAQUE;
611 ArcInfo arcinfo = {{0}};
612 arcinfo.imgPos = Point{0, 0};
613 arcinfo.radius = (style_->lineWidth_ + 1) >> 1;
614 arcinfo.startAngle = 0;
615 arcinfo.endAngle = CIRCLE_IN_DEGREE;
616 for (uint16_t i = startIndex; i < endIndex - 1; i++) {
617 data->GetPoint(i, start);
618 data->GetPoint(i + 1, end);
619 Rect rect;
620 rect.SetLeft(MATH_MIN(start.x, end.x) - style_->lineWidth_);
621 rect.SetRight(MATH_MAX(start.x, end.x) + style_->lineWidth_);
622 rect.SetTop(MATH_MIN(start.y, end.y) - style_->lineWidth_);
623 rect.SetBottom(MATH_MAX(start.y, end.y) + style_->lineWidth_);
624 if (!invalidatedArea.IsIntersect(rect)) {
625 continue;
626 }
627
628 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, start, end, invalidatedArea,
629 style_->lineWidth_, color, OPA_OPAQUE);
630 if (style_->lineWidth_ >= LINE_JOIN_WIDTH) {
631 arcinfo.center = end;
632 BaseGfxEngine::GetInstance()->DrawArc(gfxDstBuffer, arcinfo, invalidatedArea,
633 style, OPA_OPAQUE, CapType::CAP_NONE);
634 }
635 }
636 data->GetPoint(endIndex - 1, start);
637 data->GetPoint(endIndex, end);
638 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, start, end, invalidatedArea,
639 style_->lineWidth_, color, OPA_OPAQUE);
640 }
641
GetLineCrossPoint(const Point & p1,const Point & p2,const Point & p3,const Point & p4,Point & cross)642 bool UIChartPolyline::GetLineCrossPoint(const Point& p1,
643 const Point& p2,
644 const Point& p3,
645 const Point& p4,
646 Point& cross)
647 {
648 /* Rectangular ranges of line segments must intersect. */
649 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)) &&
650 (MATH_MIN(p1.y, p2.y) <= MATH_MAX(p3.y, p4.y)) && (MATH_MIN(p3.y, p4.y) <= MATH_MAX(p1.y, p2.y))) {
651 /* Check whether the lines are parallel. If the lines are collinear, there is no intersection point. */
652 if ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y) != 0) {
653 /*
654 * (y1 - y2)x + (x2 - x1)y = x2y1 - x1y2 -> ax + by = c
655 * (y3 - y4)x + (x4 - x3)y = x4y3 - x3y4 -> dx + ey = f
656 */
657 int64_t a = p1.y - p2.y;
658 int64_t b = p2.x - p1.x;
659 int64_t c = p2.x * p1.y - p1.x * p2.y;
660 int64_t d = p3.y - p4.y;
661 int64_t e = p4.x - p3.x;
662 int64_t f = p4.x * p3.y - p3.x * p4.y;
663 int64_t left = a * e - b * d;
664 int64_t right = c * e - b * f;
665 if (left == 0) {
666 return false;
667 }
668 cross.x = static_cast<int16_t>(right / left);
669 left = b * d - a * e;
670 right = c * d - a * f;
671 if (left == 0) {
672 return false;
673 }
674 cross.y = static_cast<int16_t>(right / left);
675 if ((cross.x >= MATH_MIN(p1.x, p2.x)) && (cross.x <= MATH_MAX(p1.x, p2.x)) &&
676 (cross.x >= MATH_MIN(p3.x, p4.x)) && (cross.x <= MATH_MAX(p3.x, p4.x))) {
677 return true;
678 }
679 }
680 }
681 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)) &&
682 (MATH_MIN(p1.y, p2.y) >= MATH_MAX(p3.y, p4.y)) && (MATH_MIN(p3.y, p4.y) <= MATH_MAX(p1.y, p2.y))) {
683 return enableReverse_ ? true : false;
684 }
685 return false;
686 }
687
FindCrossPoints(const ChartLine & line,const ChartLine & polyLine,CrossPointSet & cross)688 void UIChartPolyline::FindCrossPoints(const ChartLine& line, const ChartLine& polyLine, CrossPointSet& cross)
689 {
690 if (GetLineCrossPoint(line.start, line.end, polyLine.start, polyLine.end, cross.nextFirst)) {
691 if (enableReverse_ && (MATH_MIN(line.start.y, line.end.y) >= MATH_MAX(polyLine.start.y, polyLine.end.y))) {
692 if (!cross.firstFind) {
693 if (polyLine.start.y < polyLine.end.y) {
694 cross.first = cross.nextFirst;
695 cross.firstFind = false;
696 }
697 } else if (!cross.secondFind) {
698 if ((cross.first.x != cross.nextFirst.x) || (cross.first.y != cross.nextFirst.y)) {
699 cross.second = cross.nextFirst;
700 cross.secondFind = true;
701 return;
702 }
703 if (polyLine.start.y > polyLine.end.y) {
704 cross.firstFind = true;
705 }
706 }
707 return;
708 }
709 if (!cross.firstFind) {
710 /* first corss must on the line like "/" */
711 if (polyLine.start.y < polyLine.end.y) {
712 cross.first = cross.nextFirst;
713 cross.firstFind = true;
714 }
715 } else if (!cross.secondFind) {
716 /* second corss can't be same with first cross. */
717 if ((cross.first.x != cross.nextFirst.x) || (cross.first.y != cross.nextFirst.y)) {
718 cross.second = cross.nextFirst;
719 cross.secondFind = true;
720 return;
721 }
722 /* second corss must on the line like "\", otherwise skip those crosss. */
723 if (polyLine.start.y > polyLine.end.y) {
724 cross.firstFind = false;
725 }
726 }
727 }
728 }
729
DrawGradientColor(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,UIChartDataSerial * data,const ChartLine & linePoints,const ChartLine & limitPoints,int16_t startY)730 void UIChartPolyline::DrawGradientColor(BufferInfo& gfxDstBuffer,
731 const Rect& invalidatedArea,
732 UIChartDataSerial* data,
733 const ChartLine& linePoints,
734 const ChartLine& limitPoints,
735 int16_t startY)
736 {
737 if (data == nullptr) {
738 return;
739 }
740 Rect currentRect = GetContentRect();
741 CrossPointSet cross = {{0}};
742 ChartLine polyLine = {{0}};
743 uint16_t pointCount = data->GetDataCount() - 1;
744 int16_t y = enableReverse_ ? (linePoints.start.y + startY) : (startY - linePoints.start.y);
745 int16_t mixScale = !enableReverse_ ? (currentRect.GetBottom() - y) : (y - currentRect.GetTop());
746 if ((mixScale < 0) || (mixScale >= currentRect.GetHeight())) {
747 return;
748 }
749 bool onVerticalLine = enableReverse_ ? (y <= limitPoints.start.y) : (y >= limitPoints.start.y);
750 if (onVerticalLine) {
751 cross.first.x = limitPoints.start.x;
752 cross.first.y = enableReverse_ ? (y - startY) : (startY - y);
753 cross.firstFind = true;
754 }
755
756 Point start;
757 Point end;
758 for (uint16_t i = 0; i < pointCount; i++) {
759 data->GetPoint(i, start);
760 data->GetPoint(i + 1, end);
761 if (start.y == end.y) {
762 int16_t tmpY = enableReverse_ ? (start.y + startY) : (startY - start.y);
763 if (tmpY == linePoints.start.y) {
764 cross.firstFind = false;
765 cross.secondFind = false;
766 }
767 continue;
768 }
769 start.y = enableReverse_ ? (start.y - startY) : (startY - start.y);
770 end.y = enableReverse_ ? (end.y - startY) : (startY - end.y);
771 polyLine = { start, end };
772 FindCrossPoints(linePoints, polyLine, cross);
773 if (cross.firstFind && cross.secondFind) {
774 cross.first.y = enableReverse_ ? (cross.first.y + startY) : (startY - cross.first.y);
775 cross.second.y = enableReverse_ ? (cross.second.y + startY) : (startY - cross.second.y);
776 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, cross.first, cross.second,
777 invalidatedArea, 1, data->GetFillColor(), mixData_[mixScale]);
778 cross.firstFind = false;
779 cross.secondFind = false;
780 }
781 }
782
783 if (cross.firstFind && !cross.secondFind) {
784 cross.second = { limitPoints.end.x, y };
785 cross.first.y = y;
786 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, cross.first, cross.second,
787 invalidatedArea, 1, data->GetFillColor(), mixData_[mixScale]);
788 }
789 }
790
CalcVerticalInfo(int16_t top,int16_t bottom,int16_t start,int16_t end,int16_t & y,int16_t & yHeight)791 void UIChartPolyline::CalcVerticalInfo(int16_t top,
792 int16_t bottom,
793 int16_t start,
794 int16_t end,
795 int16_t& y,
796 int16_t& yHeight)
797 {
798 if ((top < start) && (bottom > start)) {
799 y = start;
800 yHeight = top;
801 } else if ((bottom <= start) && (top >= end)) {
802 y = bottom;
803 yHeight = top;
804 } else if ((top < end) && (bottom > end)) {
805 y = bottom;
806 yHeight = end;
807 }
808 }
809
GradientColor(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,UIChartDataSerial * data)810 void UIChartPolyline::GradientColor(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, UIChartDataSerial* data)
811 {
812 if (data == nullptr) {
813 return;
814 }
815 int16_t bottom = invalidatedArea.GetBottom();
816 int16_t top = invalidatedArea.GetTop();
817 Point yStart = yAxis_.GetStartPoint();
818 yStart.y = enableReverse_ ? (yStart.y + gradientBottom_) : (yStart.y - gradientBottom_);
819 int16_t topY = enableReverse_ ? data->GetValleyData() : data->GetPeakData();
820 int16_t bottomY = enableReverse_ ? data->GetPeakData() : data->GetValleyData();
821 yAxis_.TranslateToPixel(topY);
822 yAxis_.TranslateToPixel(bottomY);
823 int16_t valleyY = enableReverse_ ? topY : bottomY;
824 int16_t startY = enableReverse_ ? topY : yStart.y;
825 int16_t endY = enableReverse_ ? yStart.y : topY;
826 if ((bottom < endY) || (top > startY)) {
827 return;
828 }
829
830 int16_t y = 0;
831 int16_t yHeight = 0;
832 CalcVerticalInfo(top, bottom, startY, endY, y, yHeight);
833
834 ChartLine limitPoints = {{0}};
835 data->GetPoint(0, limitPoints.start);
836 data->GetPoint(data->GetDataCount() - 1, limitPoints.end);
837 ChartLine linePoints = {{0}};
838 linePoints.start.x = limitPoints.start.x;
839 linePoints.end.x = limitPoints.end.x;
840 Rect currentRect = GetContentRect();
841 while (y >= yHeight) {
842 linePoints.start.y = enableReverse_ ? (y - endY) : (startY - y);
843 linePoints.end.y = linePoints.start.y;
844 if (y <= valleyY) {
845 int16_t baseY = enableReverse_ ? endY : startY;
846 DrawGradientColor(gfxDstBuffer, invalidatedArea, data, linePoints, limitPoints, baseY);
847 } else {
848 int16_t mixScale = enableReverse_ ? (linePoints.start.y + endY - currentRect.GetTop()) :
849 (currentRect.GetBottom() - (startY - linePoints.start.y));
850 if ((mixScale < 0) || (mixScale >= currentRect.GetHeight())) {
851 y--;
852 continue;
853 }
854 Point start = {limitPoints.start.x, y};
855 Point end = {limitPoints.end.x, y};
856 BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, start, end, invalidatedArea, 1,
857 data->GetFillColor(), mixData_[mixScale]);
858 }
859 y--;
860 }
861 }
862 } // namespace OHOS
863