1 /*
2  * Copyright (c) 2023 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 "core/components_ng/pattern/data_panel/data_panel_modifier.h"
17 
18 #include <cmath>
19 
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/point_t.h"
22 #include "base/geometry/ng/rect_t.h"
23 #include "base/geometry/rect.h"
24 #include "base/geometry/rrect.h"
25 #include "base/utils/utils.h"
26 #include "core/components/common/properties/alignment.h"
27 #include "core/components/common/properties/color.h"
28 #include "core/components/data_panel/data_panel_theme.h"
29 #include "core/components/theme/theme_manager.h"
30 #include "core/components_ng/pattern/data_panel/data_panel_paint_property.h"
31 #include "core/components_ng/render/canvas_image.h"
32 #include "core/components_ng/render/drawing.h"
33 #include "core/components_ng/render/drawing_prop_convertor.h"
34 #include "core/pipeline/pipeline_base.h"
35 
36 namespace OHOS::Ace::NG {
37 namespace {
38 constexpr float FIXED_WIDTH = 1.0f;
39 constexpr float HALF_CIRCLE = 180.0f;
40 constexpr float WHOLE_CIRCLE = 360.0f;
41 constexpr float QUARTER_CIRCLE = 90.0f;
42 constexpr float MIN_CIRCLE = 0.03f;
43 constexpr float PERCENT_HALF = 0.5f;
44 constexpr float DIAMETER_TO_THICKNESS_RATIO = 0.12f;
45 constexpr uint32_t SHADOW_ALPHA = 0.4 * 255;
46 constexpr float ZERO_CORNER_RADIUS = 0.0f;
47 } // namespace
48 
DataPanelModifier()49 DataPanelModifier::DataPanelModifier()
50 {
51     auto pipelineContext = PipelineBase::GetCurrentContext();
52     CHECK_NULL_VOID(pipelineContext);
53     auto theme = pipelineContext->GetTheme<DataPanelTheme>();
54     auto colors = theme->GetColorsArray();
55 
56     date_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0);
57     for (size_t i = 0; i < MAX_COUNT; ++i) {
58         auto value = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0);
59         AttachProperty(value);
60         values_.emplace_back(value);
61     }
62     max_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(DEFAULT_MAX_VALUE);
63     trackBackgroundColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(theme->GetBackgroundColor()));
64     strokeWidth_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(theme->GetThickness().ConvertToPx());
65     isEffect_ = AceType::MakeRefPtr<PropertyBool>(true);
66     useContentModifier_ = AceType::MakeRefPtr<PropertyBool>(false);
67     AttachProperty(date_);
68     AttachProperty(max_);
69     AttachProperty(trackBackgroundColor_);
70     AttachProperty(strokeWidth_);
71     AttachProperty(isEffect_);
72 
73     shadowRadiusFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(theme->GetTrackShadowRadius().ConvertToPx());
74     shadowOffsetXFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(theme->GetTrackShadowOffsetX().ConvertToPx());
75     shadowOffsetYFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(theme->GetTrackShadowOffsetY().ConvertToPx());
76     AttachProperty(shadowRadiusFloat_);
77     AttachProperty(shadowOffsetXFloat_);
78     AttachProperty(shadowOffsetYFloat_);
79 
80     for (const auto& item : colors) {
81         Gradient gradient;
82         GradientColor gradientColorStart;
83         gradientColorStart.SetLinearColor(LinearColor(item.first));
84         gradientColorStart.SetDimension(Dimension(0.0));
85         gradient.AddColor(gradientColorStart);
86         GradientColor gradientColorEnd;
87         gradientColorEnd.SetLinearColor(LinearColor(item.second));
88         gradientColorEnd.SetDimension(Dimension(1.0));
89         gradient.AddColor(gradientColorEnd);
90 
91         auto gradientColor = AceType::MakeRefPtr<AnimatablePropertyVectorColor>(GradientArithmetic(gradient));
92         AttachProperty(gradientColor);
93         valueColors_.emplace_back(gradientColor);
94 
95         auto gradientShadowColor = AceType::MakeRefPtr<AnimatablePropertyVectorColor>(GradientArithmetic(gradient));
96         AttachProperty(gradientShadowColor);
97         shadowColors_.emplace_back(gradientShadowColor);
98     }
99 }
100 
onDraw(DrawingContext & context)101 void DataPanelModifier::onDraw(DrawingContext& context)
102 {
103     if (useContentModifier_->Get()) {
104         return;
105     }
106     if (dataPanelType_ == 0) {
107         PaintCircle(context, offset_);
108     } else {
109         PaintLinearProgress(context, offset_);
110     }
111 }
112 
UpdateDate()113 void DataPanelModifier::UpdateDate()
114 {
115     if (isEffect_->Get()) {
116         // When the date update, the animation will repeat once.
117         date_->Set(ANIMATION_START);
118         AnimationOption option = AnimationOption();
119         RefPtr<Curve> curve = AceType::MakeRefPtr<SpringCurve>(
120             ANIMATION_CURVE_VELOCITY, ANIMATION_CURVE_MASS, ANIMATION_CURVE_STIFFNESS, ANIMATION_CURVE_DAMPING);
121         option.SetDuration(ANIMATION_DURATION);
122         option.SetDelay(ANIMATION_DELAY);
123         option.SetCurve(curve);
124         option.SetIteration(ANIMATION_TIMES);
125         AnimationUtils::Animate(option, [&]() { date_->Set(ANIMATION_END); });
126     } else {
127         date_->Set(ANIMATION_END);
128     }
129 }
130 
PaintCircle(DrawingContext & context,OffsetF offset) const131 void DataPanelModifier::PaintCircle(DrawingContext& context, OffsetF offset) const
132 {
133     RSCanvas& canvas = context.canvas;
134     canvas.Save();
135     canvas.Translate(offset.GetX(), offset.GetY());
136 
137     auto defaultThickness = strokeWidth_->Get();
138     ArcData arcData;
139     Offset center = Offset(context.width * PERCENT_HALF, context.height * PERCENT_HALF);
140     arcData.center = center;
141     // Here radius will minus defaultThickness, when there will be new api to set padding, use the new padding.
142     arcData.radius = std::min(context.width, context.height) * PERCENT_HALF - defaultThickness;
143     if (defaultThickness >= arcData.radius) {
144         arcData.thickness = arcData.radius * DIAMETER_TO_THICKNESS_RATIO;
145     } else {
146         arcData.thickness = defaultThickness;
147     }
148     PaintTrackBackground(canvas, arcData, trackBackgroundColor_->Get().ToColor());
149     arcData.maxValue = max_->Get();
150     for (size_t i = 0; i < valuesLastLength_; ++i) {
151         arcData.totalAllValue += values_[i]->Get();
152     }
153     if (NonPositive(arcData.totalAllValue)) {
154         // all values are invalid
155         return;
156     }
157 
158     arcData.totalDrawAngle = (arcData.totalAllValue * date_->Get()) / arcData.maxValue * WHOLE_CIRCLE;
159     arcData.totalDrawAngle = std::min(arcData.totalDrawAngle, WHOLE_CIRCLE);
160     Offset shadowCenter = Offset(context.width * PERCENT_HALF + shadowOffsetXFloat_->Get(),
161         context.height * PERCENT_HALF + shadowOffsetYFloat_->Get());
162 
163     // paint shadows
164     if ((isShadowVisible_ && (isHasShadowValue_ || isEffect_->Get()))) {
165         for (size_t i = 0; i < shadowColorsLastLength_; ++i) {
166             if (NearZero(values_[i]->Get())) {
167                 continue;
168             }
169             arcData.shadowColor = SortGradientColorsOffset(shadowColors_[i]->Get().GetGradient());
170             auto totalValuePre = arcData.totalValue;
171             arcData.totalValue += values_[i]->Get();
172             if (GreatNotEqual(arcData.totalValue, arcData.maxValue)) {
173                 continue;
174             }
175             arcData.progressValue = arcData.totalValue * date_->Get();
176             arcData.drawAngle = arcData.progressValue / arcData.maxValue * WHOLE_CIRCLE;
177             arcData.drawAngle = arcData.lastAngle + std::max(arcData.drawAngle - arcData.lastAngle, MIN_CIRCLE);
178             arcData.drawAngle = std::min(arcData.drawAngle, WHOLE_CIRCLE);
179 
180             arcData.gradientPointBase = totalValuePre / arcData.totalValue;
181 
182 #ifndef USE_ROSEN_DRAWING
183             RSPath path;
184             RSPath endPath;
185 #else
186             RSRecordingPath path;
187             RSRecordingPath endPath;
188 #endif
189             arcData.center = shadowCenter;
190             GetPaintPath(arcData, path, endPath);
191             PaintProgress(canvas, arcData, path, endPath, true);
192             arcData.lastAngle = arcData.drawAngle;
193         }
194     }
195     arcData.lastAngle = 0.0f;
196     arcData.totalValue = 0.0f;
197     canvas.Restore();
198 
199     for (size_t i = 0; i < valuesLastLength_; ++i) {
200         if (NearZero(values_[i]->Get())) {
201             continue;
202         }
203         arcData.progressColors = SortGradientColorsOffset(valueColors_[i]->Get().GetGradient());
204         auto totalValuePre = arcData.totalValue;
205         arcData.totalValue += values_[i]->Get();
206         if (GreatNotEqual(arcData.totalValue, arcData.maxValue)) {
207             return;
208         }
209         arcData.progressValue = arcData.totalValue * date_->Get();
210         arcData.drawAngle = arcData.progressValue / arcData.maxValue * WHOLE_CIRCLE;
211         arcData.drawAngle = arcData.lastAngle + std::max(arcData.drawAngle - arcData.lastAngle, MIN_CIRCLE);
212         arcData.drawAngle = std::min(arcData.drawAngle, WHOLE_CIRCLE);
213 
214         arcData.gradientPointBase = totalValuePre / arcData.totalValue;
215 
216 #ifndef USE_ROSEN_DRAWING
217         RSPath path;
218         RSPath endPath;
219 #else
220         RSRecordingPath path;
221         RSRecordingPath endPath;
222 #endif
223         arcData.center = center;
224         GetPaintPath(arcData, path, endPath);
225         PaintProgress(canvas, arcData, path, endPath, false);
226         arcData.lastAngle = arcData.drawAngle;
227     }
228     canvas.Restore();
229 }
230 
GetPaintPath(ArcData & arcData,RSPath & path,RSPath & endPath) const231 void DataPanelModifier::GetPaintPath(ArcData& arcData, RSPath& path, RSPath& endPath) const
232 {
233     float thickness = arcData.thickness;
234     float radius = arcData.radius;
235     float lastAngle = arcData.lastAngle;
236     float drawAngle = arcData.drawAngle;
237     float totalDrawAngle = arcData.totalDrawAngle;
238 
239     Offset center = arcData.center;
240 
241     float sine = thickness * PERCENT_HALF / (radius - (thickness * PERCENT_HALF));
242     float radian = asin(sine);
243     // the angle of center of start half circle to center and tangent of start half circle to center
244     arcData.circleAngle = radian * HALF_CIRCLE / M_PI;
245     float circleAngle = arcData.circleAngle;
246 
247     float startAngle = 0.0f;
248     // first line and start half circle not cover end half circle
249     if (NearZero(lastAngle) && LessOrEqual(drawAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
250         lastAngle += circleAngle;
251         drawAngle += circleAngle;
252         startAngle += circleAngle;
253         totalDrawAngle += circleAngle;
254     }
255     /*
256      * lastRadian: radian of last angle
257      * drawRadian: radian of this draw angle
258      * totalDrawRadian: radian of total draw angle
259      * startRadian: radian of draw start angle
260      * originDrawRadian: radian of total draw angle before rotate
261      * d: the distance between center of start half circle and end half circle
262      * midAngel: the angle of 0 angle and line of center of start half circle and center of end half circle
263      * tagAngle: the angle of line of center of start half circle and center of end half circle and
264                  line of intersection of start half circle and intersection of end half circle
265     */
266     float lastRadian = M_PI * lastAngle / HALF_CIRCLE;
267     float drawRadian = M_PI * drawAngle / HALF_CIRCLE;
268     float totalDrawRadian = M_PI * totalDrawAngle / HALF_CIRCLE;
269     float startRadian = M_PI * startAngle / HALF_CIRCLE;
270     float originDrawRadian = M_PI * arcData.totalDrawAngle / HALF_CIRCLE;
271     float d = std::sqrt(
272         std::pow(
273             (radius - thickness * PERCENT_HALF) - (radius - thickness * PERCENT_HALF) * std::cos(originDrawRadian), 2) +
274         std::pow((radius - thickness * PERCENT_HALF) * std::sin(originDrawRadian), 2));
275     float midAngle = QUARTER_CIRCLE;
276     float tagAngle = QUARTER_CIRCLE;
277     if (d > 0) {
278         midAngle = std::acos(std::abs((radius - thickness * PERCENT_HALF) -
279                                       (radius - thickness * PERCENT_HALF) * std::cos(originDrawRadian)) /
280                              d) *
281                    HALF_CIRCLE / M_PI;
282         tagAngle = std::acos((d / thickness)) * HALF_CIRCLE / M_PI;
283     }
284     /*
285      * path of start half circle:
286      * when it's not first line or total draw angle is a whole circle, path is a half circle inward
287      * when it's first line and end half circle not cover start cover circle, path is a half circle outword
288      * when it's first line and end half circle cover start cover circle, path is cut by end half circle
289      */
290     if (GreatNotEqual(arcData.lastAngle, START_ANGLE) || arcData.totalDrawAngle == WHOLE_CIRCLE) {
291         Path2DArc(path, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(lastRadian),
292             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(lastRadian), thickness * PERCENT_HALF,
293             lastAngle + HALF_CIRCLE, lastAngle, true);
294     } else if (LessOrEqual(arcData.totalDrawAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
295         Path2DArc(path, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(lastRadian),
296             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(lastRadian), thickness * PERCENT_HALF,
297             lastAngle + HALF_CIRCLE, lastAngle, false);
298     } else {
299         Path2DArc(path, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(startRadian),
300             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(startRadian), thickness * PERCENT_HALF,
301             HALF_CIRCLE + startAngle, HALF_CIRCLE + midAngle - tagAngle + startAngle, false);
302         Path2DArc(path, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(totalDrawRadian),
303             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(totalDrawRadian), thickness * PERCENT_HALF,
304             arcData.totalDrawAngle - HALF_CIRCLE - midAngle + tagAngle + startAngle,
305             arcData.totalDrawAngle + HALF_CIRCLE - midAngle - tagAngle + startAngle, true);
306         Path2DArc(path, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(startRadian),
307             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(startRadian), thickness * PERCENT_HALF,
308             -HALF_CIRCLE + midAngle + tagAngle + startAngle, startAngle, false);
309     }
310 
311     /* when it's end half circle not cover it's start angle draw whole outer circle and inner circle and draw path
312      * of end half circle else draw outer circle and inner circle to 180 angle */
313     if (LessOrEqual(drawAngle - lastAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
314         // path of outer circle
315         Path2DArc(path, center.GetX(), center.GetY(), radius, lastAngle, drawAngle, false);
316         Path2DArc(path, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(drawRadian),
317             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(drawRadian), thickness * PERCENT_HALF,
318             drawAngle, drawAngle + HALF_CIRCLE, false);
319         // path of inner circle
320         Path2DArc(path, center.GetX(), center.GetY(), radius - thickness, drawAngle, lastAngle, true);
321     } else {
322         Path2DArc(path, center.GetX(), center.GetY(), radius, lastAngle, HALF_CIRCLE, false);
323         Path2DArc(path, center.GetX(), center.GetY(), radius - thickness, HALF_CIRCLE, lastAngle, true);
324     }
325     path.Close();
326 
327     if (GreatNotEqual(drawAngle - lastAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
328         Path2DArc(endPath, center.GetX(), center.GetY(), radius - thickness, HALF_CIRCLE, drawAngle, false);
329         Path2DArc(endPath, center.GetX() + (radius - thickness * PERCENT_HALF) * std::cos(drawRadian),
330             center.GetY() + (radius - thickness * PERCENT_HALF) * std::sin(drawRadian), thickness * PERCENT_HALF,
331             drawAngle + HALF_CIRCLE, drawAngle, true);
332         Path2DArc(endPath, center.GetX(), center.GetY(), radius, drawAngle, HALF_CIRCLE, true);
333         endPath.Close();
334     }
335 }
336 
PaintLinearProgress(DrawingContext & context,OffsetF offset) const337 void DataPanelModifier::PaintLinearProgress(DrawingContext& context, OffsetF offset) const
338 {
339     auto& canvas = context.canvas;
340     auto totalWidth = context.width;
341     auto spaceWidth = PipelineBase::Vp2PxWithCurrentDensity(FIXED_WIDTH);
342     auto segmentWidthSum = 0.0f;
343     auto segmentSize = 0.0;
344     for (size_t i = 0; i < valuesLastLength_; ++i) {
345         if (NearZero(values_[i]->Get())) {
346             continue;
347         }
348         segmentWidthSum += values_[i]->Get();
349         if (GreatOrEqual(segmentWidthSum, max_->Get())) {
350             break;
351         }
352         ++segmentSize;
353     }
354 
355     float scaleMaxValue = 0.0f;
356     scaleMaxValue = (totalWidth - segmentSize * spaceWidth) / max_->Get();
357 
358     auto widthSegment = offset.GetX();
359     auto firstSegmentWidth = values_[0]->Get() * scaleMaxValue;
360     PaintBackground(canvas, offset, totalWidth, context.height, firstSegmentWidth);
361     float totalPaintWidth = 0.0f;
362     float preWidthSegment = 0.0f;
363     std::vector<LinearData> linearDataMap;
364     bool isFirstValidDate = true;
365     for (size_t i = 0; i < valuesLastLength_; ++i) {
366         auto segmentWidth = values_[i]->Get();
367         if (NonPositive(segmentWidth)) {
368             continue;
369         }
370         LinearData segmentLinearData;
371         segmentLinearData.offset = offset;
372         segmentLinearData.height = context.height;
373         segmentLinearData.totalWidth = totalWidth;
374 
375         if (isFirstValidDate) {
376             segmentLinearData.isFirstData = true;
377             isFirstValidDate = false;
378         }
379 
380         segmentLinearData.segmentColor = SortGradientColorsOffset(valueColors_[i]->Get().GetGradient());
381         segmentLinearData.segmentWidth = segmentWidth * scaleMaxValue;
382         segmentLinearData.xSegment = widthSegment;
383         preWidthSegment = widthSegment;
384         if (GreatOrEqual(segmentLinearData.segmentWidth + segmentLinearData.xSegment, totalWidth)) {
385             segmentLinearData.segmentWidth = totalWidth - preWidthSegment;
386         }
387         // mark last data or add space width
388         widthSegment += values_[i]->Get() * scaleMaxValue;
389         totalPaintWidth += segmentWidth;
390         if (GreatOrEqual(totalPaintWidth, max_->Get())) {
391             segmentLinearData.isEndData = true;
392         } else {
393             widthSegment += spaceWidth;
394         }
395         // draw the shadow at the bottom
396         if ((isShadowVisible_ && (isHasShadowValue_ || isEffect_->Get())) && (i < shadowColorsLastLength_)) {
397             segmentLinearData.segmentShadowColor = SortGradientColorsOffset(shadowColors_[i]->Get().GetGradient());
398             PaintColorSegmentFilterMask(canvas, segmentLinearData);
399         }
400 
401         linearDataMap.emplace_back(segmentLinearData);
402     }
403     // draw the data and the space after drawing the shadow
404     for (size_t i = 0; i < linearDataMap.size(); ++i) {
405         PaintColorSegment(canvas, linearDataMap[i]);
406         if (!linearDataMap[i].isEndData) {
407             PaintSpace(canvas, linearDataMap[i], spaceWidth);
408         }
409     }
410 }
411 
PaintBackground(RSCanvas & canvas,OffsetF offset,float totalWidth,float height,float segmentWidth) const412 void DataPanelModifier::PaintBackground(
413     RSCanvas& canvas, OffsetF offset, float totalWidth, float height, float segmentWidth) const
414 {
415     RSBrush brush;
416     brush.SetColor(ToRSColor(trackBackgroundColor_->Get()));
417     brush.SetAntiAlias(true);
418     canvas.AttachBrush(brush);
419     RSRect rRect(offset.GetX(), offset.GetY(), totalWidth + offset.GetX(), height + offset.GetY());
420     RSRoundRect rrRect;
421     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
422         if (height <= segmentWidth) {
423             rrRect = RSRoundRect(rRect, height, height);
424         } else {
425             rrRect = RSRoundRect(rRect, segmentWidth, segmentWidth);
426         }
427     } else {
428         rrRect = RSRoundRect(rRect, ZERO_CORNER_RADIUS, ZERO_CORNER_RADIUS);
429     }
430     canvas.DrawRoundRect(rrRect);
431     canvas.DetachBrush();
432 }
433 
SetCornerRadius(RSRoundRect & paintRect,const LinearData & segmentLinearData,const float height) const434 void DataPanelModifier::SetCornerRadius(
435     RSRoundRect& paintRect, const LinearData& segmentLinearData, const float height) const
436 {
437     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
438         if (segmentLinearData.isFirstData) {
439             paintRect.SetCornerRadius(RSRoundRect::TOP_LEFT_POS, height, height);
440             paintRect.SetCornerRadius(RSRoundRect::BOTTOM_LEFT_POS, height, height);
441         }
442 
443         if (segmentLinearData.isEndData) {
444             paintRect.SetCornerRadius(RSRoundRect::TOP_RIGHT_POS, height, height);
445             paintRect.SetCornerRadius(RSRoundRect::BOTTOM_RIGHT_POS, height, height);
446         }
447     }
448 }
449 
PaintColorSegment(RSCanvas & canvas,const LinearData & segmentLinearData) const450 void DataPanelModifier::PaintColorSegment(RSCanvas& canvas, const LinearData& segmentLinearData) const
451 {
452     auto offset = segmentLinearData.offset;
453     auto xSegment = segmentLinearData.xSegment;
454     auto segmentWidth = segmentLinearData.segmentWidth;
455     auto height = segmentLinearData.height;
456 
457     std::vector<RSColorQuad> colors;
458     std::vector<float> pos;
459     size_t length = segmentLinearData.segmentColor.GetColors().size();
460     for (size_t i = 0; i < length; ++i) {
461         colors.emplace_back(segmentLinearData.segmentColor.GetColors().at(i).GetLinearColor().GetValue());
462         pos.emplace_back(segmentLinearData.segmentColor.GetColors().at(i).GetDimension().Value());
463     }
464 
465     RSRect rect(xSegment, offset.GetY(), xSegment + segmentWidth, offset.GetY() + height);
466     RSRoundRect paintRect = RSRoundRect(rect, 0, 0);
467     SetCornerRadius(paintRect, segmentLinearData, height);
468 
469     RSPoint segmentStartPoint;
470     segmentStartPoint.SetX(rect.GetLeft());
471     segmentStartPoint.SetY(rect.GetTop());
472     RSPoint segmentEndPoint;
473     segmentEndPoint.SetX(rect.GetRight());
474     segmentEndPoint.SetY(rect.GetBottom());
475     canvas.Save();
476     RSBrush brush;
477 #ifndef USE_ROSEN_DRAWING
478     brush.SetShaderEffect(
479         RSShaderEffect::CreateLinearGradient(segmentStartPoint, segmentEndPoint, colors, pos, RSTileMode::CLAMP));
480 #else
481     brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
482         segmentStartPoint, segmentEndPoint, colors, pos, RSTileMode::CLAMP));
483 #endif
484     canvas.AttachBrush(brush);
485     if (isRtl_) {
486         canvas.Translate(segmentLinearData.totalWidth, 0);
487         canvas.Scale(-1, 1);
488     }
489     canvas.DrawRoundRect(paintRect);
490     canvas.DetachBrush();
491     canvas.Restore();
492 }
493 
PaintColorSegmentFilterMask(RSCanvas & canvas,const LinearData & segmentLinearData) const494 void DataPanelModifier::PaintColorSegmentFilterMask(RSCanvas& canvas, const LinearData& segmentLinearData) const
495 {
496     auto offset = segmentLinearData.offset;
497     auto xSegment = segmentLinearData.xSegment;
498     auto segmentWidth = segmentLinearData.segmentWidth;
499     auto height = segmentLinearData.height;
500 
501     std::vector<RSColorQuad> colors;
502     std::vector<float> pos;
503     size_t length = segmentLinearData.segmentShadowColor.GetColors().size();
504     for (size_t i = 0; i < length; ++i) {
505         colors.emplace_back(segmentLinearData.segmentShadowColor.GetColors().at(i).GetLinearColor().GetValue());
506         pos.emplace_back(segmentLinearData.segmentShadowColor.GetColors().at(i).GetDimension().Value());
507     }
508 
509     RSRect rect(xSegment + shadowOffsetXFloat_->Get(), offset.GetY() + shadowOffsetYFloat_->Get(),
510         xSegment + segmentWidth + shadowOffsetXFloat_->Get(), offset.GetY() + height + shadowOffsetYFloat_->Get());
511     RSRoundRect paintRect = RSRoundRect(rect, 0, 0);
512     SetCornerRadius(paintRect, segmentLinearData, height);
513 
514     RSPoint segmentStartPoint;
515     segmentStartPoint.SetX(rect.GetLeft());
516     segmentStartPoint.SetY(rect.GetTop());
517     RSPoint segmentEndPoint;
518     segmentEndPoint.SetX(rect.GetRight());
519     segmentEndPoint.SetY(rect.GetBottom());
520     canvas.Save();
521     RSBrush brush;
522     RSFilter filter;
523 #ifndef USE_ROSEN_DRAWING
524     filter.SetImageFilter(RSImageFilter::CreateBlurImageFilter(
525         shadowRadiusFloat_->Get(), shadowRadiusFloat_->Get(), RSTileMode::DECAL, nullptr));
526     brush.SetShaderEffect(
527         RSShaderEffect::CreateLinearGradient(segmentStartPoint, segmentEndPoint, colors, pos, RSTileMode::CLAMP));
528 #else
529     filter.SetImageFilter(RSRecordingImageFilter::CreateBlurImageFilter(
530         shadowRadiusFloat_->Get(), shadowRadiusFloat_->Get(), RSTileMode::DECAL, nullptr));
531     brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
532         segmentStartPoint, segmentEndPoint, colors, pos, RSTileMode::CLAMP));
533 #endif
534     brush.SetFilter(filter);
535     brush.SetAlpha(SHADOW_ALPHA);
536     canvas.AttachBrush(brush);
537     if (isRtl_) {
538         canvas.Translate(segmentLinearData.totalWidth, 0);
539         canvas.Scale(-1, 1);
540     }
541     canvas.DrawRoundRect(paintRect);
542     canvas.DetachBrush();
543     canvas.Restore();
544 }
545 
PaintSpace(RSCanvas & canvas,const LinearData & segmentLinearData,float spaceWidth) const546 void DataPanelModifier::PaintSpace(RSCanvas& canvas, const LinearData& segmentLinearData, float spaceWidth) const
547 {
548     float xSpace = segmentLinearData.xSegment + segmentLinearData.segmentWidth;
549     auto offset = segmentLinearData.offset;
550     canvas.Save();
551     RSBrush brush;
552     RSRect rect(xSpace, offset.GetY(), xSpace + spaceWidth, offset.GetY() + segmentLinearData.height);
553     brush.SetColor(ToRSColor(Color::WHITE));
554     brush.SetAntiAlias(true);
555     canvas.AttachBrush(brush);
556     if (isRtl_) {
557         canvas.Translate(segmentLinearData.totalWidth, 0);
558         canvas.Scale(-1, 1);
559     }
560     canvas.DrawRect(rect);
561     canvas.DetachBrush();
562     canvas.Restore();
563 }
564 
PaintTrackBackground(RSCanvas & canvas,ArcData arcData,const Color color) const565 void DataPanelModifier::PaintTrackBackground(RSCanvas& canvas, ArcData arcData, const Color color) const
566 {
567     RSPen backgroundTrackData;
568 #ifndef USE_ROSEN_DRAWING
569     RSPath backgroundTrackPath;
570 #else
571     RSRecordingPath backgroundTrackPath;
572 #endif
573     auto center = arcData.center;
574     float thickness = arcData.thickness;
575     float radius = arcData.radius;
576 
577     RSRect rect(center.GetX() - radius + thickness * PERCENT_HALF, center.GetY() - radius + thickness * PERCENT_HALF,
578         center.GetX() + radius - thickness * PERCENT_HALF, center.GetY() + radius - thickness * PERCENT_HALF);
579 
580     backgroundTrackPath.AddArc(rect, 0.0, WHOLE_CIRCLE);
581     backgroundTrackData.SetColor(ToRSColor(color));
582     backgroundTrackData.SetAntiAlias(true);
583     backgroundTrackData.SetWidth(thickness);
584 
585     canvas.AttachPen(backgroundTrackData);
586     canvas.DrawPath(backgroundTrackPath);
587     canvas.DetachPen();
588 }
589 
PaintProgress(RSCanvas & canvas,ArcData arcData,RSPath & path,RSPath & endPath,bool isShadow) const590 void DataPanelModifier::PaintProgress(
591     RSCanvas& canvas, ArcData arcData, RSPath& path, RSPath& endPath, bool isShadow) const
592 {
593     std::vector<RSColorQuad> colors;
594     std::vector<float> pos;
595     Gradient sourceColors;
596     if (isShadow) {
597         sourceColors = arcData.shadowColor;
598     } else {
599         sourceColors = arcData.progressColors;
600     }
601     float circleAngle = arcData.circleAngle;
602     // add end half circle color
603     if (GreatNotEqual(arcData.gradientPointBase, 0.0f)) {
604         pos.emplace_back(circleAngle / WHOLE_CIRCLE);
605         colors.emplace_back(sourceColors.GetColors().rbegin()->GetLinearColor().GetValue());
606     }
607     size_t length = sourceColors.GetColors().size();
608     for (size_t i = 0; i < length; ++i) {
609         colors.emplace_back(sourceColors.GetColors().at(i).GetLinearColor().GetValue());
610         if (NearZero(arcData.gradientPointBase)) {
611             pos.emplace_back(sourceColors.GetColors().at(i).GetDimension().Value());
612         } else {
613             auto itemPos = (1.0f - arcData.gradientPointBase) * sourceColors.GetColors().at(i).GetDimension().Value() +
614                            arcData.gradientPointBase;
615             pos.emplace_back(itemPos);
616         }
617     }
618 
619     float lastAngle = arcData.lastAngle;
620     float drawAngle = arcData.drawAngle;
621     Offset center = arcData.center;
622 
623     // first line and start half circle not cover end half circle
624     if (NearZero(lastAngle) && LessOrEqual(drawAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
625         lastAngle += circleAngle;
626         drawAngle += circleAngle;
627     }
628     RSBrush gradientPaint;
629     gradientPaint.SetAntiAlias(true);
630 
631     /*
632      * when it's first line and end circle not cover start circle, use wide gradient
633      * when it's first line and end circle cover start circle, use default gradient and set start half circle color
634      * extra else paint line and use default gradient
635      */
636     if (NearZero(arcData.lastAngle) && LessOrEqual(arcData.drawAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
637         drawAngle += circleAngle;
638         canvas.Save();
639         canvas.Rotate(-QUARTER_CIRCLE - circleAngle, center.GetX(), center.GetY());
640     } else if (NearZero(arcData.lastAngle) && GreatNotEqual(arcData.drawAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
641         float startPos = (WHOLE_CIRCLE - circleAngle) / WHOLE_CIRCLE;
642         pos.emplace_back(startPos);
643         colors.emplace_back(sourceColors.GetColors().begin()->GetLinearColor().GetValue());
644         canvas.Save();
645         canvas.Rotate(-QUARTER_CIRCLE, center.GetX(), center.GetY());
646     } else {
647         canvas.Save();
648         canvas.Rotate(-QUARTER_CIRCLE, center.GetX(), center.GetY());
649     }
650 #ifndef USE_ROSEN_DRAWING
651     gradientPaint.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(center.GetX(), center.GetY())),
652         colors, pos, RSTileMode::CLAMP, START_ANGLE, drawAngle, nullptr));
653 #else
654     gradientPaint.SetShaderEffect(
655         RSRecordingShaderEffect::CreateSweepGradient(ToRSPoint(PointF(center.GetX(), center.GetY())), colors, pos,
656             RSTileMode::CLAMP, START_ANGLE, drawAngle, nullptr));
657 #endif
658     RSFilter filter;
659     if (isShadow) {
660         gradientPaint.SetAlpha(SHADOW_ALPHA);
661 #ifndef USE_ROSEN_DRAWING
662         filter.SetImageFilter(RSImageFilter::CreateBlurImageFilter(
663             shadowRadiusFloat_->Get(), shadowRadiusFloat_->Get(), RSTileMode::DECAL, nullptr));
664         gradientPaint.SetFilter(filter);
665 #else
666         filter.SetImageFilter(RSRecordingImageFilter::CreateBlurImageFilter(
667             shadowRadiusFloat_->Get(), shadowRadiusFloat_->Get(), RSTileMode::DECAL, nullptr));
668         gradientPaint.SetFilter(filter);
669 #endif
670     }
671 
672     canvas.AttachBrush(gradientPaint);
673     canvas.DrawPath(path);
674     canvas.DetachBrush();
675     path.Reset();
676     canvas.Restore();
677 
678     /* if path havn't end half circle, draw end circle extra. when draw is not whole circle, add fix angle to cover
679        gap between line and end half circle */
680     if (GreatNotEqual(drawAngle - lastAngle, WHOLE_CIRCLE - circleAngle * 2.0f)) {
681         RSBrush endCirclePaint;
682         endCirclePaint.SetAntiAlias(true);
683         if (NearZero(arcData.lastAngle)) {
684             pos.pop_back();
685             colors.pop_back();
686         }
687         pos.emplace(pos.begin(), circleAngle / WHOLE_CIRCLE);
688         colors.emplace(colors.begin(), sourceColors.GetColors().rbegin()->GetLinearColor().GetValue());
689 #ifndef USE_ROSEN_DRAWING
690         endCirclePaint.SetShaderEffect(
691             RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(center.GetX(), center.GetY())), colors, pos,
692                 RSTileMode::CLAMP, START_ANGLE, drawAngle, nullptr));
693 #else
694         endCirclePaint.SetShaderEffect(
695             RSRecordingShaderEffect::CreateSweepGradient(ToRSPoint(PointF(center.GetX(), center.GetY())), colors, pos,
696                 RSTileMode::CLAMP, START_ANGLE, drawAngle, nullptr));
697 #endif
698         if (isShadow) {
699             endCirclePaint.SetAlpha(SHADOW_ALPHA);
700             endCirclePaint.SetFilter(filter);
701         }
702         canvas.Save();
703         canvas.Rotate(-QUARTER_CIRCLE, center.GetX(), center.GetY());
704         canvas.AttachBrush(endCirclePaint);
705         canvas.DrawPath(endPath);
706         canvas.DetachBrush();
707         endPath.Reset();
708         canvas.Restore();
709     }
710 }
711 
Path2DArc(RSPath & path,double x,double y,double r,double startAngle,double endAngle,bool counterclockwise) const712 void DataPanelModifier::Path2DArc(
713     RSPath& path, double x, double y, double r, double startAngle, double endAngle, bool counterclockwise) const
714 {
715     RSPoint point1(x - r, y - r);
716     RSPoint point2(x + r, y + r);
717 
718     double sweepAngle = endAngle - startAngle;
719     if (!NearZero(counterclockwise)) {
720         sweepAngle = endAngle > startAngle ? (std::fmod(sweepAngle, WHOLE_CIRCLE) - WHOLE_CIRCLE) : sweepAngle;
721     } else {
722         sweepAngle = endAngle > startAngle ? sweepAngle : (std::fmod(sweepAngle, WHOLE_CIRCLE) + WHOLE_CIRCLE);
723     }
724 
725     if (NearEqual(std::fmod(sweepAngle, WHOLE_CIRCLE), 0.0) && !NearEqual(startAngle, endAngle)) {
726         path.ArcTo(point1, point2, startAngle, HALF_CIRCLE);
727         path.ArcTo(point1, point2, startAngle + HALF_CIRCLE, HALF_CIRCLE);
728     } else if (!NearEqual(std::fmod(sweepAngle, WHOLE_CIRCLE), 0.0) && std::abs(sweepAngle) > WHOLE_CIRCLE) {
729         path.ArcTo(point1, point2, startAngle, HALF_CIRCLE);
730         path.ArcTo(point1, point2, startAngle + HALF_CIRCLE, HALF_CIRCLE);
731         path.ArcTo(point1, point2, startAngle + HALF_CIRCLE + HALF_CIRCLE, sweepAngle);
732     } else {
733         path.ArcTo(point1, point2, startAngle, sweepAngle);
734     }
735 }
736 
SortGradientColorsOffset(const Gradient & srcGradient) const737 Gradient DataPanelModifier::SortGradientColorsOffset(const Gradient& srcGradient) const
738 {
739     auto srcGradientColors = srcGradient.GetColors();
740     std::sort(
741         srcGradientColors.begin(), srcGradientColors.end(), [](const GradientColor& left, const GradientColor& right) {
742             return left.GetDimension().Value() < right.GetDimension().Value();
743         });
744 
745     Gradient gradient;
746     for (const auto& item : srcGradientColors) {
747         gradient.AddColor(item);
748     }
749 
750     return gradient;
751 }
752 } // namespace OHOS::Ace::NG
753