• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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