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