1 /*
2  * Copyright (c) 2024 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 #include "core/components_ng/pattern/gauge/gauge_modifier.h"
16 
17 #include <algorithm>
18 #include <cmath>
19 
20 #include "base/utils/utils.h"
21 #include "core/common/container.h"
22 #include "core/components/progress/progress_theme.h"
23 #include "core/components_ng/pattern/gauge/gauge_paint_property.h"
24 #include "core/components_ng/pattern/gauge/gauge_paint_method.h"
25 #include "core/components_ng/pattern/gauge/gauge_pattern.h"
26 #include "core/components_ng/pattern/gauge/gauge_theme.h"
27 #include "core/components_ng/render/drawing_prop_convertor.h"
28 #include "core/components_ng/render/image_painter.h"
29 #include "core/components_ng/render/node_paint_method.h"
30 #include "core/components_ng/render/paint_wrapper.h"
31 
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr float DEFAULT_VALUE = 0.0f;
35 constexpr float ZERO_CIRCLE = 0.0f;
36 constexpr float MIN_CIRCLE = 2.0f;
37 constexpr float HALF_CIRCLE = 180.0f;
38 constexpr float WHOLE_CIRCLE = 360.0f;
39 constexpr float QUARTER_CIRCLE = 90.0f;
40 constexpr float PERCENT_HALF = 0.5f;
41 constexpr float SEGMENTS_SPACE_PERCENT = 0.008f;
42 }
onDraw(DrawingContext & context)43 void GaugeModifier::onDraw(DrawingContext& context)
44 {
45     if (useContentModifier_->Get()) {
46         return;
47     }
48     RSCanvas& canvas = context.canvas;
49     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
50         PaintCircularAndIndicator(canvas);
51     } else {
52         NewPaintCircularAndIndicator(canvas);
53     }
54 }
55 
UpdateValue()56 void GaugeModifier::UpdateValue()
57 {
58     auto pattern = DynamicCast<GaugePattern>(pattern_.Upgrade());
59     CHECK_NULL_VOID(pattern);
60     auto paintProperty = pattern->GetPaintProperty<GaugePaintProperty>();
61     CHECK_NULL_VOID(paintProperty);
62     UpdateProperty(paintProperty);
63     float value = paintProperty->GetValueValue();
64     if (paintProperty->GetIsSensitiveValue(false)) {
65         value = 0.0f;
66     }
67     float max = paintProperty->GetMaxValue();
68     float min = paintProperty->GetMinValue();
69     value = std::clamp(value, min, max);
70     float ratio = 0.0f;
71     if (Positive(max - min)) {
72         ratio = (value - min) / (max - min);
73     }
74     if (NearEqual(ratio, value_->Get())) {
75         return;
76     }
77     start_ = end_;
78     end_ = ratio;
79     value_->Set(start_);
80     AnimationOption option = AnimationOption();
81     auto curve =
82         AceType::MakeRefPtr<ResponsiveSpringMotion>(RESPONSE, DAMPING_FRACTION);
83     option.SetDuration(ANIMATION_DURATION);
84     option.SetDelay(ANIMATION_DELAY);
85     option.SetCurve(curve);
86     option.SetIteration(ANIMATION_TIMES);
87     AnimationUtils::Animate(option, [&]() { value_->Set(end_); });
88 }
89 
InitProperty()90 void GaugeModifier::InitProperty()
91 {
92     auto pattern = DynamicCast<GaugePattern>(pattern_.Upgrade());
93     CHECK_NULL_VOID(pattern);
94     auto paintProperty = pattern->GetPaintProperty<GaugePaintProperty>();
95     CHECK_NULL_VOID(paintProperty);
96 
97     float startAngle = paintProperty->GetStartAngleValue(DEFAULT_START_DEGREE);
98     float endAngle = paintProperty->GetEndAngleValue(DEFAULT_END_DEGREE);
99     startAngle_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(startAngle);
100     endAngle_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(endAngle);
101     max_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(paintProperty->GetMaxValue());
102     min_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(paintProperty->GetMinValue());
103 
104     float strokeWidth = DEFAULT_VALUE;
105     if (paintProperty->GetStrokeWidth().has_value()) {
106         strokeWidth = paintProperty->GetStrokeWidth()->ConvertToPx();
107     }
108     strokeWidth_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(strokeWidth);
109     float indicatorSpace = paintProperty->GetIndicatorSpaceValue(INDICATOR_DISTANCE_TO_TOP).ConvertToPx();
110     indicatorSpace_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(indicatorSpace);
111     GaugeType gaugeType = paintProperty->GetGaugeTypeValue(GaugeType::TYPE_CIRCULAR_SINGLE_SEGMENT_GRADIENT);
112     gaugeTypeValue_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(static_cast<int>(gaugeType));
113     isShowIndicator_ = AceType::MakeRefPtr<PropertyBool>(paintProperty->GetIsShowIndicatorValue(true));
114     indicatorChange_ = AceType::MakeRefPtr<PropertyBool>(paintProperty->GetIndicatorChangeValue(false));
115 
116     if (paintProperty->HasShadowOptions()) {
117         GaugeShadowOptions shadowOptions = paintProperty->GetShadowOptionsValue();
118         shadowRadiusFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(shadowOptions.radius);
119         shadowOffsetXFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(shadowOptions.offsetX);
120         shadowOffsetYFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(shadowOptions.offsetY);
121     } else {
122         shadowRadiusFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(DEFAULT_VALUE);
123         shadowOffsetXFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(DEFAULT_VALUE);
124         shadowOffsetYFloat_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(DEFAULT_VALUE);
125     }
126     if (paintProperty->GetColors().has_value()) {
127         auto colors = paintProperty->GetColorsValue();
128         for (size_t i = 0; i < colors.size(); i++) {
129             auto color =  AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(colors[i]));
130             AttachProperty(color);
131             colors_.emplace_back(color);
132         }
133     }
134     if (paintProperty->HasGradientColors()) {
135         auto colors = paintProperty->GetGradientColorsValue().at(0);
136         for (size_t i = 0; i < colors.size(); i++) {
137             auto color =  AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(colors[i].first));
138             AttachProperty(color);
139             gradientColors_.emplace_back(color);
140         }
141     }
142 }
143 
UpdateProperty(RefPtr<GaugePaintProperty> & paintProperty)144 void GaugeModifier::UpdateProperty(RefPtr<GaugePaintProperty>& paintProperty)
145 {
146     startAngle_->Set(paintProperty->GetStartAngleValue(DEFAULT_START_DEGREE));
147     endAngle_->Set(paintProperty->GetEndAngleValue(DEFAULT_END_DEGREE));
148     max_->Set(paintProperty->GetMaxValue());
149     min_->Set(paintProperty->GetMinValue());
150 
151     if (paintProperty->GetStrokeWidth().has_value()) {
152         float strokeWidth = paintProperty->GetStrokeWidth()->ConvertToPx();
153         strokeWidth_->Set(strokeWidth);
154     } else {
155         strokeWidth_->Set(DEFAULT_VALUE);
156     }
157     indicatorSpace_->Set(paintProperty->GetIndicatorSpaceValue(INDICATOR_DISTANCE_TO_TOP).ConvertToPx());
158     GaugeType gaugeType = paintProperty->GetGaugeTypeValue(GaugeType::TYPE_CIRCULAR_SINGLE_SEGMENT_GRADIENT);
159     gaugeTypeValue_->Set(static_cast<int>(gaugeType));
160     isShowIndicator_ = AceType::MakeRefPtr<PropertyBool>(paintProperty->GetIsShowIndicatorValue(true));
161 
162     if (paintProperty->HasShadowOptions()) {
163         GaugeShadowOptions shadowOptions = paintProperty->GetShadowOptionsValue();
164         shadowRadiusFloat_->Set(shadowOptions.radius);
165         shadowOffsetXFloat_->Set(shadowOptions.offsetX);
166         shadowOffsetYFloat_->Set(shadowOptions.offsetY);
167     }
168 
169     if (paintProperty->GetColors().has_value()) {
170         auto colors = paintProperty->GetColorsValue();
171         for (size_t i = 0; i < colors.size() && i < colors_.size(); i++) {
172             colors_[i]->Set(LinearColor(colors[i]));
173         }
174     }
175 
176     if (paintProperty->HasGradientColors()) {
177         auto colors = paintProperty->GetGradientColorsValue().at(0);
178         for (size_t i = 0; i < colors.size() && i < gradientColors_.size(); i++) {
179             gradientColors_[i]->Set(LinearColor(colors[i].first));
180         }
181     }
182 
183     if (paintProperty->GetIsShowIndicatorValue(false)) {
184         auto indicatorChange = indicatorChange_->Get();
185         indicatorChange_->Set(!indicatorChange);
186     }
187 }
188 
189 
PaintCircularAndIndicator(RSCanvas & canvas)190 void GaugeModifier::PaintCircularAndIndicator(RSCanvas& canvas)
191 {
192     auto pattern = DynamicCast<GaugePattern>(pattern_.Upgrade());
193     CHECK_NULL_VOID(pattern);
194     auto paintProperty = pattern->GetPaintProperty<GaugePaintProperty>();
195     CHECK_NULL_VOID(paintProperty);
196     auto pipelineContext = PipelineBase::GetCurrentContext();
197     CHECK_NULL_VOID(pipelineContext);
198     auto host = pattern->GetHost();
199     auto geometryNode = host->GetGeometryNode();
200     auto offset = geometryNode->GetContentOffset();
201     auto contentSize = geometryNode->GetContentSize();
202     RenderRingInfo data;
203     data.radius = std::min(contentSize.Width(), contentSize.Height()) * PERCENT_HALF;
204     data.center = Offset(contentSize.Width() * PERCENT_HALF + offset.GetX(),
205         contentSize.Height() * PERCENT_HALF + offset.GetY());
206     float startAngle = DEFAULT_START_DEGREE;
207     float endAngle = DEFAULT_END_DEGREE;
208     if (paintProperty->GetStartAngle().has_value()
209         && !std::isnan(paintProperty->GetStartAngle().value())) {
210         startAngle = paintProperty->GetStartAngle().value();
211     }
212     if (paintProperty->GetEndAngle().has_value()
213         && !std::isnan(paintProperty->GetEndAngle().value())) {
214         endAngle = paintProperty->GetEndAngle().value();
215     }
216     float startDegree = startAngle;
217     float sweepDegree = endAngle - startAngle;
218     if (GreatNotEqual(sweepDegree, WHOLE_CIRCLE)  || LessNotEqual(sweepDegree, ZERO_CIRCLE)) {
219         sweepDegree = sweepDegree - floor(sweepDegree / WHOLE_CIRCLE) * WHOLE_CIRCLE;
220     }
221     if (NearZero(sweepDegree)) {
222         sweepDegree = WHOLE_CIRCLE;
223     }
224     auto theme = pipelineContext->GetTheme<ProgressTheme>();
225     data.thickness = theme->GetTrackThickness().ConvertToPx();
226     if (paintProperty->GetStrokeWidth().has_value()
227         && paintProperty->GetStrokeWidth()->Value() > 0) {
228         data.thickness =
229             std::min(static_cast<float>(paintProperty->GetStrokeWidth()->ConvertToPx()),
230             contentSize.Width() * PERCENT_HALF);
231     }
232     PaintDraw(canvas, paintProperty, startDegree, endAngle, data);
233 }
234 
PaintDraw(RSCanvas & canvas,RefPtr<GaugePaintProperty> & paintProperty,float startDegree,float sweepDegree,RenderRingInfo data)235 void GaugeModifier::PaintDraw(RSCanvas& canvas, RefPtr<GaugePaintProperty>& paintProperty,
236     float startDegree, float sweepDegree, RenderRingInfo data)
237 {
238     std::vector<float> weights;
239     if (paintProperty->GetValues().has_value()) {
240         weights = paintProperty->GetValuesValue();
241     } else {
242         weights.push_back(1);
243     }
244     std::vector<Color> colors;
245     if (paintProperty->GetColors().has_value()) {
246         colors = paintProperty->GetColorsValue();
247     } else {
248         colors.push_back(Color::BLACK);
249     }
250     if (colors.size() == 0 || colors.size() != weights.size()) {
251         TAG_LOGW(AceLogTag::ACE_GAUGE, "Gauge get the color size is 0 or is not equal to weight size");
252         return;
253     }
254     float totalWeight = ZERO_CIRCLE;
255     for (auto& weight : weights) {
256         totalWeight += weight;
257     }
258     if (NearEqual(totalWeight, 0.0)) {
259         return;
260     }
261     float currentStart = ZERO_CIRCLE;
262     float highLightStart = ZERO_CIRCLE;
263     size_t highLightIndex = 0;
264     auto ratio = value_->Get();
265     for (int32_t index = static_cast<int32_t>(colors.size()) - 1; index >= 0; --index) {
266         data.color = colors[index];
267         data.color.ChangeAlpha(UNSELECT_ALPHA);
268         currentStart += weights[index];
269         if (ShouldHighLight(totalWeight - currentStart, weights[index], ratio * totalWeight)) {
270             highLightIndex = static_cast<size_t>(index);
271             highLightStart = totalWeight - currentStart;
272         }
273         data.startDegree = startDegree + (1 - currentStart / totalWeight) * sweepDegree;
274         data.sweepDegree = (weights[index] / totalWeight) * sweepDegree;
275         DrawGauge(canvas, data);
276     }
277     // draw highlight part
278     data.color = colors[highLightIndex];
279     data.startDegree = startDegree + (highLightStart / totalWeight) * sweepDegree;
280     data.sweepDegree = (weights[highLightIndex] / totalWeight) * sweepDegree;
281     DrawGauge(canvas, data);
282     data.startDegree = startDegree;
283     data.sweepDegree = sweepDegree * ratio;
284     DrawIndicator(canvas, data);
285 }
286 
DrawGauge(RSCanvas & canvas,RenderRingInfo data)287 void GaugeModifier::DrawGauge(RSCanvas& canvas, RenderRingInfo data)
288 {
289     float thickness = data.thickness;
290     RSPen pen;
291     pen.SetAntiAlias(true);
292     pen.SetColor(data.color.GetValue());
293     pen.SetWidth(thickness);
294     pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
295 
296     canvas.AttachPen(pen);
297 #ifndef USE_ROSEN_DRAWING
298     RSPath path;
299 #else
300     RSRecordingPath path;
301 #endif
302     RSRect rRect(data.center.GetX() - data.radius + thickness * PERCENT_HALF,
303         data.center.GetY() - data.radius + thickness * PERCENT_HALF,
304         data.center.GetX() + data.radius - thickness * PERCENT_HALF,
305         data.center.GetY() + data.radius - thickness * PERCENT_HALF);
306     path.AddArc(rRect, data.startDegree - QUARTER_CIRCLE, data.sweepDegree);
307     canvas.DrawPath(path);
308     canvas.DetachPen();
309 }
310 
DrawIndicator(RSCanvas & canvas,RenderRingInfo data)311 void GaugeModifier::DrawIndicator(RSCanvas& canvas, RenderRingInfo data)
312 {
313 #ifndef USE_ROSEN_DRAWING
314     RSPath path;
315 #else
316     RSRecordingPath path;
317 #endif
318     float pathStartVertexX = data.center.GetX();
319     float pathStartVertexY = data.center.GetY() - data.radius + (data.thickness / 2);
320     path.MoveTo(pathStartVertexX, pathStartVertexY);
321     path.LineTo(pathStartVertexX - EDGE, pathStartVertexY + EDGE);
322     path.LineTo(pathStartVertexX - EDGE, pathStartVertexY + EDGE + HEIGHT_OFFSET);
323     path.LineTo(pathStartVertexX + EDGE, pathStartVertexY + EDGE + HEIGHT_OFFSET);
324     path.LineTo(pathStartVertexX + EDGE, pathStartVertexY + EDGE);
325     path.LineTo(pathStartVertexX, pathStartVertexY);
326 
327     canvas.Save();
328     canvas.Rotate(data.startDegree + data.sweepDegree, data.center.GetX(), data.center.GetY());
329     RSBrush paint;
330     paint.SetColor(Color::WHITE.GetValue());
331     canvas.AttachBrush(paint);
332     canvas.DrawPath(path);
333     canvas.DetachBrush();
334 
335     RSPen pen;
336     pen.SetColor(Color::BLACK.GetValue());
337     pen.SetWidth(INDICATOR_STROKE_WIDTH);
338     canvas.AttachPen(pen);
339     canvas.DrawPath(path);
340     canvas.DetachPen();
341     canvas.Restore();
342 }
343 
ShouldHighLight(float start,float interval,float percent)344 bool GaugeModifier::ShouldHighLight(float start, float interval, float percent)
345 {
346     if (LessOrEqual(percent, start + interval) && GreatOrEqual(percent, start)) {
347         return true;
348     }
349     return false;
350 }
351 
NewPaintCircularAndIndicator(RSCanvas & canvas)352 void GaugeModifier::NewPaintCircularAndIndicator(RSCanvas& canvas)
353 {
354     auto pattern = DynamicCast<GaugePattern>(pattern_.Upgrade());
355     CHECK_NULL_VOID(pattern);
356     auto paintProperty = pattern->GetPaintProperty<GaugePaintProperty>();
357     CHECK_NULL_VOID(paintProperty);
358     auto host = pattern->GetHost();
359     auto geometryNode = host->GetGeometryNode();
360     auto pipelineContext = PipelineBase::GetCurrentContext();
361     CHECK_NULL_VOID(pipelineContext);
362     auto offset = geometryNode->GetContentOffset();
363 
364     canvas.Save();
365     auto paddingSize = geometryNode->GetPaddingSize();
366     CHECK_NULL_VOID(geometryNode->GetPadding());
367     auto left = geometryNode->GetPadding()->left.value_or(0.0f);
368     auto top = geometryNode->GetPadding()->top.value_or(0.0f);
369     auto radius = std::min(paddingSize.Width(), paddingSize.Height()) * PERCENT_HALF;
370     RenderRingInfo data;
371     data.contentSize = paddingSize;
372     data.radius = radius;
373     data.center = Offset(offset.GetX() + left + radius, offset.GetY() + top + radius);
374     auto theme = pipelineContext->GetTheme<GaugeTheme>();
375     data.thickness = theme->GetTrackThickness().ConvertToPx();
376     CalculateStartAndSweepDegree(paintProperty, data);
377     if (paintProperty->GetIsSensitiveValue(false)) {
378         PaintMonochromeCircular(canvas, data, paintProperty);
379     } else {
380         switch (paintProperty->GetGaugeTypeValue(GaugeType::TYPE_CIRCULAR_SINGLE_SEGMENT_GRADIENT)) {
381             case GaugeType::TYPE_CIRCULAR_MULTI_SEGMENT_GRADIENT: {
382                 PaintMultiSegmentGradientCircular(canvas, data, paintProperty);
383                 break;
384             }
385             case GaugeType::TYPE_CIRCULAR_SINGLE_SEGMENT_GRADIENT: {
386                 PaintSingleSegmentGradientCircular(canvas, data, paintProperty);
387                 break;
388             }
389             case GaugeType::TYPE_CIRCULAR_MONOCHROME: {
390                 PaintMonochromeCircular(canvas, data, paintProperty);
391                 break;
392             }
393             default:
394                 // do nothing.
395                 break;
396         }
397     }
398     canvas.Restore();
399 }
400 
PaintMonochromeCircular(RSCanvas & canvas,RenderRingInfo data,RefPtr<GaugePaintProperty> & paintProperty)401 void GaugeModifier::PaintMonochromeCircular(
402     RSCanvas& canvas, RenderRingInfo data, RefPtr<GaugePaintProperty>& paintProperty)
403 {
404     CHECK_NULL_VOID(paintProperty);
405     Color color(Color::BLACK);
406     if (paintProperty->HasGradientColors()) {
407         color = paintProperty->GetGradientColorsValue().at(0).at(0).first;
408     }
409     auto gaugeType = paintProperty->GetGaugeTypeValue(GaugeType::TYPE_CIRCULAR_SINGLE_SEGMENT_GRADIENT);
410     auto isSensitive = paintProperty->GetIsSensitive().value_or(false);
411     if (isSensitive && gaugeType != GaugeType::TYPE_CIRCULAR_MONOCHROME) {
412         color = Color::GRAY;
413     }
414     Color backgroundColor = color.ChangeOpacity(MONOCHROME_CIRCULAR_BACKGROUND_COLOR_OPACITY);
415     float offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
416     auto ratio = value_->Get();
417     RSBrush brush;
418     brush.SetAntiAlias(true);
419 
420     RSPath path;
421     auto startAngle = data.startDegree - QUARTER_CIRCLE + offsetDegree;
422     auto sweepAngle = GreatNotEqual(data.sweepDegree, MIN_CIRCLE * offsetDegree)
423                                ? data.sweepDegree - MIN_CIRCLE * offsetDegree
424                                : ZERO_CIRCLE;
425     GetDrawPath(path, data, startAngle, sweepAngle);
426     auto tempSweepDegree = GreatNotEqual(data.sweepDegree * ratio, MIN_CIRCLE * offsetDegree)
427                                ? data.sweepDegree * ratio - MIN_CIRCLE * offsetDegree
428                                : ZERO_CIRCLE;
429 
430     PaintMonochromeCircularShadow(canvas, data, color, paintProperty, tempSweepDegree);
431     brush.SetColor(backgroundColor.GetValue());
432     canvas.Save();
433     canvas.AttachBrush(brush);
434     canvas.DrawPath(path);
435     canvas.DetachBrush();
436     canvas.Restore();
437 
438     path.Reset();
439     GetDrawPath(path, data, startAngle, tempSweepDegree);
440     brush.SetColor(color.GetValue());
441     canvas.Save();
442     canvas.AttachBrush(brush);
443     canvas.DrawPath(path);
444     canvas.DetachBrush();
445     canvas.Restore();
446     data.sweepDegree = data.sweepDegree * ratio;
447     NewDrawIndicator(canvas, paintProperty, data);
448 }
449 
PaintMonochromeCircularShadow(RSCanvas & canvas,RenderRingInfo & data,Color & color,RefPtr<GaugePaintProperty> & paintProperty,float sweepDegree)450 void GaugeModifier::PaintMonochromeCircularShadow(RSCanvas& canvas, RenderRingInfo& data, Color& color,
451     RefPtr<GaugePaintProperty>& paintProperty, float sweepDegree)
452 {
453     CHECK_NULL_VOID(paintProperty);
454     GaugeShadowOptions shadowOptions;
455     if (paintProperty->HasShadowOptions()) {
456         shadowOptions = paintProperty->GetShadowOptionsValue();
457     }
458     if (!shadowOptions.isShadowVisible) {
459         return;
460     }
461     float offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
462     RSFilter filter;
463     filter.SetMaskFilter(RSMaskFilter::CreateBlurMaskFilter(RSBlurType::NORMAL, shadowOptions.radius));
464     RSBrush shadowPen;
465     shadowPen.SetAntiAlias(true);
466     shadowPen.SetColor(color.GetValue());
467     shadowPen.SetFilter(filter);
468     shadowPen.SetAlphaF(SHADOW_ALPHA);
469 
470     RSPath shadowPath;
471     GetDrawPath(shadowPath, data, data.startDegree - QUARTER_CIRCLE + offsetDegree, sweepDegree);
472 
473     canvas.Save();
474     canvas.Translate(shadowOptions.offsetX, shadowOptions.offsetY);
475     canvas.AttachBrush(shadowPen);
476     canvas.DrawPath(shadowPath);
477     canvas.DetachBrush();
478     canvas.Restore();
479 }
480 
PaintSingleSegmentGradientCircular(RSCanvas & canvas,RenderRingInfo data,RefPtr<GaugePaintProperty> & paintProperty)481 void GaugeModifier::PaintSingleSegmentGradientCircular(
482     RSCanvas& canvas, RenderRingInfo data, RefPtr<GaugePaintProperty>& paintProperty)
483 {
484     CHECK_NULL_VOID(paintProperty);
485     std::vector<RSColorQuad> colors;
486     std::vector<float> pos;
487     if (paintProperty->HasGradientColors()) {
488         auto colorsArray = paintProperty->GetGradientColorsValue().at(0);
489         for (auto& colorStop : colorsArray) {
490             colors.emplace_back(colorStop.first.GetValue());
491             pos.emplace_back(colorStop.second.Value());
492         }
493     } else {
494         CreateDefaultColor(colors, pos);
495     }
496 
497     PaintSingleSegmentGradientCircularShadow(canvas, data, paintProperty, colors, pos);
498     float offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
499     RSPen pen;
500     pen.SetAntiAlias(true);
501     pen.SetWidth(data.thickness);
502     pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
503     pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(data.center.GetX(), data.center.GetY())),
504         colors, pos, RSTileMode::CLAMP, offsetDegree, data.sweepDegree - offsetDegree, nullptr));
505 
506     RSRect rRect(data.center.GetX() - data.radius + data.thickness * PERCENT_HALF,
507                  data.center.GetY() - data.radius + data.thickness * PERCENT_HALF,
508                  data.center.GetX() + data.radius - data.thickness * PERCENT_HALF,
509                  data.center.GetY() + data.radius - data.thickness * PERCENT_HALF);
510 
511     RSPath path;
512     path.AddArc(rRect, offsetDegree, data.sweepDegree - MIN_CIRCLE * offsetDegree);
513     canvas.Rotate(data.startDegree - QUARTER_CIRCLE, data.center.GetX(), data.center.GetY());
514     canvas.AttachPen(pen);
515     canvas.DrawPath(path);
516     canvas.DetachPen();
517 
518     auto ratio = value_->Get();
519     data.startDegree = QUARTER_CIRCLE;
520     data.sweepDegree = data.sweepDegree * ratio;
521     NewDrawIndicator(canvas, paintProperty, data);
522 }
523 
PaintSingleSegmentGradientCircularShadow(RSCanvas & canvas,RenderRingInfo & data,RefPtr<GaugePaintProperty> & paintProperty,std::vector<RSColorQuad> & colors,std::vector<float> & pos)524 void GaugeModifier::PaintSingleSegmentGradientCircularShadow(RSCanvas& canvas, RenderRingInfo& data,
525     RefPtr<GaugePaintProperty>& paintProperty, std::vector<RSColorQuad>& colors,
526     std::vector<float>& pos)
527 {
528     CHECK_NULL_VOID(paintProperty);
529     GaugeShadowOptions shadowOptions;
530     if (paintProperty->HasShadowOptions()) {
531         shadowOptions = paintProperty->GetShadowOptionsValue();
532     }
533     if (!shadowOptions.isShadowVisible) {
534         return;
535     }
536 
537     float offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
538     RSFilter filter;
539     filter.SetImageFilter(
540         RSImageFilter::CreateBlurImageFilter(shadowOptions.radius, shadowOptions.radius, RSTileMode::CLAMP, nullptr));
541     RSPen shadowPen;
542     shadowPen.SetAntiAlias(true);
543     shadowPen.SetWidth(data.thickness);
544     shadowPen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
545     shadowPen.SetAlphaF(SHADOW_ALPHA);
546     shadowPen.SetFilter(filter);
547     shadowPen.SetShaderEffect(
548         RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(data.center.GetX(), data.center.GetY())), colors, pos,
549             RSTileMode::CLAMP, offsetDegree, data.sweepDegree - offsetDegree, nullptr));
550 
551     RSRect rRect(data.center.GetX() - data.radius + data.thickness * PERCENT_HALF,
552                  data.center.GetY() - data.radius + data.thickness * PERCENT_HALF,
553                  data.center.GetX() + data.radius - data.thickness * PERCENT_HALF,
554                  data.center.GetY() + data.radius - data.thickness * PERCENT_HALF);
555 
556     RSPath path;
557     path.AddArc(rRect, offsetDegree, data.sweepDegree - MIN_CIRCLE * offsetDegree);
558 
559     canvas.Save();
560     canvas.Translate(shadowOptions.offsetX, shadowOptions.offsetY);
561     canvas.Rotate(data.startDegree - QUARTER_CIRCLE, data.center.GetX(), data.center.GetY());
562     canvas.AttachPen(shadowPen);
563     canvas.DrawPath(path);
564     canvas.DetachPen();
565     canvas.Restore();
566 }
567 
PaintMultiSegmentGradientCircular(RSCanvas & canvas,RenderRingInfo data,RefPtr<GaugePaintProperty> & paintProperty)568 void GaugeModifier::PaintMultiSegmentGradientCircular(
569     RSCanvas& canvas, RenderRingInfo data, RefPtr<GaugePaintProperty>& paintProperty)
570 {
571     CHECK_NULL_VOID(paintProperty);
572     float startDegree = data.startDegree;
573     float sweepDegree = data.sweepDegree;
574     std::vector<float> weights;
575     if (paintProperty->HasValues()) {
576         weights = paintProperty->GetValuesValue();
577     } else {
578         weights.push_back(1);
579     }
580     std::vector<ColorStopArray> colors;
581     if (paintProperty->HasGradientColors()) {
582         colors = paintProperty->GetGradientColorsValue();
583     } else {
584         ColorStopArray colorStopArray;
585         colorStopArray.emplace_back(std::make_pair(Color::BLACK, Dimension(0.0)));
586         colors.push_back(colorStopArray);
587     }
588 
589     if (colors.size() == 0 || colors.size() != weights.size()) {
590         TAG_LOGW(AceLogTag::ACE_GAUGE, "Gauge get the color size is 0 or is not equal to weight size");
591         return;
592     }
593     float totalWeight = ZERO_CIRCLE;
594     for (auto& weight : weights) {
595         totalWeight += weight;
596     }
597     if (NearEqual(totalWeight, 0.0)) {
598         return;
599     }
600 
601     PaintMultiSegmentGradientCircularShadow(canvas, data, paintProperty, colors, weights);
602 
603     auto ratio = value_->Get();
604     data.startDegree = QUARTER_CIRCLE;
605     data.sweepDegree = sweepDegree * ratio;
606     SingleSegmentGradientInfo info;
607     info.isDrawShadow = false;
608     canvas.Rotate(startDegree - QUARTER_CIRCLE, data.center.GetX(), data.center.GetY());
609     for (size_t index = 0; index < colors.size(); index++) {
610         info.drawStartDegree = info.drawStartDegree + info.drawSweepDegree;
611         info.drawSweepDegree = (weights[index] / totalWeight) * sweepDegree;
612         info.offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
613         info.colorStopArray = colors.at(index);
614         DrawSingleSegmentGradient(canvas, data, paintProperty, info, index);
615     }
616     NewDrawIndicator(canvas, paintProperty, data);
617 }
618 
PaintMultiSegmentGradientCircularShadow(RSCanvas & canvas,RenderRingInfo & data,RefPtr<GaugePaintProperty> & paintProperty,std::vector<ColorStopArray> & colors,std::vector<float> & weights)619 void GaugeModifier::PaintMultiSegmentGradientCircularShadow(RSCanvas& canvas, RenderRingInfo& data,
620     RefPtr<GaugePaintProperty>& paintProperty, std::vector<ColorStopArray>& colors,
621     std::vector<float>& weights)
622 {
623     CHECK_NULL_VOID(paintProperty);
624     GaugeShadowOptions shadowOptions;
625     if (paintProperty->HasShadowOptions()) {
626         shadowOptions = paintProperty->GetShadowOptionsValue();
627     }
628 
629     if (!shadowOptions.isShadowVisible) {
630         return;
631     }
632     float totalWeight = ZERO_CIRCLE;
633     for (auto& weight : weights) {
634         totalWeight += weight;
635     }
636     canvas.Save();
637     canvas.Translate(shadowOptions.offsetX, shadowOptions.offsetY);
638     canvas.Rotate(data.startDegree - QUARTER_CIRCLE, data.center.GetX(), data.center.GetY());
639 
640     SingleSegmentGradientInfo info;
641     info.isDrawShadow = true;
642     info.shadowRadius = shadowOptions.radius;
643     for (size_t index = 0; index < colors.size(); index++) {
644         info.drawStartDegree = info.drawStartDegree + info.drawSweepDegree;
645         info.drawSweepDegree = (weights[index] / totalWeight) * data.sweepDegree;
646         info.offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
647         info.colorStopArray = colors.at(index);
648         DrawSingleSegmentGradient(canvas, data, paintProperty, info, index);
649     }
650     canvas.Restore();
651 }
652 
DrawSingleSegmentGradient(RSCanvas & canvas,RenderRingInfo & data,RefPtr<GaugePaintProperty> & paintProperty,SingleSegmentGradientInfo & info,size_t index)653 void GaugeModifier::DrawSingleSegmentGradient(RSCanvas& canvas, RenderRingInfo& data,
654     RefPtr<GaugePaintProperty>& paintProperty, SingleSegmentGradientInfo& info, size_t index)
655 {
656     auto drawStartDegree = info.drawStartDegree;
657     auto drawSweepDegree = info.drawSweepDegree;
658     auto offsetDegree = info.offsetDegree;
659     std::vector<RSColorQuad> colorValues;
660     std::vector<float> pos;
661     for (auto& colorStop : info.colorStopArray) {
662         colorValues.emplace_back(colorStop.first.GetValue());
663         pos.emplace_back(colorStop.second.Value());
664     }
665 
666     RSPen pen;
667     RSPath path;
668     pen.SetAntiAlias(true);
669     pen.SetWidth(data.thickness);
670     pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
671     RSRect rRect(data.center.GetX() - data.radius + data.thickness * PERCENT_HALF,
672                  data.center.GetY() - data.radius + data.thickness * PERCENT_HALF,
673                  data.center.GetX() + data.radius - data.thickness * PERCENT_HALF,
674                  data.center.GetY() + data.radius - data.thickness * PERCENT_HALF);
675 
676     if (info.isDrawShadow) {
677         RSFilter filter;
678         filter.SetImageFilter(
679             RSImageFilter::CreateBlurImageFilter(info.shadowRadius, info.shadowRadius, RSTileMode::CLAMP, nullptr));
680         pen.SetFilter(filter);
681         pen.SetAlphaF(SHADOW_ALPHA);
682     } else {
683         NewDrawIndicator(canvas, paintProperty, data);
684     }
685 
686     if (index != 0) {
687         if (!info.isDrawShadow) {
688             DrawHighLight(canvas, data, drawStartDegree);
689         }
690         pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
691             ToRSPoint(PointF(data.center.GetX(), data.center.GetY())), colorValues, pos, RSTileMode::CLAMP,
692             drawStartDegree - offsetDegree, drawStartDegree + drawSweepDegree - offsetDegree, nullptr));
693         path.AddArc(rRect, drawStartDegree - offsetDegree, drawSweepDegree);
694     } else {
695         pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
696             ToRSPoint(PointF(data.center.GetX(), data.center.GetY())), colorValues, pos, RSTileMode::CLAMP,
697             drawStartDegree + offsetDegree, drawStartDegree + drawSweepDegree + offsetDegree, nullptr));
698         path.AddArc(rRect, drawStartDegree + offsetDegree, drawSweepDegree - MIN_CIRCLE * offsetDegree);
699     }
700 
701     canvas.AttachPen(pen);
702     canvas.DrawPath(path);
703     canvas.DetachPen();
704 }
705 
CalculateStartAndSweepDegree(RefPtr<GaugePaintProperty> & paintProperty,RenderRingInfo & data)706 void GaugeModifier::CalculateStartAndSweepDegree(
707     RefPtr<GaugePaintProperty>& paintProperty, RenderRingInfo& data)
708 {
709     CHECK_NULL_VOID(paintProperty);
710     float startAngle = DEFAULT_START_DEGREE;
711     float endAngle = DEFAULT_END_DEGREE;
712 
713     if (paintProperty->HasStartAngle() && !std::isnan(paintProperty->GetStartAngleValue())) {
714         startAngle = paintProperty->GetStartAngleValue();
715     }
716 
717     if (paintProperty->HasEndAngle() && !std::isnan(paintProperty->GetEndAngleValue())) {
718         endAngle = paintProperty->GetEndAngleValue();
719     }
720 
721     if (paintProperty->HasStrokeWidth() && (paintProperty->GetStrokeWidth()->Value() > 0)) {
722         data.thickness = std::min(static_cast<float>(paintProperty->GetStrokeWidth()->ConvertToPx()),
723             data.contentSize.Width() * PERCENT_HALF);
724     }
725 
726     float sweepDegree = endAngle - startAngle;
727     if (GreatNotEqual(sweepDegree, DEFAULT_END_DEGREE) || LessNotEqual(sweepDegree, DEFAULT_START_DEGREE)) {
728         sweepDegree = sweepDegree - floor(sweepDegree / WHOLE_CIRCLE) * WHOLE_CIRCLE;
729     }
730 
731     if (NearZero(sweepDegree)) {
732         sweepDegree = WHOLE_CIRCLE;
733     }
734     data.startDegree = startAngle;
735     data.sweepDegree = sweepDegree;
736 }
737 
DrawHighLight(RSCanvas & canvas,RenderRingInfo & data,float drawStartDegree)738 void GaugeModifier::DrawHighLight(RSCanvas& canvas, RenderRingInfo& data, float drawStartDegree)
739 {
740     float offsetDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF);
741     auto radius = data.radius - data.thickness * PERCENT_HALF;
742     auto space = data.radius * MIN_CIRCLE * SEGMENTS_SPACE_PERCENT;
743     RSPath path1;
744     path1.AddCircle(data.center.GetX() + radius * std::cos((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE),
745                     data.center.GetY() + radius * std::sin((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE),
746                     data.thickness * PERCENT_HALF);
747 
748     RSPath path2;
749     path2.AddCircle(data.center.GetX() + radius * std::cos((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE),
750                     data.center.GetY() + radius * std::sin((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE),
751                     (data.thickness * PERCENT_HALF + space));
752 
753     canvas.ClipPath(path2, RSClipOp::DIFFERENCE, true);
754 
755     RSPath path3;
756     path3.Op(path2, path1, RSPathOp::DIFFERENCE);
757 
758     float tempDegree = GetOffsetDegree(data, data.thickness * PERCENT_HALF + space);
759     RSPath path4;
760     path4.MoveTo(data.center.GetX() + (data.radius) * std::cos((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE),
761                  data.center.GetY() + (data.radius) * std::sin((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE));
762     path4.LineTo(
763         data.center.GetX() + (data.radius - data.thickness) *
764             std::cos((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE),
765         data.center.GetY() + (data.radius - data.thickness) *
766             std::sin((drawStartDegree - offsetDegree) * M_PI / HALF_CIRCLE));
767     path4.LineTo(
768         data.center.GetX() + (data.radius - data.thickness) *
769             std::cos((drawStartDegree - offsetDegree - tempDegree) * M_PI / HALF_CIRCLE),
770         data.center.GetY() + (data.radius - data.thickness) *
771             std::sin((drawStartDegree - offsetDegree - tempDegree) * M_PI / HALF_CIRCLE));
772     path4.LineTo(
773         data.center.GetX() + (data.radius) *
774             std::cos((drawStartDegree - offsetDegree - tempDegree) * M_PI / HALF_CIRCLE),
775         data.center.GetY() + (data.radius) *
776             std::sin((drawStartDegree - offsetDegree - tempDegree) * M_PI / HALF_CIRCLE));
777 
778     RSPath path5;
779     path5.Op(path3, path4, RSPathOp::DIFFERENCE);
780 
781     canvas.ClipPath(path5, RSClipOp::DIFFERENCE, true);
782 }
783 
GetOffsetDegree(RenderRingInfo & data,float oppositeSide)784 float GaugeModifier::GetOffsetDegree(RenderRingInfo& data, float oppositeSide)
785 {
786     return NearEqual(data.radius, data.thickness * PERCENT_HALF)
787                ? ZERO_CIRCLE
788                : std::tan((oppositeSide) / (data.radius - data.thickness * PERCENT_HALF)) * HALF_CIRCLE / M_PI;
789 }
790 
NewDrawIndicator(RSCanvas & canvas,RefPtr<GaugePaintProperty> & paintProperty,RenderRingInfo & data)791 void GaugeModifier::NewDrawIndicator(
792     RSCanvas& canvas, RefPtr<GaugePaintProperty>& paintProperty, RenderRingInfo& data)
793 {
794     CHECK_NULL_VOID(paintProperty);
795     if (!(paintProperty->GetIsShowIndicatorValue(true))) {
796         return;
797     }
798     if (paintProperty->HasIndicatorIconSourceInfo()) {
799         NewDrawImageIndicator(canvas, paintProperty, data);
800         return;
801     }
802 
803     auto pipelineContext = PipelineBase::GetCurrentContext();
804     CHECK_NULL_VOID(pipelineContext);
805     auto theme = pipelineContext->GetTheme<GaugeTheme>();
806 
807     Dimension indicatorToTop = paintProperty->GetIndicatorSpaceValue(INDICATOR_DISTANCE_TO_TOP);
808     if (GreatNotEqual(indicatorToTop.ConvertToPx(), data.radius)) {
809         indicatorToTop = INDICATOR_DISTANCE_TO_TOP;
810     }
811 
812     float pathStartVertexX = data.center.GetX();
813     float pathStartVertexY = data.center.GetY() - data.radius + indicatorToTop.ConvertToPx() -
814                              INDICATOR_BORDER_WIDTH_RATIO * data.radius * PERCENT_HALF;
815     RSPath path;
816     CreateDefaultTrianglePath(pathStartVertexX, pathStartVertexY, data.radius, path);
817     canvas.Save();
818     canvas.Rotate(data.startDegree + data.sweepDegree, data.center.GetX(), data.center.GetY());
819     RSPen pen;
820     pen.SetBlendMode(RSBlendMode::SRC_OVER);
821     pen.SetColor(theme->GetIndicatorBorderColor().GetValue());
822     pen.SetAntiAlias(true);
823     pen.SetWidth(INDICATOR_BORDER_WIDTH_RATIO_DOUBLE * data.radius);
824     pen.SetJoinStyle(RSPen::JoinStyle::ROUND_JOIN);
825     canvas.AttachPen(pen);
826     canvas.DrawPath(path);
827     canvas.DetachPen();
828 
829     RSBrush paint;
830     paint.SetAntiAlias(true);
831     paint.SetColor(theme->GetIndicatorColor().GetValue());
832     paint.SetBlendMode(RSBlendMode::SRC_OVER);
833     canvas.AttachBrush(paint);
834     canvas.DrawPath(path);
835     canvas.DetachBrush();
836     canvas.Restore();
837 }
838 
NewDrawImageIndicator(RSCanvas & canvas,RefPtr<GaugePaintProperty> & paintProperty,RenderRingInfo & data)839 void GaugeModifier::NewDrawImageIndicator(
840     RSCanvas& canvas, RefPtr<GaugePaintProperty>& paintProperty, RenderRingInfo& data)
841 {
842     CHECK_NULL_VOID(paintProperty);
843     canvas.Save();
844     canvas.Rotate(data.startDegree + data.sweepDegree, data.center.GetX(), data.center.GetY());
845     auto gaugePattern = DynamicCast<GaugePattern>(pattern_.Upgrade());
846     CHECK_NULL_VOID(gaugePattern);
847     RefPtr<CanvasImage> indicatorIconCanvasImage = gaugePattern->GetIndicatorIconCanvasImage();
848     Dimension indicatorToTop = paintProperty->GetIndicatorSpaceValue(INDICATOR_DISTANCE_TO_TOP);
849     if (GreatNotEqual(indicatorToTop.ConvertToPx(), data.radius)) {
850         indicatorToTop = INDICATOR_DISTANCE_TO_TOP;
851     }
852 
853     CHECK_NULL_VOID(indicatorIconCanvasImage);
854     auto&& config = indicatorIconCanvasImage->GetPaintConfig();
855     config.renderMode_ = ImageRenderMode::ORIGINAL;
856     config.imageInterpolation_ = ImageInterpolation::NONE;
857     config.imageRepeat_ = ImageRepeat::NO_REPEAT;
858     config.imageFit_ = ImageFit::FILL;
859     config.isSvg_ = true;
860     auto diameter = data.radius * RADIUS_TO_DIAMETER;
861     float pathStartVertexX = data.center.GetX();
862     float pathStartVertexY = data.center.GetY() - data.radius + indicatorToTop.ConvertToPx();
863 
864     ImagePainter indicatorIconImagePainter(indicatorIconCanvasImage);
865     indicatorIconImagePainter.DrawImage(canvas,
866         OffsetF(pathStartVertexX - INDICATOR_WIDTH_RADIO * data.radius, pathStartVertexY),
867         SizeF(INDICATOR_WIDTH_RADIO * diameter, INDICATOR_HEIGHT_RADIO * diameter));
868     canvas.Restore();
869 }
870 
CreateDefaultColor(std::vector<RSColorQuad> & colors,std::vector<float> & pos)871 void GaugeModifier::CreateDefaultColor(std::vector<RSColorQuad>& colors, std::vector<float>& pos)
872 {
873     float space = ZERO_CIRCLE;
874     for (auto& color : GAUGE_DEFAULT_COLOR) {
875         colors.emplace_back(color.GetValue());
876         pos.emplace_back(space);
877         space += PERCENT_HALF;
878     }
879 }
880 
CreateDefaultTrianglePath(float pathStartVertexX,float pathStartVertexY,float radius,RSPath & path)881 void GaugeModifier::CreateDefaultTrianglePath(
882     float pathStartVertexX, float pathStartVertexY, float radius, RSPath& path)
883 {
884     auto width = radius * RADIUS_TO_DIAMETER * INDICATOR_WIDTH_RATIO;
885     auto height = radius * RADIUS_TO_DIAMETER * INDICATOR_HEIGHT_RATIO;
886     auto hypotenuse = std::sqrt(((width * PERCENT_HALF) * (width * PERCENT_HALF)) + (height * height));
887 
888     auto cornerRadius = radius * RADIUS_TO_DIAMETER * INDICATOR_CORNER_RADIUS_RATIO;
889     auto bottomAngle = std::atan(height / (width * PERCENT_HALF));
890 
891     if (!NearZero(hypotenuse) && hypotenuse != 0) {
892         auto tempTopHypotenuse = cornerRadius / (width * PERCENT_HALF) * height;
893         auto tempTopWidth = tempTopHypotenuse / hypotenuse * (width * PERCENT_HALF);
894         auto tempTopHeight = tempTopHypotenuse / hypotenuse * height;
895         auto tempBottomHypotenuse = cornerRadius / std::tan(bottomAngle * PERCENT_HALF);
896         auto tempBottomWidth = tempBottomHypotenuse / hypotenuse * (width * PERCENT_HALF);
897         auto tempBottomHeight = tempBottomHypotenuse / hypotenuse * height;
898 
899         PointF topControlPoint = PointF(pathStartVertexX, pathStartVertexY);
900         PointF leftControlPoint = PointF(pathStartVertexX - width * PERCENT_HALF, pathStartVertexY + height);
901         PointF rightControlPoint = PointF(pathStartVertexX + width * PERCENT_HALF, pathStartVertexY + height);
902 
903         PointF trianglePoint1 = topControlPoint + OffsetF(-tempTopWidth, tempTopHeight);
904         PointF trianglePoint2 = leftControlPoint + OffsetF(tempBottomWidth, -tempBottomHeight);
905         PointF trianglePoint3 = leftControlPoint + OffsetF(tempBottomHypotenuse, ZERO_CIRCLE);
906         PointF trianglePoint4 = rightControlPoint + OffsetF(-tempBottomHypotenuse, ZERO_CIRCLE);
907         PointF trianglePoint5 = rightControlPoint + OffsetF(-tempBottomWidth, -tempBottomHeight);
908         PointF trianglePoint6 = topControlPoint + OffsetF(tempTopWidth, tempTopHeight);
909 
910         path.MoveTo(trianglePoint1.GetX(), trianglePoint1.GetY());
911         path.LineTo(trianglePoint2.GetX(), trianglePoint2.GetY());
912         path.QuadTo(leftControlPoint.GetX(), leftControlPoint.GetY(), trianglePoint3.GetX(), trianglePoint3.GetY());
913         path.LineTo(trianglePoint4.GetX(), trianglePoint4.GetY());
914         path.QuadTo(rightControlPoint.GetX(), rightControlPoint.GetY(), trianglePoint5.GetX(), trianglePoint5.GetY());
915         path.LineTo(trianglePoint6.GetX(), trianglePoint6.GetY());
916         path.QuadTo(topControlPoint.GetX(), topControlPoint.GetY(), trianglePoint1.GetX(), trianglePoint1.GetY());
917     }
918 }
GetDrawPath(RSPath & path,RenderRingInfo & data,float startAngle,float sweepAngle)919 void GaugeModifier::GetDrawPath(RSPath& path, RenderRingInfo& data, float startAngle, float sweepAngle)
920 {
921     auto startRadian = M_PI * startAngle / HALF_CIRCLE;
922     RSPoint startPoint1(data.center.GetX() + (data.radius - data.thickness * PERCENT_HALF) * std::cos(startRadian) -
923                             data.thickness * PERCENT_HALF,
924         data.center.GetY() + (data.radius - data.thickness * PERCENT_HALF) * std::sin(startRadian) -
925             data.thickness * PERCENT_HALF);
926     RSPoint startPoint2(data.center.GetX() + (data.radius - data.thickness * PERCENT_HALF) * std::cos(startRadian) +
927                             data.thickness * PERCENT_HALF,
928         data.center.GetY() + (data.radius - data.thickness * PERCENT_HALF) * std::sin(startRadian) +
929             data.thickness * PERCENT_HALF);
930 
931     path.ArcTo(startPoint1, startPoint2, startAngle + HALF_CIRCLE, HALF_CIRCLE);
932 
933     if (Positive(sweepAngle)) {
934         RSPoint outPoint1(data.center.GetX() - data.radius, data.center.GetY() - data.radius);
935         RSPoint outPoint2(data.center.GetX() + data.radius, data.center.GetY() + data.radius);
936         path.ArcTo(outPoint1, outPoint2, startAngle, sweepAngle);
937     }
938 
939     auto endAngle = startAngle + sweepAngle;
940     auto endRadian = M_PI * endAngle / HALF_CIRCLE;
941     RSPoint endPoint1(data.center.GetX() + (data.radius - data.thickness * PERCENT_HALF) * std::cos(endRadian) -
942                           data.thickness * PERCENT_HALF,
943         data.center.GetY() + (data.radius - data.thickness * PERCENT_HALF) * std::sin(endRadian) -
944             data.thickness * PERCENT_HALF);
945     RSPoint endPoint2(data.center.GetX() + (data.radius - data.thickness * PERCENT_HALF) * std::cos(endRadian) +
946                           data.thickness * PERCENT_HALF,
947         data.center.GetY() + (data.radius - data.thickness * PERCENT_HALF) * std::sin(endRadian) +
948             data.thickness * PERCENT_HALF);
949     path.ArcTo(endPoint1, endPoint2, endAngle, HALF_CIRCLE);
950 
951     if (Positive(sweepAngle)) {
952         RSPoint inPoint1(
953             data.center.GetX() - data.radius + data.thickness, data.center.GetY() - data.radius + data.thickness);
954         RSPoint inPoint2(
955             data.center.GetX() + data.radius - data.thickness, data.center.GetY() + data.radius - data.thickness);
956         path.ArcTo(inPoint1, inPoint2, startAngle + sweepAngle, -sweepAngle);
957     }
958     path.Close();
959 }
960 }
961