1 /*
2  * Copyright (c) 2023-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 
16 #include "core/components_ng/pattern/progress/progress_modifier.h"
17 
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/geometry/ng/size_t.h"
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/base/modifier.h"
24 #include "core/components_ng/render/drawing.h"
25 #include "core/components_ng/render/drawing_prop_convertor.h"
26 #include "core/pipeline/pipeline_base.h"
27 
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr int32_t INT32_TWO = 2;
31 constexpr int32_t ANGLE_45 = 45;
32 constexpr int32_t ANGLE_90 = 90;
33 constexpr int32_t ANGLE_180 = 180;
34 constexpr int32_t ANGLE_270 = 270;
35 constexpr int32_t ANGLE_360 = 360;
36 constexpr int32_t TIME_2000 = 2000; // 2000 ms
37 constexpr int32_t TIME_4000 = 4000; // 4000 ms
38 constexpr int32_t LOADING_ANIMATION_DURATION = 2000;
39 constexpr float DEFAULT_MAX_VALUE = 100.0f;
40 constexpr float DEFAULT_SCALE_WIDTH = 10.0f;
41 constexpr int32_t DEFAULT_SCALE_COUNT = 100;
42 constexpr double DEFAULT_CAPSULE_BORDER_WIDTH = 0.0;
43 constexpr float FLOAT_ZERO_FIVE = 0.5f;
44 constexpr float FLOAT_TWO_ZERO = 2.0f;
45 constexpr float SPRING_MOTION_RESPONSE = 0.314f;
46 constexpr float SPRING_MOTION_DAMPING_FRACTION = 0.95f;
47 constexpr Dimension SWEEP_WIDTH = 80.0_vp;
48 constexpr float RING_SHADOW_OFFSET_X = 5.0f;
49 constexpr float RING_SHADOW_OFFSET_Y = 5.0f;
50 constexpr float RING_SHADOW_BLUR_RADIUS_MIN = 5.0f;
51 constexpr float RING_SHADOW_VALID_RADIUS_MIN = 10.0f;
52 constexpr float RING_SHADOW_OPACITY = 0.4f;
53 constexpr Dimension LINEAR_SWEEPING_LEN = 80.0_vp;
54 } // namespace
ProgressModifier(const ProgressAnimatableProperty & progressAnimatableProperty_)55 ProgressModifier::ProgressModifier(const ProgressAnimatableProperty& progressAnimatableProperty_)
56     : strokeWidth_(AceType::MakeRefPtr<AnimatablePropertyFloat>(progressAnimatableProperty_.strokeWidth)),
57       color_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(progressAnimatableProperty_.color))),
58       bgColor_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(progressAnimatableProperty_.bgColor))),
59       borderColor_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(progressAnimatableProperty_.borderColor))),
60       value_(AceType::MakeRefPtr<AnimatablePropertyFloat>(progressAnimatableProperty_.value)),
61       ringProgressColors_(AceType::MakeRefPtr<AnimatablePropertyVectorColor>(
62         GradientArithmetic(progressAnimatableProperty_.ringProgressColor))),
63       sweepingDate_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
64       trailingHeadDate_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
65       trailingTailDate_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f)),
66       strokeRadius_(AceType::MakeRefPtr<AnimatablePropertyFloat>(progressAnimatableProperty_.strokeRadius)),
67       offset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
68       contentSize_(AceType::MakeRefPtr<PropertySizeF>(SizeF())),
69       maxValue_(AceType::MakeRefPtr<PropertyFloat>(DEFAULT_MAX_VALUE)),
70       scaleWidth_(AceType::MakeRefPtr<PropertyFloat>(DEFAULT_SCALE_WIDTH)),
71       scaleCount_(AceType::MakeRefPtr<PropertyInt>(DEFAULT_SCALE_COUNT)),
72       progressType_(AceType::MakeRefPtr<PropertyInt>(static_cast<int32_t>(ProgressType::LINEAR))),
73       capsuleBorderWidth_(AceType::MakeRefPtr<PropertyFloat>(DEFAULT_CAPSULE_BORDER_WIDTH)),
74       sweepEffect_(AceType::MakeRefPtr<PropertyBool>(false)),
75       ringSweepEffect_(AceType::MakeRefPtr<PropertyBool>(false)),
76       linearSweepEffect_(AceType::MakeRefPtr<PropertyBool>(false)),
77       paintShadow_(AceType::MakeRefPtr<PropertyBool>(false)),
78       progressStatus_(AceType::MakeRefPtr<PropertyInt>(static_cast<int32_t>(ProgressStatus::PROGRESSING))),
79       isItalic_(AceType::MakeRefPtr<PropertyBool>(false)),
80       smoothEffect_(AceType::MakeRefPtr<PropertyBool>(true)),
81       useContentModifier_(AceType::MakeRefPtr<PropertyBool>(false)),
82       isRightToLeft_(AceType::MakeRefPtr<PropertyBool>(false))
83 {
84     AttachProperty(strokeWidth_);
85     AttachProperty(color_);
86     AttachProperty(bgColor_);
87     AttachProperty(borderColor_);
88     AttachProperty(maxValue_);
89     AttachProperty(value_);
90     AttachProperty(scaleWidth_);
91     AttachProperty(scaleCount_);
92     AttachProperty(progressType_);
93     AttachProperty(capsuleBorderWidth_);
94     AttachProperty(sweepEffect_);
95     AttachProperty(trailingHeadDate_);
96     AttachProperty(trailingTailDate_);
97     AttachProperty(strokeRadius_);
98 
99     AttachProperty(ringProgressColors_);
100     AttachProperty(sweepingDate_);
101     AttachProperty(paintShadow_);
102     AttachProperty(progressStatus_);
103     AttachProperty(ringSweepEffect_);
104     AttachProperty(linearSweepEffect_);
105     AttachProperty(isItalic_);
106     AttachProperty(smoothEffect_);
107     AttachProperty(isRightToLeft_);
108 }
109 
onDraw(DrawingContext & context)110 void ProgressModifier::onDraw(DrawingContext& context)
111 {
112     if (useContentModifier_->Get()) {
113         return;
114     }
115     ContentDrawWithFunction(context);
116 }
117 
SetUseContentModifier(bool useContentModifier)118 void ProgressModifier::SetUseContentModifier(bool useContentModifier)
119 {
120     if (useContentModifier_) {
121         useContentModifier_->Set(useContentModifier);
122     }
123 }
124 
SetStrokeWidth(float width)125 void ProgressModifier::SetStrokeWidth(float width)
126 {
127     CHECK_NULL_VOID(strokeWidth_);
128     strokeWidth_->Set(width);
129 }
130 
SetColor(LinearColor color)131 void ProgressModifier::SetColor(LinearColor color)
132 {
133     CHECK_NULL_VOID(color_);
134     color_->Set(color);
135 }
136 
SetBackgroundColor(LinearColor color)137 void ProgressModifier::SetBackgroundColor(LinearColor color)
138 {
139     CHECK_NULL_VOID(bgColor_);
140     bgColor_->Set(color);
141 }
142 
SetBorderColor(LinearColor color)143 void ProgressModifier::SetBorderColor(LinearColor color)
144 {
145     CHECK_NULL_VOID(borderColor_);
146     borderColor_->Set(color);
147 }
148 
SetProgressType(ProgressType type)149 void ProgressModifier::SetProgressType(ProgressType type)
150 {
151     CHECK_NULL_VOID(progressType_);
152     progressType_->Set(static_cast<int32_t>(type));
153 }
154 
ProcessSweepingAnimation(ProgressType type,float value)155 void ProgressModifier::ProcessSweepingAnimation(ProgressType type, float value)
156 {
157     if (!isVisible_) {
158         return;
159     }
160 
161     switch (type) {
162         case ProgressType::RING:
163             ProcessRingSweepingAnimation(value);
164             break;
165         case ProgressType::LINEAR:
166             ProcessLinearSweepingAnimation(value);
167             break;
168         case ProgressType::CAPSULE:
169             StartCapsuleSweepingAnimation(value);
170             break;
171         default:
172             break;
173     }
174 }
175 
StartCapsuleSweepingAnimation(float value)176 void ProgressModifier::StartCapsuleSweepingAnimation(float value)
177 {
178     auto contentSize = contentSize_->Get();
179     float barLength =
180         GreatOrEqual(contentSize.Width(), contentSize.Height()) ? contentSize.Width() : contentSize.Height();
181     float date = (value / maxValue_->Get()) * barLength + SWEEP_WIDTH.ConvertToPx();
182     float sweepSpeed = barLength / TIME_2000; // It takes 2 seconds to sweep the whole bar length.
183 
184     if (!isSweeping_ && sweepEffect_->Get() && isVisible_) {
185         StartCapsuleSweepingAnimationImpl(date, sweepSpeed);
186     } else if (!sweepEffect_->Get() || !isVisible_) {
187         StopSweepingAnimation();
188     } else {
189         dateUpdated_ = !NearEqual(sweepingDateBackup_, date);
190     }
191     sweepingDateBackup_ = date;
192 }
193 
StartCapsuleSweepingAnimationImpl(float value,float speed)194 void ProgressModifier::StartCapsuleSweepingAnimationImpl(float value, float speed)
195 {
196     if (!isVisible_ || !sweepEffect_) {
197         return;
198     }
199 
200     isSweeping_ = true;
201     sweepingDate_->Set(0.0f);
202     speed = NearZero(speed) ? 1.0f : speed;
203     int32_t time = value / speed;
204     AnimationOption option = AnimationOption();
205     auto motion = AceType::MakeRefPtr<LinearCurve>();
206     option.SetCurve(motion);
207     option.SetIteration(-1);
208     option.SetDuration(time);
209 
210     AnimationUtils::Animate(
211         option,
212         [value, id = Container::CurrentId(), weak = WeakClaim(this)]() {
213             ContainerScope scope(id);
214             auto modifier = weak.Upgrade();
215             CHECK_NULL_VOID(modifier);
216             modifier->sweepingDate_->Set(value);
217         },
218         nullptr,
219         [id = Container::CurrentId(), weak = WeakClaim(this), speed]() {
220             ContainerScope scope(id);
221             auto modifier = weak.Upgrade();
222             CHECK_NULL_VOID(modifier);
223             float currentDate = modifier->sweepingDate_->Get();
224             if (modifier->dateUpdated_) {
225                 modifier->dateUpdated_ = false;
226                 modifier->StopSweepingAnimation(currentDate);
227                 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
228                 auto context = PipelineBase::GetCurrentContext();
229                 context->RequestFrame();
230             }
231         });
232 }
233 
SetRingProgressColor(const Gradient & color)234 void ProgressModifier::SetRingProgressColor(const Gradient& color)
235 {
236     CHECK_NULL_VOID(ringProgressColors_);
237     ringProgressColors_->Set(GradientArithmetic(color));
238 }
239 
SetPaintShadow(bool paintShadow)240 void ProgressModifier::SetPaintShadow(bool paintShadow)
241 {
242     CHECK_NULL_VOID(paintShadow_);
243     paintShadow_->Set(paintShadow);
244 }
245 
SetProgressStatus(ProgressStatus status)246 void ProgressModifier::SetProgressStatus(ProgressStatus status)
247 {
248     CHECK_NULL_VOID(progressStatus_);
249     progressStatus_->Set(static_cast<int32_t>(status));
250     if (status == ProgressStatus::LOADING) {
251         StartRingLoadingAnimation();
252     }
253 }
254 
SetIsItalic(bool isItalic)255 void ProgressModifier::SetIsItalic(bool isItalic)
256 {
257     CHECK_NULL_VOID(isItalic_);
258     isItalic_->Set(isItalic);
259 }
260 
SetVisible(bool isVisible)261 void ProgressModifier::SetVisible(bool isVisible)
262 {
263     CHECK_NULL_VOID(isVisible_ != isVisible);
264     isVisible_ = isVisible;
265 
266     if (!isVisible) {
267         if (isLoading_) {
268             StopRingLoadingHeadAnimation();
269             StopRingLoadingTailAnimation();
270         }
271 
272         if (isSweeping_) {
273             StopSweepingAnimation();
274         }
275     }
276 }
277 
SetSmoothEffect(bool value)278 void ProgressModifier::SetSmoothEffect(bool value)
279 {
280     CHECK_NULL_VOID(smoothEffect_);
281     smoothEffect_->Set(value);
282 }
283 
StartRingLoadingAnimation()284 void ProgressModifier::StartRingLoadingAnimation()
285 {
286     if (!isLoading_ && isVisible_) {
287         isLoading_ = true;
288         StartRingLoadingHeadAnimation();
289         StartRingLoadingTailAnimation();
290     }
291 }
292 
StartRingLoadingHeadAnimation()293 void ProgressModifier::StartRingLoadingHeadAnimation()
294 {
295     auto context = PipelineBase::GetCurrentContext();
296     CHECK_NULL_VOID(context);
297     bool isFormRender = context->IsFormRender();
298     AnimationOption optionHead = AnimationOption();
299     auto curveHead = AceType::MakeRefPtr<TailingHeadCurve>();
300     optionHead.SetDuration(LOADING_ANIMATION_DURATION);
301     optionHead.SetCurve(curveHead);
302     optionHead.SetIteration(isFormRender ? 1 : -1);
303     AnimationUtils::Animate(
304         optionHead, [&]() { trailingHeadDate_->Set(ANGLE_360); }, nullptr,
305         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
306             ContainerScope scope(id);
307             auto modifier = weak.Upgrade();
308             CHECK_NULL_VOID(modifier);
309             if (static_cast<ProgressStatus>(modifier->progressStatus_->Get()) == ProgressStatus::PROGRESSING) {
310                 modifier->StopRingLoadingHeadAnimation();
311             }
312         });
313 }
314 
StartRingLoadingTailAnimation()315 void ProgressModifier::StartRingLoadingTailAnimation()
316 {
317     auto context = PipelineBase::GetCurrentContext();
318     CHECK_NULL_VOID(context);
319     bool isFormRender = context->IsFormRender();
320     AnimationOption optionTail = AnimationOption();
321     auto curveTail = AceType::MakeRefPtr<CubicCurve>(0.33f, 0.00f, 0.66f, 0.10f);
322     optionTail.SetDuration(LOADING_ANIMATION_DURATION);
323     optionTail.SetCurve(curveTail);
324     optionTail.SetIteration(isFormRender ? 1 : -1);
325     AnimationUtils::Animate(
326         optionTail,
327         [&]() { trailingTailDate_->Set(ANGLE_360); },
328         nullptr,
329         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
330             ContainerScope scope(id);
331             auto modifier = weak.Upgrade();
332             CHECK_NULL_VOID(modifier);
333             if (static_cast<ProgressStatus>(modifier->progressStatus_->Get()) == ProgressStatus::PROGRESSING) {
334                 modifier->StopRingLoadingTailAnimation();
335                 if (GreatOrEqual(modifier->valueBackup_, 0.0f)) {
336                     modifier->SetValue(modifier->valueBackup_);
337                 }
338             }
339         });
340 }
341 
StopRingLoadingHeadAnimation()342 void ProgressModifier::StopRingLoadingHeadAnimation()
343 {
344     AnimationOption option = AnimationOption();
345     option.SetDuration(0);
346     AnimationUtils::Animate(option, [&]() { trailingHeadDate_->Set(0.0f); });
347 }
348 
StopRingLoadingTailAnimation()349 void ProgressModifier::StopRingLoadingTailAnimation()
350 {
351     AnimationOption option = AnimationOption();
352     option.SetDuration(0);
353     AnimationUtils::Animate(option, [&]() { trailingTailDate_->Set(0.0f); });
354     isLoading_ = false;
355 }
356 
ProcessRingSweepingAnimation(float value)357 void ProgressModifier::ProcessRingSweepingAnimation(float value)
358 {
359     if (NearZero(value) || NearEqual(value, maxValue_->Get())) {
360         StopSweepingAnimation();
361     } else {
362         StartRingSweepingAnimation(value);
363     }
364 }
365 
ProcessLinearSweepingAnimation(float value)366 void ProgressModifier::ProcessLinearSweepingAnimation(float value)
367 {
368     if (NearZero(value) || NearEqual(value, maxValue_->Get())) {
369         StopSweepingAnimation();
370     } else {
371         StartLinearSweepingAnimation(value);
372     }
373 }
374 
StartRingSweepingAnimation(float value)375 void ProgressModifier::StartRingSweepingAnimation(float value)
376 {
377     float maxValue = NearZero(maxValue_->Get()) ? 1.0f : maxValue_->Get();
378     float date = value / maxValue * ANGLE_360 + ANGLE_45;
379     float additionalAngle = CalcRingProgressAdditionalAngle();
380     date += additionalAngle * 2;
381     float sweepSpeed = float(ANGLE_360) / TIME_4000; // It takes 4 seconds to sweep a circle
382 
383     if (!isSweeping_ && ringSweepEffect_->Get()) {
384         StartRingSweepingAnimationImpl(date, sweepSpeed);
385     } else if (!ringSweepEffect_->Get() || NearEqual(valueBackup_, maxValue_->Get())) {
386         StopSweepingAnimation();
387     } else {
388         dateUpdated_ = !NearEqual(sweepingDateBackup_, date);
389     }
390 
391     sweepingDateBackup_ = date;
392 }
393 
StartRingSweepingAnimationImpl(float date,float speed)394 void ProgressModifier::StartRingSweepingAnimationImpl(float date, float speed)
395 {
396     if (!isVisible_ || !ringSweepEffect_) {
397         return;
398     }
399 
400     if (NearEqual(valueBackup_, maxValue_->Get())) {
401         return;
402     }
403 
404     auto context = PipelineBase::GetCurrentContext();
405     CHECK_NULL_VOID(context);
406     bool isFormRender = context->IsFormRender();
407     isSweeping_ = true;
408     AnimationOption option = AnimationOption();
409     speed = NearZero(speed) ? 1.0f : speed;
410     int32_t time = date / speed;
411     auto motion = AceType::MakeRefPtr<LinearCurve>();
412     option.SetCurve(motion);
413     option.SetIteration(isFormRender ? 1 : -1);
414     option.SetDuration(time);
415     AnimationUtils::Animate(
416         option,
417         [&]() { sweepingDate_->Set(date); },
418         nullptr,
419         [weak = WeakClaim(this), id = Container::CurrentId(), speed]() {
420             ContainerScope scope(id);
421             auto modifier = weak.Upgrade();
422             CHECK_NULL_VOID(modifier);
423             if (modifier->dateUpdated_) {
424                 modifier->dateUpdated_ = false;
425                 float currentDate = modifier->sweepingDate_->Get();
426                 modifier->StopSweepingAnimation(currentDate);
427                 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
428                 auto context = PipelineBase::GetCurrentContext();
429                 context->RequestFrame();
430             }
431         });
432 }
433 
StartContinuousSweepingAnimation(float currentDate,float newDate,float speed)434 void ProgressModifier::StartContinuousSweepingAnimation(float currentDate, float newDate, float speed)
435 {
436     if (!isVisible_ || !IsSweepEffectOn()) {
437         return;
438     }
439 
440     if (NearEqual(valueBackup_, maxValue_->Get()) &&
441         (ProgressType(progressType_->Get()) == ProgressType::LINEAR ||
442         ProgressType(progressType_->Get()) == ProgressType::RING)) {
443         return;
444     }
445 
446     isSweeping_ = true;
447     speed = NearZero(speed) ? 1.0f : speed;
448     int32_t time = (newDate - currentDate) / speed;
449     AnimationOption option = AnimationOption();
450     auto motion = AceType::MakeRefPtr<LinearCurve>();
451     option.SetCurve(motion);
452     option.SetDuration(time);
453     AnimationUtils::Animate(
454         option,
455         [&]() { sweepingDate_->Set(newDate); },
456         [weak = WeakClaim(this), id = Container::CurrentId(), speed]() {
457             ContainerScope scope(id);
458             auto modifier = weak.Upgrade();
459             CHECK_NULL_VOID(modifier);
460             if (!modifier->dateUpdated_) {
461                 modifier->sweepingDate_->Set(0.0f);
462                 switch (ProgressType(modifier->progressType_->Get())) {
463                     case ProgressType::LINEAR:
464                         modifier->StartLinearSweepingAnimationImpl(modifier->sweepingDateBackup_, speed);
465                         break;
466                     case ProgressType::CAPSULE:
467                         modifier->StartCapsuleSweepingAnimationImpl(modifier->sweepingDateBackup_, speed);
468                         break;
469                     case ProgressType::RING:
470                         modifier->StartRingSweepingAnimationImpl(modifier->sweepingDateBackup_, speed);
471                         break;
472                     default:
473                         return;
474                 }
475                 auto context = PipelineBase::GetCurrentContext();
476                 context->RequestFrame();
477             } else {
478                 modifier->dateUpdated_ = false;
479                 float currentDate = modifier->sweepingDate_->Get();
480                 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
481                 auto context = PipelineBase::GetCurrentContext();
482                 context->RequestFrame();
483             }
484         });
485 }
486 
IsSweepEffectOn()487 bool ProgressModifier::IsSweepEffectOn()
488 {
489     switch (ProgressType(progressType_->Get())) {
490         case ProgressType::LINEAR:
491             return linearSweepEffect_;
492         case ProgressType::CAPSULE:
493             return sweepEffect_;
494         case ProgressType::RING:
495             return ringSweepEffect_;
496         default:
497             return false;
498     }
499 }
500 
StopSweepingAnimation(float date)501 void ProgressModifier::StopSweepingAnimation(float date)
502 {
503     if (isSweeping_) {
504         isSweeping_ = false;
505         dateUpdated_ = false;
506         AnimationOption option = AnimationOption();
507         option.SetDuration(0);
508         AnimationUtils::Animate(option, [&]() { sweepingDate_->Set(date); });
509     }
510 }
511 
CalcRingProgressAdditionalAngle() const512 float ProgressModifier::CalcRingProgressAdditionalAngle() const
513 {
514     auto contentSize = contentSize_->Get();
515     auto strokeWidth = strokeWidth_->Get();
516     PointF centerPt = PointF(contentSize.Width() / 2, contentSize.Height() / 2);
517     float radius = std::min(contentSize.Width() / 2, contentSize.Height() / 2);
518     auto paintShadow = paintShadow_->Get() && GreatNotEqual(radius, RING_SHADOW_VALID_RADIUS_MIN);
519     auto shadowBlurOffset = paintShadow ? strokeWidth / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
520     if (GreatOrEqual(strokeWidth + shadowBlurOffset, radius)) {
521         strokeWidth = radius / 2;
522         shadowBlurOffset = paintShadow ? strokeWidth / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
523     }
524     return asinf((strokeWidth / 2) / (radius - strokeWidth / 2 - shadowBlurOffset)) * ANGLE_180 / PI_NUM;
525 }
526 
StartLinearSweepingAnimation(float value)527 void ProgressModifier::StartLinearSweepingAnimation(float value)
528 {
529     auto contentSize = contentSize_->Get();
530     float radius = strokeWidth_->Get() / 2;
531     float barLength = 0.0f;
532     float dateLength = 0.0f;
533 
534     if (GreatOrEqual(contentSize.Width(), contentSize.Height())) {
535         barLength = contentSize.Width() - radius * 2;
536     } else {
537         barLength = contentSize.Height() - radius * 2;
538     }
539 
540     if (NearEqual(barLength, 0.0f)) {
541         return;
542     }
543 
544     dateLength = barLength * value / maxValue_->Get();
545     if (NearEqual(dateLength, 0.0f)) {
546         return;
547     }
548 
549     float date = dateLength + strokeWidth_->Get() + LINEAR_SWEEPING_LEN.ConvertToPx();
550     float sweepSpeed = barLength / TIME_2000; // It takes 2 seconds to sweep the whole bar length
551     if (!isSweeping_ && linearSweepEffect_->Get()) {
552         StartLinearSweepingAnimationImpl(date, sweepSpeed);
553     } else if (!linearSweepEffect_->Get() || NearEqual(valueBackup_, maxValue_->Get())) {
554         StopSweepingAnimation();
555     } else {
556         dateUpdated_ = !NearEqual(sweepingDateBackup_, date);
557     }
558 
559     sweepingDateBackup_ = date;
560 }
561 
StartLinearSweepingAnimationImpl(float date,float speed)562 void ProgressModifier::StartLinearSweepingAnimationImpl(float date, float speed)
563 {
564     if (!isVisible_ || !linearSweepEffect_) {
565         return;
566     }
567 
568     if (NearEqual(valueBackup_, maxValue_->Get())) {
569         return;
570     }
571 
572     auto context = PipelineBase::GetCurrentContext();
573     CHECK_NULL_VOID(context);
574     bool isFormRender = context->IsFormRender();
575     isSweeping_ = true;
576     sweepingDate_->Set(0.0f);
577     speed = NearZero(speed) ? 1.0f : speed;
578     int32_t time = date / speed;
579     AnimationOption option = AnimationOption();
580     auto motion = AceType::MakeRefPtr<LinearCurve>();
581     option.SetCurve(motion);
582     option.SetIteration(isFormRender ? 1 : -1);
583     option.SetDuration(time);
584     AnimationUtils::Animate(
585         option,
586         [&]() { sweepingDate_->Set(date); },
587         nullptr,
588         [weak = WeakClaim(this), id = Container::CurrentId(), speed]() {
589             ContainerScope scope(id);
590             auto modifier = weak.Upgrade();
591             CHECK_NULL_VOID(modifier);
592             if (modifier->dateUpdated_) {
593                 modifier->dateUpdated_ = false;
594                 float currentDate = modifier->sweepingDate_->Get();
595                 modifier->StopSweepingAnimation(currentDate);
596                 modifier->StartContinuousSweepingAnimation(currentDate, modifier->sweepingDateBackup_, speed);
597                 auto context = PipelineBase::GetCurrentContext();
598                 context->RequestFrame();
599             }
600         });
601 }
602 
SetMaxValue(float value)603 void ProgressModifier::SetMaxValue(float value)
604 {
605     CHECK_NULL_VOID(maxValue_);
606     maxValue_->Set(value);
607 }
608 
SetValue(float value)609 void ProgressModifier::SetValue(float value)
610 {
611     valueBackup_ = value;
612 
613     if (isLoading_) {
614         return;
615     }
616 
617     CHECK_NULL_VOID(value_);
618     AnimationOption option = AnimationOption();
619     if (smoothEffect_->Get()) {
620         if (isVisible_) {
621             auto motion =
622                 AceType::MakeRefPtr<ResponsiveSpringMotion>(SPRING_MOTION_RESPONSE, SPRING_MOTION_DAMPING_FRACTION);
623             option.SetCurve(motion);
624         } else {
625             option.SetDuration(0);
626         }
627         AnimationUtils::Animate(option, [&]() { value_->Set(value); });
628     } else {
629         value_->Set(value);
630     }
631     ProcessSweepingAnimation(ProgressType(progressType_->Get()), value);
632 }
633 
SetScaleWidth(float value)634 void ProgressModifier::SetScaleWidth(float value)
635 {
636     CHECK_NULL_VOID(scaleWidth_);
637     scaleWidth_->Set(value);
638 }
639 
SetScaleCount(int32_t value)640 void ProgressModifier::SetScaleCount(int32_t value)
641 {
642     CHECK_NULL_VOID(scaleCount_);
643     scaleCount_->Set(value);
644 }
645 
SetContentOffset(const OffsetF & offset)646 void ProgressModifier::SetContentOffset(const OffsetF& offset)
647 {
648     CHECK_NULL_VOID(offset_);
649     offset_->Set(offset);
650 }
651 
SetContentSize(const SizeF & contentSize)652 void ProgressModifier::SetContentSize(const SizeF& contentSize)
653 {
654     CHECK_NULL_VOID(contentSize_);
655     contentSize_->Set(contentSize);
656 }
657 
SetBorderWidth(float width)658 void ProgressModifier::SetBorderWidth(float width)
659 {
660     capsuleBorderWidth_->Set(width);
661 }
662 
SetSweepEffect(bool value)663 void ProgressModifier::SetSweepEffect(bool value)
664 {
665     sweepEffect_->Set(value);
666 }
667 
SetRingSweepEffect(bool value)668 void ProgressModifier::SetRingSweepEffect(bool value)
669 {
670     ringSweepEffect_->Set(value);
671 }
672 
SetLinearSweepEffect(bool value)673 void ProgressModifier::SetLinearSweepEffect(bool value)
674 {
675     linearSweepEffect_->Set(value);
676 }
677 
ContentDrawWithFunction(DrawingContext & context)678 void ProgressModifier::ContentDrawWithFunction(DrawingContext& context)
679 {
680     auto contentSize = contentSize_->Get();
681     auto& canvas = context.canvas;
682     if (progressType_->Get() == static_cast<int32_t>(ProgressType::LINEAR)) {
683         PaintLinear(canvas, offset_->Get(), contentSize);
684     } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::RING)) {
685         PaintRing(canvas, offset_->Get(), contentSize);
686     } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::SCALE)) {
687         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
688             PaintScaleRingForApiNine(canvas, offset_->Get(), contentSize);
689         } else {
690             PaintScaleRing(canvas, offset_->Get(), contentSize);
691         }
692     } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::MOON)) {
693         PaintMoon(canvas, offset_->Get(), contentSize);
694     } else if (progressType_->Get() == static_cast<int32_t>(ProgressType::CAPSULE)) {
695         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
696             if (contentSize.Width() >= contentSize.Height()) {
697                 PaintCapsuleForApiNine(canvas, offset_->Get(), contentSize);
698             } else {
699                 PaintVerticalCapsuleForApiNine(canvas, offset_->Get(), contentSize);
700             }
701         } else {
702             if (contentSize.Width() >= contentSize.Height()) {
703                 PaintCapsule(canvas, offset_->Get(), contentSize);
704             } else {
705                 PaintVerticalCapsule(canvas, offset_->Get(), contentSize);
706             }
707         }
708     } else {
709         PaintLinear(canvas, offset_->Get(), contentSize);
710     }
711 }
712 
PaintLinear(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const713 void ProgressModifier::PaintLinear(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
714 {
715     RSBrush brush;
716     brush.SetAntiAlias(true);
717     brush.SetColor(ToRSColor(bgColor_->Get()));
718     double radius = strokeRadius_->Get();
719     if (contentSize.Width() >= contentSize.Height()) {
720         double barLength = contentSize.Width() - radius * INT32_TWO;
721         CHECK_NULL_VOID(Positive(barLength));
722         double dateLength = std::min(barLength * value_->Get() / maxValue_->Get(), barLength);
723         canvas.AttachBrush(brush);
724         canvas.DrawRoundRect(
725             { { offset.GetX(), offset.GetY(), contentSize.Width() + offset.GetX(),
726                                    strokeWidth_->Get() + offset.GetY() },
727             radius, radius });
728         canvas.DetachBrush();
729         // progress selected part
730         CHECK_NULL_VOID(Positive(dateLength));
731         brush.SetColor(ToRSColor((color_->Get())));
732         canvas.AttachBrush(brush);
733 #ifndef USE_ROSEN_DRAWING
734         RSPath path;
735 #else
736         RSRecordingPath path;
737 #endif
738         float leftTopX = !isRightToLeft_->Get()
739                              ? offset.GetX()
740                              : contentSize.Width() + offset.GetX() - (dateLength + radius * INT32_TWO);
741         float leftTopY = offset.GetY();
742         float rightBottomX = !isRightToLeft_->Get() ? dateLength + offset.GetX() + radius * INT32_TWO
743                                                     : contentSize.Width() + offset.GetX();
744         float rightBottomY = strokeWidth_->Get() + offset.GetY();
745         path.AddRoundRect({ leftTopX, leftTopY, rightBottomX, rightBottomY }, radius, radius);
746         canvas.DrawPath(path);
747         canvas.DetachBrush();
748         canvas.Restore();
749         PaintLinearSweeping(canvas, offset, path, true, contentSize);
750     } else {
751         double barLength = contentSize.Height() - radius * INT32_TWO;
752         CHECK_NULL_VOID(Positive(barLength));
753         double dateLength = std::min(barLength * value_->Get() / maxValue_->Get(), barLength);
754         canvas.AttachBrush(brush);
755         canvas.DrawRoundRect(
756             { { offset.GetX(), offset.GetY(), strokeWidth_->Get() + offset.GetX(),
757                                    contentSize.Height() + offset.GetY() },
758             radius, radius });
759         canvas.DetachBrush();
760         // progress selected part
761         CHECK_NULL_VOID(Positive(dateLength));
762         brush.SetColor(ToRSColor((color_->Get())));
763         canvas.AttachBrush(brush);
764 #ifndef USE_ROSEN_DRAWING
765         RSPath path;
766 #else
767         RSRecordingPath path;
768 #endif
769         path.AddRoundRect(
770             { offset.GetX(), offset.GetY(), strokeWidth_->Get() + offset.GetX(),
771               dateLength + offset.GetY() + radius * INT32_TWO },
772             radius, radius);
773         canvas.DrawPath(path);
774         canvas.DetachBrush();
775         canvas.Restore();
776         PaintLinearSweeping(canvas, offset, path, false, contentSize);
777     }
778 }
779 
PaintLinearSweeping(RSCanvas & canvas,const OffsetF & offset,const RSPath & path,bool isHorizontal,const SizeF & contentSize) const780 void ProgressModifier::PaintLinearSweeping(
781     RSCanvas& canvas, const OffsetF& offset, const RSPath& path, bool isHorizontal, const SizeF& contentSize) const
782 {
783     if (NearZero(value_->Get()) || NearZero(maxValue_->Get())) {
784         return;
785     }
786 
787     // If the progress reaches 100%, stop sweeping.
788     if (NearEqual(value_->Get(), maxValue_->Get())) {
789         return;
790     }
791 
792     std::vector<RSColorQuad> colors;
793     std::vector<float> pos;
794     GenerateLinearSweepingGradientInfo(colors, pos);
795 
796     RSBrush brush;
797     float leftTopX = 0.0f;
798     float leftTopY = 0.0f;
799     float rightBottomX = 0.0f;
800     float rightBottomY = 0.0f;
801     if (isHorizontal) {
802         leftTopX = !isRightToLeft_->Get() ? offset.GetX() + sweepingDate_->Get() - LINEAR_SWEEPING_LEN.ConvertToPx()
803                                           : contentSize.Width() + offset.GetX() - sweepingDate_->Get();
804         leftTopY = offset.GetY();
805         rightBottomX = !isRightToLeft_->Get() ? offset.GetX() + sweepingDate_->Get()
806                                               : contentSize.Width() + offset.GetX() - sweepingDate_->Get() +
807                                                     LINEAR_SWEEPING_LEN.ConvertToPx();
808         rightBottomY = offset.GetY() + strokeWidth_->Get();
809     } else {
810         leftTopX = offset.GetX();
811         leftTopY = offset.GetY() + sweepingDate_->Get() - LINEAR_SWEEPING_LEN.ConvertToPx();
812         rightBottomX = offset.GetX() + strokeWidth_->Get();
813         rightBottomY = offset.GetY() + sweepingDate_->Get();
814     }
815 
816     RSRect rect(leftTopX, leftTopY, rightBottomX, rightBottomY);
817 #ifndef USE_ROSEN_DRAWING
818     brush.SetShaderEffect(RSShaderEffect::CreateLinearGradient(
819         ToRSPoint(PointF(leftTopX, leftTopY)),
820         ToRSPoint(PointF(rightBottomX, rightBottomY)),
821         colors, pos, RSTileMode::CLAMP));
822 #else
823     brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
824         ToRSPoint(PointF(leftTopX, leftTopY)),
825         ToRSPoint(PointF(rightBottomX, rightBottomY)),
826         colors, pos, RSTileMode::CLAMP));
827 #endif
828     canvas.Save();
829     canvas.ClipPath(path, RSClipOp::INTERSECT, true);
830     canvas.AttachBrush(brush);
831     canvas.DrawRect(rect);
832     canvas.DetachBrush();
833     canvas.Restore();
834 }
835 
GenerateLinearSweepingGradientInfo(std::vector<RSColorQuad> & colors,std::vector<float> & pos) const836 void ProgressModifier::GenerateLinearSweepingGradientInfo(
837     std::vector<RSColorQuad>& colors, std::vector<float>& pos) const
838 {
839     Gradient gradient;
840     Color sweepingColorBase = Color::WHITE;
841     Color sweepingColorEnd = sweepingColorBase.ChangeOpacity(0.0);
842     GradientColor gradientColorEnd;
843     gradientColorEnd.SetColor(sweepingColorEnd);
844     gradientColorEnd.SetDimension(Dimension(0.0, DimensionUnit::PERCENT));
845     gradient.AddColor(gradientColorEnd);
846 
847     // The sweep layer is a white gradient layer of length 80 vp with a opacity of 0.3 at 75 vp and 0.0 at both ends.
848     Dimension stageLen = 75.0_vp;
849     Color sweepingColorMiddle = sweepingColorBase.ChangeOpacity(0.3);
850     GradientColor gradientColorMiddle;
851     gradientColorMiddle.SetColor(sweepingColorMiddle);
852     gradientColorMiddle.SetDimension(
853         Dimension(!isRightToLeft_->Get() ? stageLen.ConvertToPx() / LINEAR_SWEEPING_LEN.ConvertToPx()
854                                          : 1 - stageLen.ConvertToPx() / LINEAR_SWEEPING_LEN.ConvertToPx(),
855             DimensionUnit::PERCENT));
856     gradient.AddColor(gradientColorMiddle);
857 
858     Color sweepingColorStart = sweepingColorBase.ChangeOpacity(0.0);
859     GradientColor gradientColorStart;
860     gradientColorStart.SetColor(sweepingColorStart);
861     gradientColorStart.SetDimension(Dimension(1.0, DimensionUnit::PERCENT));
862     gradient.AddColor(gradientColorStart);
863 
864     auto gradientColors = gradient.GetColors();
865     for (size_t i = 0; i < gradientColors.size(); i++) {
866         colors.emplace_back(gradientColors[i].GetColor().GetValue());
867         pos.emplace_back(gradientColors[i].GetDimension().Value());
868     }
869 }
870 
PaintRing(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const871 void ProgressModifier::PaintRing(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
872 {
873     auto centerPt = PointF(contentSize.Width() / 2, contentSize.Height() / 2) + offset;
874     auto radius = std::min(contentSize.Width() / 2, contentSize.Height() / 2);
875     auto angle = std::min((value_->Get() / maxValue_->Get()) * ANGLE_360, static_cast<float>(ANGLE_360));
876     auto thickness = strokeWidth_->Get();
877     // No shadow is drawn if radius is less or equal to 10, because it is no enough space to draw both ring and shadow.
878     auto paintShadow = paintShadow_->Get() && GreatNotEqual(radius, RING_SHADOW_VALID_RADIUS_MIN);
879     auto shadowBlurOffset = paintShadow ? thickness / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
880     if (GreatOrEqual(thickness + shadowBlurOffset, radius)) {
881         thickness = radius / 2;
882         shadowBlurOffset = paintShadow ? thickness / 2 + std::max(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y) : 0.0f;
883     }
884     // The shadowBlurSigma is an empirical value. If it is greater than thickness / 5, the shadow will be cut by
885     // the canvas boundary.
886     auto shadowBlurSigma = std::max(thickness / 5, RING_SHADOW_BLUR_RADIUS_MIN);
887     radius = radius - thickness / 2 - shadowBlurOffset;
888 
889     RingProgressData ringData;
890     ringData.centerPt = centerPt;
891     ringData.radius = radius;
892     ringData.angle = angle;
893     ringData.thickness = thickness;
894     ringData.shadowBlurSigma = shadowBlurSigma;
895 
896     PaintRingBackground(canvas, ringData);
897     if (isRightToLeft_->Get()) {
898         canvas.Scale(-1, 1);
899         canvas.Translate(-((radius + shadowBlurOffset) * INT32_TWO + thickness), 0);
900     }
901     if (isLoading_) {
902         PaintTrailing(canvas, ringData);
903         return;
904     }
905 
906     if (NearZero(angle)) {
907         return;
908     }
909 
910     if (paintShadow) {
911         ringData.centerPt = centerPt + OffsetF(RING_SHADOW_OFFSET_X, RING_SHADOW_OFFSET_Y);
912         PaintRingProgressOrShadow(canvas, ringData, true);
913     }
914     ringData.centerPt = centerPt;
915     PaintRingProgressOrShadow(canvas, ringData, false);
916     PaintRingSweeping(canvas, ringData);
917 }
918 
PaintRingBackground(RSCanvas & canvas,const RingProgressData & ringProgressData) const919 void ProgressModifier::PaintRingBackground(RSCanvas& canvas, const RingProgressData& ringProgressData) const
920 {
921     RSPen pen;
922     pen.SetAntiAlias(true);
923     pen.SetWidth(ringProgressData.thickness);
924     pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
925     pen.SetColor(ToRSColor(bgColor_->Get()));
926 
927     canvas.Save();
928     canvas.AttachPen(pen);
929     canvas.DrawCircle(ToRSPoint(ringProgressData.centerPt), ringProgressData.radius);
930     canvas.DetachPen();
931     canvas.Restore();
932 }
933 
GetRingProgressGradientColors() const934 std::vector<GradientColor> ProgressModifier::GetRingProgressGradientColors() const
935 {
936     Gradient gradient = SortGradientColorsByOffset(ringProgressColors_->Get().GetGradient());
937     std::vector<GradientColor> gradientColors = gradient.GetColors();
938     // Fault protection processing, if gradientColors is empty, set to default colors.
939 
940     if (gradientColors.empty()) {
941         auto pipeline = PipelineBase::GetCurrentContext();
942         CHECK_NULL_RETURN(pipeline, gradientColors);
943         auto theme = pipeline->GetTheme<ProgressTheme>();
944         CHECK_NULL_RETURN(theme, gradientColors);
945         GradientColor endColor;
946         GradientColor beginColor;
947         endColor.SetLinearColor(LinearColor(theme->GetRingProgressEndSideColor()));
948         endColor.SetDimension(0.0);
949         beginColor.SetLinearColor(LinearColor(theme->GetRingProgressBeginSideColor()));
950         beginColor.SetDimension(1.0);
951         gradientColors.emplace_back(endColor);
952         gradientColors.emplace_back(beginColor);
953     }
954     return gradientColors;
955 }
956 
PaintRingProgressOrShadow(RSCanvas & canvas,const RingProgressData & ringProgressData,bool isShadow) const957 void ProgressModifier::PaintRingProgressOrShadow(
958     RSCanvas& canvas, const RingProgressData& ringProgressData, bool isShadow) const
959 {
960     auto angle = ringProgressData.angle;
961     float additionalAngle = CalcRingProgressAdditionalAngle();
962 
963     std::vector<GradientColor> gradientColors = GetRingProgressGradientColors();
964 
965     RSBrush brush;
966     brush.SetAntiAlias(true);
967 
968     if (isShadow) {
969         for (size_t i = 0; i < gradientColors.size(); i++) {
970             Color color = gradientColors[i].GetLinearColor().BlendOpacity(RING_SHADOW_OPACITY);
971             gradientColors[i].SetLinearColor(LinearColor(color));
972         }
973         RSFilter filter;
974 #ifndef USE_ROSEN_DRAWING
975         filter.SetImageFilter(RSImageFilter::CreateBlurImageFilter(
976             ringProgressData.shadowBlurSigma, ringProgressData.shadowBlurSigma, RSTileMode::DECAL, nullptr));
977 #else
978         filter.SetImageFilter(RSRecordingImageFilter::CreateBlurImageFilter(
979             ringProgressData.shadowBlurSigma, ringProgressData.shadowBlurSigma, RSTileMode::DECAL, nullptr));
980 #endif
981         brush.SetFilter(filter);
982     }
983 
984     if (LessOrEqual(angle + additionalAngle * INT32_TWO, ANGLE_360)) {
985         PaintWhole(canvas, brush, ringProgressData, gradientColors);
986     } else {
987         PaintBeginHalf(canvas, brush, ringProgressData, gradientColors);
988         PaintEndHalf(canvas, brush, ringProgressData, gradientColors);
989     }
990 }
991 
PaintWhole(RSCanvas & canvas,RSBrush & brush,const RingProgressData & ringProgressData,std::vector<GradientColor> & gradientColors) const992 void ProgressModifier::PaintWhole(RSCanvas& canvas, RSBrush& brush, const RingProgressData& ringProgressData,
993     std::vector<GradientColor>& gradientColors) const
994 {
995     PointF centerPt = ringProgressData.centerPt;
996     auto radius = ringProgressData.radius;
997     auto angle = ringProgressData.angle;
998     auto thickness = ringProgressData.thickness;
999     double halfThickness = thickness / 2;
1000     float additionalAngle = CalcRingProgressAdditionalAngle();
1001     double radAngle = angle / ANGLE_180 * PI_NUM;
1002     double radAdditionalAngle = additionalAngle / ANGLE_180 * PI_NUM;
1003     double sinPointOutside = std::sin(radAngle + radAdditionalAngle) * (radius - halfThickness);
1004     double cosPointOutside = std::cos(radAngle + radAdditionalAngle) * (radius - halfThickness);
1005     double sinPointStart = std::sin(radAdditionalAngle) * radius;
1006     double cosPointStart = std::cos(radAdditionalAngle) * radius;
1007 
1008     std::vector<RSColorQuad> colors;
1009     std::vector<float> pos;
1010     for (size_t i = 0; i < gradientColors.size(); i++) {
1011         colors.emplace_back(gradientColors[i].GetLinearColor().GetValue());
1012         pos.emplace_back(gradientColors[i].GetDimension().Value());
1013     }
1014 
1015     RSPath path;
1016 #ifndef USE_ROSEN_DRAWING
1017     brush.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())),
1018         colors, pos, RSTileMode::CLAMP, 0, angle + additionalAngle * INT32_TWO, nullptr));
1019 #else
1020     brush.SetShaderEffect(
1021         RSRecordingShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1022             RSTileMode::CLAMP, 0, angle + additionalAngle * INT32_TWO, nullptr));
1023 #endif
1024     path.ArcTo(centerPt.GetX() + cosPointStart - halfThickness, centerPt.GetY() + sinPointStart - halfThickness,
1025         centerPt.GetX() + cosPointStart + halfThickness, centerPt.GetY() + sinPointStart + halfThickness,
1026         ANGLE_180 + additionalAngle, ANGLE_180);
1027     path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1028         centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness, additionalAngle, angle);
1029     path.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION, centerPt.GetX() + cosPointOutside,
1030         centerPt.GetY() + sinPointOutside);
1031     path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1032         centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness, additionalAngle + angle,
1033         -angle);
1034     path.Close();
1035     canvas.Save();
1036     canvas.Rotate(-ANGLE_90 - additionalAngle, centerPt.GetX(), centerPt.GetY());
1037     canvas.AttachBrush(brush);
1038     canvas.DrawPath(path);
1039     canvas.DetachBrush();
1040     canvas.Restore();
1041 }
1042 
PaintBeginHalf(RSCanvas & canvas,RSBrush & brush,const RingProgressData & ringProgressData,std::vector<GradientColor> & gradientColors) const1043 void ProgressModifier::PaintBeginHalf(RSCanvas& canvas, RSBrush& brush, const RingProgressData& ringProgressData,
1044     std::vector<GradientColor>& gradientColors) const
1045 {
1046     PointF centerPt = ringProgressData.centerPt;
1047     auto radius = ringProgressData.radius;
1048     auto angle = ringProgressData.angle;
1049     auto thickness = ringProgressData.thickness;
1050     double halfThickness = thickness / 2;
1051     float additionalAngle = CalcRingProgressAdditionalAngle();
1052 
1053     std::vector<RSColorQuad> colors;
1054     std::vector<float> pos;
1055     for (size_t i = 0; i < gradientColors.size(); i++) {
1056         colors.emplace_back(gradientColors[i].GetLinearColor().GetValue());
1057         pos.emplace_back(gradientColors[i].GetDimension().Value());
1058     }
1059     colors.emplace_back(gradientColors[0].GetLinearColor().GetValue());
1060     pos.emplace_back((ANGLE_360 - additionalAngle) / ANGLE_360);
1061 
1062     RSPath beginPath;
1063     RSPath path;
1064 #ifndef USE_ROSEN_DRAWING
1065     brush.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())),
1066         colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1067 #else
1068     brush.SetShaderEffect(RSRecordingShaderEffect::CreateSweepGradient(
1069         ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1070 #endif
1071     beginPath.MoveTo(centerPt.GetX() + radius - halfThickness, centerPt.GetY());
1072     beginPath.ArcTo(centerPt.GetX() + radius - halfThickness, centerPt.GetY() - halfThickness,
1073         centerPt.GetX() + radius + halfThickness, centerPt.GetY() + halfThickness, ANGLE_180, ANGLE_180);
1074     beginPath.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1075         centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness, 0, ANGLE_180);
1076     beginPath.LineTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY());
1077     beginPath.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1078         centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness, ANGLE_180, -ANGLE_180);
1079     beginPath.Close();
1080 
1081     canvas.Save();
1082     canvas.Rotate(-ANGLE_90, centerPt.GetX(), centerPt.GetY());
1083     canvas.AttachBrush(brush);
1084     canvas.DrawPath(beginPath);
1085     canvas.DetachBrush();
1086     canvas.Restore();
1087 }
1088 
PaintEndHalf(RSCanvas & canvas,RSBrush & brush,const RingProgressData & ringProgressData,std::vector<GradientColor> & gradientColors) const1089 void ProgressModifier::PaintEndHalf(RSCanvas& canvas, RSBrush& brush, const RingProgressData& ringProgressData,
1090     std::vector<GradientColor>& gradientColors) const
1091 {
1092     PointF centerPt = ringProgressData.centerPt;
1093     auto radius = ringProgressData.radius;
1094     auto angle = ringProgressData.angle;
1095     auto thickness = ringProgressData.thickness;
1096     double halfThickness = thickness / 2;
1097     float additionalAngle = CalcRingProgressAdditionalAngle();
1098     double radArc = (angle - ANGLE_180) / ANGLE_180 * PI_NUM;
1099     double sinPointOutside = std::sin(radArc) * (radius - halfThickness);
1100     double cosPointOutside = std::cos(radArc) * (radius - halfThickness);
1101 
1102     std::vector<RSColorQuad> colors;
1103     std::vector<float> pos;
1104     if (gradientColors.size() != 0) {
1105         colors.emplace_back(gradientColors[gradientColors.size() - 1].GetLinearColor().GetValue());
1106     }
1107     pos.emplace_back(additionalAngle / ANGLE_360);
1108     for (size_t i = 0; i < gradientColors.size(); i++) {
1109         colors.emplace_back(gradientColors[i].GetLinearColor().GetValue());
1110         pos.emplace_back(gradientColors[i].GetDimension().Value());
1111     }
1112 
1113     RSPath endPath;
1114 #ifndef USE_ROSEN_DRAWING
1115     brush.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
1116         ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1117 #else
1118     brush.SetShaderEffect(RSRecordingShaderEffect::CreateSweepGradient(
1119         ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos, RSTileMode::CLAMP, 0, angle, nullptr));
1120 #endif
1121     endPath.MoveTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY());
1122     endPath.LineTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY());
1123     endPath.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1124         centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness, ANGLE_180,
1125         angle - ANGLE_180);
1126     endPath.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION, centerPt.GetX() - cosPointOutside,
1127         centerPt.GetY() - sinPointOutside);
1128     endPath.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1129         centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness, angle, ANGLE_180 - angle);
1130     endPath.Close();
1131     canvas.Save();
1132     canvas.Rotate(-ANGLE_90, centerPt.GetX(), centerPt.GetY());
1133     canvas.AttachBrush(brush);
1134     canvas.DrawPath(endPath);
1135     canvas.DetachBrush();
1136     canvas.Restore();
1137 }
1138 
SortGradientColorsByOffset(const Gradient & gradient) const1139 Gradient ProgressModifier::SortGradientColorsByOffset(const Gradient& gradient) const
1140 {
1141     auto srcGradientColors = gradient.GetColors();
1142     std::sort(
1143         srcGradientColors.begin(), srcGradientColors.end(), [](const GradientColor& left, const GradientColor& right) {
1144             return left.GetDimension().Value() < right.GetDimension().Value();
1145         });
1146 
1147     Gradient sortedGradient;
1148     for (const auto& item : srcGradientColors) {
1149         sortedGradient.AddColor(item);
1150     }
1151 
1152     return sortedGradient;
1153 }
1154 
PaintRingSweeping(RSCanvas & canvas,const RingProgressData & ringProgressData) const1155 void ProgressModifier::PaintRingSweeping(RSCanvas& canvas, const RingProgressData& ringProgressData) const
1156 {
1157     if (NearZero(value_->Get()) || NearZero(maxValue_->Get())) {
1158         return;
1159     }
1160 
1161     // If the progress reaches 100%, stop sweeping.
1162     if (NearEqual(value_->Get(), maxValue_->Get())) {
1163         return;
1164     }
1165 
1166     auto centerPt = ringProgressData.centerPt;
1167     auto radius = ringProgressData.radius;
1168     auto thickness = ringProgressData.thickness;
1169     double halfThickness = thickness / 2;
1170 
1171     RSPen pen;
1172     pen.SetAntiAlias(true);
1173     pen.SetWidth(thickness);
1174     pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
1175 
1176     std::vector<RSColorQuad> colors;
1177     std::vector<float> pos;
1178     GenerateRingSweepingGradientInfo(colors, pos);
1179 
1180     float date = sweepingDate_->Get();
1181     float additionalAngle = CalcRingProgressAdditionalAngle();
1182     float startSweepAngle = date - ANGLE_45 - additionalAngle;
1183 #ifndef USE_ROSEN_DRAWING
1184     pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
1185         ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1186         RSTileMode::CLAMP, 0, ANGLE_45, nullptr));
1187 
1188     RSPath path;
1189 #else
1190     pen.SetShaderEffect(RSRecordingShaderEffect::CreateSweepGradient(
1191         ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1192         RSTileMode::CLAMP, 0, ANGLE_45, nullptr));
1193 
1194     RSRecordingPath path;
1195 #endif
1196     double totalDegree = ANGLE_360;
1197     double angleArc = (value_->Get() / maxValue_->Get()) * totalDegree;
1198     double radArc = angleArc / totalDegree * 2 * PI_NUM;
1199     double sinPointInside = std::sin(radArc) * (radius - halfThickness);
1200     double cosPointInside = std::cos(radArc) * (radius - halfThickness);
1201     if (LessOrEqual(startSweepAngle, ANGLE_90)) {
1202         path.MoveTo(centerPt.GetX(), centerPt.GetY() - radius + halfThickness);
1203         path.ArcTo(centerPt.GetX() - halfThickness, centerPt.GetY() - radius - halfThickness,
1204             centerPt.GetX() + halfThickness, centerPt.GetY() - radius + halfThickness,
1205             ANGLE_90, ANGLE_180);
1206         if (GreatNotEqual(angleArc + additionalAngle * INT32_TWO, ANGLE_360)) {
1207             path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1208                 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1209                 -ANGLE_90, ANGLE_180);
1210             path.LineTo(centerPt.GetX(), centerPt.GetY() + radius - halfThickness);
1211             path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1212                 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1213                 ANGLE_90, -ANGLE_180);
1214 #ifndef USE_ROSEN_DRAWING
1215             RSPath path2;
1216             RSPath path3;
1217 #else
1218             RSRecordingPath path2;
1219             RSRecordingPath path3;
1220 #endif
1221             float x = centerPt.GetX() + sin(radArc) * radius;
1222             float y = centerPt.GetY() - cos(radArc) * radius;
1223             path2.AddCircle(x, y, halfThickness);
1224             path3.Op(path, path2, RSPathOp::INTERSECT);
1225             path.Op(path, path3, RSPathOp::DIFFERENCE);
1226         } else if (GreatNotEqual(angleArc, ANGLE_180)) {
1227             path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1228                 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1229                 -ANGLE_90, ANGLE_180);
1230             path.LineTo(centerPt.GetX(), centerPt.GetY() + radius - halfThickness);
1231             path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1232                 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1233                 ANGLE_90, -ANGLE_180);
1234         } else {
1235             path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1236                 centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1237                 -ANGLE_90, angleArc);
1238             path.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION,
1239                 centerPt.GetX() + sinPointInside, centerPt.GetY() - cosPointInside);
1240             path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1241                 centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1242                 -ANGLE_90 + angleArc, -angleArc);
1243         }
1244     } else {
1245         path.MoveTo(centerPt.GetX() + radius - halfThickness, centerPt.GetY());
1246         path.LineTo(centerPt.GetX() + radius + halfThickness, centerPt.GetY());
1247         path.ArcTo(centerPt.GetX() - radius - halfThickness, centerPt.GetY() - radius - halfThickness,
1248             centerPt.GetX() + radius + halfThickness, centerPt.GetY() + radius + halfThickness,
1249             0, angleArc - ANGLE_90);
1250         path.ArcTo(halfThickness, halfThickness, 0.0f, RSPathDirection::CW_DIRECTION,
1251             centerPt.GetX() + sinPointInside, centerPt.GetY() - cosPointInside);
1252         path.ArcTo(centerPt.GetX() - radius + halfThickness, centerPt.GetY() - radius + halfThickness,
1253             centerPt.GetX() + radius - halfThickness, centerPt.GetY() + radius - halfThickness,
1254             angleArc - ANGLE_90, -angleArc + ANGLE_90);
1255     }
1256 
1257     canvas.Save();
1258     canvas.ClipPath(path, RSClipOp::INTERSECT, true);
1259 
1260     float rotateAngle = -ANGLE_90 + startSweepAngle;
1261     canvas.Rotate(rotateAngle, centerPt.GetX(), centerPt.GetY());
1262     canvas.AttachPen(pen);
1263     canvas.DrawArc(
1264         { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1265         0, ANGLE_45);
1266     canvas.DetachPen();
1267     canvas.Restore();
1268 }
1269 
GenerateRingSweepingGradientInfo(std::vector<RSColorQuad> & colors,std::vector<float> & pos) const1270 void ProgressModifier::GenerateRingSweepingGradientInfo(
1271     std::vector<RSColorQuad>& colors, std::vector<float>& pos) const
1272 {
1273     Gradient gradient;
1274     Color sweepingColorBase = Color::WHITE;
1275     Color sweepingColorEnd = sweepingColorBase.ChangeOpacity(0.0f);
1276     GradientColor gradientColorEnd;
1277     gradientColorEnd.SetColor(sweepingColorEnd);
1278     gradientColorEnd.SetDimension(Dimension(0.0, DimensionUnit::PERCENT));
1279     gradient.AddColor(gradientColorEnd);
1280 
1281     // The sweep layer is a 45-degree white gradient with an opacity of 0.4 at 35 degrees and 0 at both ends.
1282     Color sweepingColorMiddle = sweepingColorBase.ChangeOpacity(0.4f);
1283     GradientColor gradientColorMiddle;
1284     gradientColorMiddle.SetColor(sweepingColorMiddle);
1285     gradientColorMiddle.SetDimension(Dimension(35.0f / ANGLE_45, DimensionUnit::PERCENT));
1286     gradient.AddColor(gradientColorMiddle);
1287 
1288     Color sweepingColorStart = sweepingColorBase.ChangeOpacity(0.0f);
1289     GradientColor gradientColorStart;
1290     gradientColorStart.SetColor(sweepingColorStart);
1291     gradientColorStart.SetDimension(Dimension(1.0, DimensionUnit::PERCENT));
1292     gradient.AddColor(gradientColorStart);
1293 
1294     auto gradientColors = gradient.GetColors();
1295     for (size_t i = 0; i < gradientColors.size(); i++) {
1296         colors.emplace_back(gradientColors[i].GetColor().GetValue());
1297         pos.emplace_back(gradientColors[i].GetDimension().Value());
1298     }
1299 }
1300 
PaintTrailing(RSCanvas & canvas,const RingProgressData & ringProgressData) const1301 void ProgressModifier::PaintTrailing(RSCanvas& canvas, const RingProgressData& ringProgressData) const
1302 {
1303     PointF centerPt = ringProgressData.centerPt;
1304     double radius = ringProgressData.radius;
1305     double thickness = ringProgressData.thickness;
1306     float head = trailingHeadDate_->Get();
1307     float tail = trailingTailDate_->Get();
1308     float additionalAngle = CalcRingProgressAdditionalAngle();
1309     float rotateAngle = 0.0f;
1310     if (GreatOrEqual(head, ANGLE_360 - additionalAngle * FLOAT_TWO_ZERO)) {
1311         rotateAngle = -ANGLE_360 + head - tail + additionalAngle * FLOAT_TWO_ZERO;
1312     }
1313     head += (additionalAngle + rotateAngle);
1314     tail += (additionalAngle + rotateAngle);
1315 
1316     RSPen pen;
1317     pen.SetAntiAlias(true);
1318     pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
1319     pen.SetWidth(thickness);
1320     canvas.Save();
1321 
1322     std::vector<GradientColor> gradientColors = GetRingProgressGradientColors();
1323     std::vector<RSColorQuad> colors;
1324     std::vector<float> pos;
1325 
1326     for (size_t i = 0; i < gradientColors.size(); i++) {
1327         double dimension = gradientColors[i].GetDimension().Value();
1328         Color color = gradientColors[i].GetLinearColor().BlendOpacity(dimension);
1329         colors.emplace_back(color.GetValue());
1330         pos.emplace_back(gradientColors[i].GetDimension().Value());
1331     }
1332 
1333     RSPath path;
1334 #ifndef USE_ROSEN_DRAWING
1335     pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors,
1336         pos, RSTileMode::CLAMP, tail - additionalAngle, head + additionalAngle, nullptr));
1337 #else
1338     pen.SetShaderEffect(
1339         RSRecordingShaderEffect::CreateSweepGradient(ToRSPoint(PointF(centerPt.GetX(), centerPt.GetY())), colors, pos,
1340             RSTileMode::CLAMP, tail - additionalAngle, head + additionalAngle, nullptr));
1341 #endif
1342     RSRect rRect(
1343         centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius);
1344     path.AddArc(rRect, tail, head - tail);
1345     canvas.Rotate(-ANGLE_90 - rotateAngle - additionalAngle, centerPt.GetX(), centerPt.GetY());
1346     canvas.AttachPen(pen);
1347     canvas.DrawPath(path);
1348     canvas.DetachPen();
1349     canvas.Restore();
1350 }
1351 
PaintScaleRing(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1352 void ProgressModifier::PaintScaleRing(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1353 {
1354     PointF centerPt = PointF(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO) + offset;
1355     double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1356     double lengthOfScale = strokeWidth_->Get();
1357     double pathDistance = FLOAT_TWO_ZERO * M_PI * radius / scaleCount_->Get();
1358     if (scaleWidth_->Get() > pathDistance) {
1359         PaintRing(canvas, offset, contentSize);
1360         return;
1361     }
1362     double widthOfLine = scaleWidth_->Get();
1363     RSPen pen;
1364 #ifndef USE_ROSEN_DRAWING
1365     RSPath path;
1366 #else
1367     RSRecordingPath path;
1368 #endif
1369     pen.SetWidth(widthOfLine);
1370     path.AddRoundRect(
1371         { 0, 0, widthOfLine, lengthOfScale }, widthOfLine / INT32_TWO, widthOfLine / INT32_TWO,
1372         RSPathDirection::CW_DIRECTION);
1373     pen.SetAntiAlias(true);
1374     pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
1375 #ifndef USE_ROSEN_DRAWING
1376     pen.SetPathEffect(RSPathEffect::CreatePathDashEffect(path, pathDistance, 0.0f, RSPathDashStyle::ROTATE));
1377 #else
1378     pen.SetPathEffect(RSRecordingPathEffect::CreatePathDashEffect(path, pathDistance, 0.0f, RSPathDashStyle::ROTATE));
1379 #endif
1380     pen.SetColor(ToRSColor(bgColor_->Get()));
1381     canvas.AttachPen(pen);
1382     if (isRightToLeft_->Get()) {
1383         canvas.Scale(-1, 1);
1384         canvas.Translate(-radius * INT32_TWO, 0);
1385     }
1386     canvas.DrawArc(
1387         { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1388         ANGLE_270, ANGLE_360);
1389     canvas.DetachPen();
1390     // start to draw cur value progress
1391     pen.SetColor(ToRSColor((color_->Get())));
1392     canvas.AttachPen(pen);
1393     double angle = std::min((value_->Get() / maxValue_->Get()) * ANGLE_360, static_cast<float>(ANGLE_360));
1394     canvas.DrawArc(
1395         { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1396         ANGLE_270, angle);
1397     canvas.DetachPen();
1398     canvas.Restore();
1399 }
1400 
PaintMoon(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1401 void ProgressModifier::PaintMoon(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1402 {
1403     static int32_t totalDegree = 1;
1404     PointF centerPt = PointF(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO) + offset;
1405     double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1406     RSBrush brush;
1407     brush.SetAntiAlias(true);
1408     brush.SetAlpha(true);
1409     brush.SetColor(ToRSColor(bgColor_->Get()));
1410     double angle = std::min((value_->Get() / maxValue_->Get()) * totalDegree, static_cast<float>(totalDegree));
1411 #ifndef USE_ROSEN_DRAWING
1412     RSPath path;
1413 #else
1414     RSRecordingPath path;
1415 #endif
1416     canvas.AttachBrush(brush);
1417     canvas.DrawCircle(ToRSPoint(centerPt), radius);
1418     canvas.DetachBrush();
1419     brush.SetColor(ToRSColor((color_->Get())));
1420     canvas.AttachBrush(brush);
1421     path.AddArc(
1422         { centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1423         ANGLE_90, !isRightToLeft_->Get() ? ANGLE_180 : -ANGLE_180);
1424     if (angle <= FLOAT_ZERO_FIVE) {
1425         double progressOffset = radius - radius * angle / FLOAT_ZERO_FIVE;
1426         path.MoveTo(centerPt.GetX(), centerPt.GetY() - radius);
1427         // startAngle:270  sweepAngle:-180
1428         path.AddArc({ centerPt.GetX() - progressOffset, centerPt.GetY() - radius, centerPt.GetX() + progressOffset,
1429                         centerPt.GetY() + radius },
1430             ANGLE_270, !isRightToLeft_->Get() ? -ANGLE_180 : ANGLE_180);
1431         canvas.DrawPath(path);
1432     } else {
1433         double progressOffset = radius * (angle - FLOAT_ZERO_FIVE) / FLOAT_ZERO_FIVE;
1434         path.MoveTo(centerPt.GetX(), centerPt.GetY() - radius);
1435         // startAngle:270  sweepAngle:180
1436         path.AddArc({ centerPt.GetX() - progressOffset, centerPt.GetY() - radius, centerPt.GetX() + progressOffset,
1437                         centerPt.GetY() + radius },
1438             ANGLE_270, !isRightToLeft_->Get() ? ANGLE_180 : -ANGLE_180);
1439         canvas.DrawPath(path);
1440     }
1441     canvas.DetachBrush();
1442 }
1443 
PaintCapsule(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1444 void ProgressModifier::PaintCapsule(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1445 {
1446     auto borderWidth = capsuleBorderWidth_->Get();
1447     if (GreatNotEqual(2 * borderWidth, contentSize.Height())) {
1448         borderWidth = contentSize.Height() / 2;
1449     }
1450     static int32_t totalDegree = 1;
1451     double radiusBig =
1452         std::min((contentSize.Width() - borderWidth) / INT32_TWO, (contentSize.Height() - borderWidth) / INT32_TWO);
1453     double offsetXBig = offset.GetX() + borderWidth / INT32_TWO;
1454     double offsetYBig = offset.GetY() + borderWidth / INT32_TWO;
1455     double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1456     double offsetX = offset.GetX();
1457     double offsetY = offset.GetY();
1458     double progressWidth =
1459         std::min((value_->Get() / maxValue_->Get()) * totalDegree * contentSize.Width(), contentSize.Width());
1460     RSBrush brush;
1461     brush.SetAntiAlias(true);
1462     RSPen pen;
1463     brush.SetAlpha(true);
1464     brush.SetColor(ToRSColor(bgColor_->Get()));
1465     pen.SetWidth(borderWidth);
1466     pen.SetAntiAlias(true);
1467     pen.SetColor(ToRSColor(borderColor_->Get()));
1468 #ifndef USE_ROSEN_DRAWING
1469     RSPath path;
1470 #else
1471     RSRecordingPath path;
1472 #endif
1473     canvas.AttachBrush(brush);
1474     canvas.DrawRoundRect(
1475         { { offsetX, offsetY, contentSize.Width() + offsetX, contentSize.Height() + offsetY }, radius, radius });
1476     canvas.DetachBrush();
1477     canvas.AttachPen(pen);
1478     if (!NearZero(borderWidth)) {
1479         canvas.DrawRoundRect(
1480             { { offsetXBig, offsetYBig, contentSize.Width() - borderWidth + offsetXBig,
1481                 contentSize.Height() - borderWidth + offsetYBig },
1482                 radiusBig, radiusBig });
1483     }
1484     canvas.DetachPen();
1485     brush.SetColor(ToRSColor((color_->Get())));
1486     canvas.AttachBrush(brush);
1487     if (!isRightToLeft_->Get()) {
1488         path.AddArc(
1489             { offsetX, offsetY, INT32_TWO * radius + offsetX, contentSize.Height() + offsetY }, ANGLE_90, ANGLE_180);
1490     } else {
1491         path.AddArc({ offsetX + contentSize.Width() - INT32_TWO * radius, offsetY, offsetX + contentSize.Width(),
1492                         contentSize.Height() + offsetY },
1493             ANGLE_270, ANGLE_180);
1494     }
1495     if (LessNotEqual(progressWidth, radius)) {
1496         if (!isRightToLeft_->Get()) {
1497             // startAngle:270  sweepAngle:-180
1498             path.AddArc({ offsetX + progressWidth, offsetY, INT32_TWO * radius - progressWidth + offsetX,
1499                             contentSize.Height() + offsetY },
1500                 ANGLE_270, -ANGLE_180);
1501         } else {
1502             path.AddArc({ offsetX + contentSize.Width() - INT32_TWO * radius + progressWidth, offsetY,
1503                             offsetX + contentSize.Width() - progressWidth, contentSize.Height() + offsetY },
1504                 ANGLE_90, -ANGLE_180);
1505         }
1506     } else if (GreatNotEqual(progressWidth, contentSize.Width() - radius)) {
1507         path.AddRect(
1508             { offsetX + radius, offsetY, contentSize.Width() + offsetX - radius, contentSize.Height() + offsetY });
1509         if (!isRightToLeft_->Get()) {
1510             // startAngle:270  sweepAngle:180
1511             path.AddArc({ offsetX + (contentSize.Width() - radius) * FLOAT_TWO_ZERO - progressWidth, offsetY,
1512                             offsetX + progressWidth, contentSize.Height() + offsetY },
1513                 ANGLE_270, ANGLE_180);
1514         } else {
1515             path.AddArc({ offsetX + contentSize.Width() - progressWidth, offsetY,
1516                             offsetX - contentSize.Width() + progressWidth + INT32_TWO * radius,
1517                             contentSize.Height() + offsetY },
1518                 ANGLE_90, ANGLE_180);
1519         }
1520     } else {
1521         if (!isRightToLeft_->Get()) {
1522             path.AddRect({ radius + offsetX, offsetY, progressWidth + offsetX, contentSize.Height() + offsetY });
1523         } else {
1524             path.AddRect({ offsetX + contentSize.Width() - progressWidth, offsetY,
1525                 contentSize.Width() + offsetX - radius, contentSize.Height() + offsetY });
1526         }
1527     }
1528     canvas.DrawPath(path);
1529     canvas.DetachBrush();
1530     canvas.Restore();
1531 
1532     PaintCapsuleLightSweep(canvas, contentSize, offset, path, false);
1533 }
1534 
PaintVerticalCapsule(RSCanvas & canvas,const OffsetF & offset,const SizeF & contentSize) const1535 void ProgressModifier::PaintVerticalCapsule(RSCanvas& canvas, const OffsetF& offset, const SizeF& contentSize) const
1536 {
1537     auto borderWidth = capsuleBorderWidth_->Get();
1538     if (GreatNotEqual(2 * borderWidth, contentSize.Width())) {
1539         borderWidth = contentSize.Width() / 2;
1540     }
1541     static int32_t totalDegree = 1;
1542     double radiusBig =
1543         std::min((contentSize.Width() - borderWidth) / INT32_TWO, (contentSize.Height() - borderWidth) / INT32_TWO);
1544     double offsetXBig = offset.GetX() + borderWidth / INT32_TWO;
1545     double offsetYBig = offset.GetY() + borderWidth / INT32_TWO;
1546     double radius = std::min(contentSize.Width() / INT32_TWO, contentSize.Height() / INT32_TWO);
1547     double offsetX = offset.GetX();
1548     double offsetY = offset.GetY();
1549     double progressWidth =
1550         std::min((value_->Get() / maxValue_->Get()) * totalDegree * contentSize.Height(), contentSize.Height());
1551     RSBrush brush;
1552     brush.SetAntiAlias(true);
1553     RSPen pen;
1554     pen.SetWidth(borderWidth);
1555     pen.SetAntiAlias(true);
1556     pen.SetColor(ToRSColor(borderColor_->Get()));
1557     brush.SetAlpha(true);
1558     brush.SetColor(ToRSColor(bgColor_->Get()));
1559 #ifndef USE_ROSEN_DRAWING
1560     RSPath path;
1561 #else
1562     RSRecordingPath path;
1563 #endif
1564     canvas.AttachBrush(brush);
1565     canvas.DrawRoundRect(
1566         { { offsetX, offsetY, contentSize.Width() + offsetX, contentSize.Height() + offsetY }, radius, radius });
1567     canvas.DetachBrush();
1568     canvas.AttachPen(pen);
1569     if (!NearZero(borderWidth)) {
1570         canvas.DrawRoundRect(
1571             { { offsetXBig, offsetYBig, contentSize.Width() - borderWidth + offsetXBig,
1572                 contentSize.Height() - borderWidth + offsetYBig },
1573                 radiusBig, radiusBig });
1574     }
1575     canvas.DetachPen();
1576     brush.SetColor(ToRSColor((color_->Get())));
1577     canvas.AttachBrush(brush);
1578     path.AddArc(
1579         { offsetX, offsetY, contentSize.Width() + offsetX, contentSize.Width() + offsetY }, 0, -ANGLE_180);
1580     if (LessNotEqual(progressWidth, radius)) {
1581         // startAngle:180  sweepAngle:180
1582         path.AddArc(
1583             { offsetX, offsetY + progressWidth, contentSize.Width() + offsetX,
1584                         contentSize.Width() - progressWidth + offsetY },
1585             ANGLE_180, ANGLE_180);
1586     } else if (GreatNotEqual(progressWidth, contentSize.Height() - radius)) {
1587         path.AddRect(
1588             { offsetX, offsetY + radius, contentSize.Width() + offsetX, contentSize.Height() - radius + offsetY });
1589         // startAngle:180  sweepAngle:-180
1590         path.AddArc(
1591             { offsetX, offsetY + (contentSize.Height() - radius) * FLOAT_TWO_ZERO - progressWidth,
1592                         contentSize.Width() + offsetX, progressWidth + offsetY },
1593             ANGLE_180, -ANGLE_180);
1594     } else {
1595         path.AddRect(
1596             { offsetX, radius + offsetY, offsetX + contentSize.Width(), progressWidth + offsetY });
1597     }
1598     canvas.DrawPath(path);
1599     canvas.DetachBrush();
1600     canvas.Restore();
1601 
1602     PaintCapsuleLightSweep(canvas, contentSize, offset, path, true);
1603 }
1604 
PaintCapsuleLightSweep(RSCanvas & canvas,const SizeF & contentSize,const OffsetF & offset,const RSPath & path,bool isVertical) const1605 void ProgressModifier::PaintCapsuleLightSweep(
1606     RSCanvas& canvas, const SizeF& contentSize, const OffsetF& offset, const RSPath& path, bool isVertical) const
1607 {
1608     RSPen pen;
1609     pen.SetAntiAlias(true);
1610     pen.SetWidth(1);
1611     auto gradient = CreateCapsuleGradient();
1612     std::vector<RSColorQuad> colors;
1613     std::vector<float> pos;
1614     RSBrush brush;
1615     auto gradientColors = gradient.GetColors();
1616     for (size_t i = 0; i < gradientColors.size(); i++) {
1617         colors.emplace_back(gradientColors[i].GetColor().GetValue());
1618         pos.emplace_back(gradientColors[i].GetDimension().Value());
1619     }
1620 
1621     float endPos = sweepingDate_->Get();
1622 #ifndef USE_ROSEN_DRAWING
1623     if (isVertical) {
1624         brush.SetShaderEffect(RSShaderEffect::CreateLinearGradient(
1625             ToRSPoint(
1626                 PointF(offset.GetX() + contentSize.Width() / 2, offset.GetY() + endPos - SWEEP_WIDTH.ConvertToPx())),
1627             ToRSPoint(PointF(offset.GetX() + contentSize.Width() / 2, offset.GetY() + endPos)), colors, pos,
1628             RSTileMode::CLAMP));
1629     } else {
1630         brush.SetShaderEffect(RSShaderEffect::CreateLinearGradient(
1631             ToRSPoint(PointF(!isRightToLeft_->Get() ? offset.GetX() + endPos - SWEEP_WIDTH.ConvertToPx()
1632                                                     : offset.GetX() + contentSize.Width() - endPos,
1633                 offset.GetY() + contentSize.Height() / 2)),
1634             ToRSPoint(PointF(!isRightToLeft_->Get()
1635                                  ? offset.GetX() + endPos
1636                                  : offset.GetX() + contentSize.Width() - endPos + SWEEP_WIDTH.ConvertToPx(),
1637                 offset.GetY() + contentSize.Height() / 2)),
1638             colors, pos, RSTileMode::CLAMP));
1639     }
1640 #else
1641     if (isVertical) {
1642         brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
1643             ToRSPoint(PointF(offset.GetX() + contentSize.Width() / 2,
1644                 offset.GetY() + endPos - SWEEP_WIDTH.ConvertToPx() + capsuleBorderWidth_->Get())),
1645             ToRSPoint(
1646                 PointF(offset.GetX() + contentSize.Width() / 2, offset.GetY() + endPos - capsuleBorderWidth_->Get())),
1647             colors, pos, RSTileMode::CLAMP));
1648     } else {
1649         brush.SetShaderEffect(RSRecordingShaderEffect::CreateLinearGradient(
1650             ToRSPoint(PointF(!isRightToLeft_->Get()
1651                                  ? offset.GetX() + endPos - SWEEP_WIDTH.ConvertToPx() + capsuleBorderWidth_->Get()
1652                                  : offset.GetX() + contentSize.Width() - endPos + capsuleBorderWidth_->Get(),
1653                 offset.GetY() + contentSize.Height() / 2)),
1654             ToRSPoint(PointF(!isRightToLeft_->Get() ? offset.GetX() + endPos - capsuleBorderWidth_->Get()
1655                                                     : offset.GetX() + contentSize.Width() - endPos +
1656                                                           SWEEP_WIDTH.ConvertToPx() - capsuleBorderWidth_->Get(),
1657                 offset.GetY() + contentSize.Height() / 2)),
1658             colors, pos, RSTileMode::CLAMP));
1659     }
1660 #endif
1661 
1662     auto offsetX = offset.GetX();
1663     auto offsetY = offset.GetY();
1664     canvas.Save();
1665     canvas.ClipPath(path, RSClipOp::INTERSECT, true);
1666     canvas.AttachBrush(brush);
1667     if (isVertical) {
1668         canvas.DrawRect(
1669             { offsetX, offsetY + endPos - SWEEP_WIDTH.ConvertToPx(),
1670             offsetX + contentSize.Width(), offsetY + endPos });
1671     } else {
1672         if (!isRightToLeft_->Get()) {
1673             canvas.DrawRect({ offsetX + endPos - SWEEP_WIDTH.ConvertToPx(), offsetY, offsetX + endPos,
1674                 offsetY + contentSize.Height() });
1675         } else {
1676             canvas.DrawRect({ offsetX + contentSize.Width() - endPos, offsetY,
1677                 offsetX + contentSize.Width() - endPos + SWEEP_WIDTH.ConvertToPx(), offsetY + contentSize.Height() });
1678         }
1679     }
1680     canvas.DetachBrush();
1681     canvas.Restore();
1682 }
1683 
CreateCapsuleGradient() const1684 Gradient ProgressModifier::CreateCapsuleGradient() const
1685 {
1686     Gradient gradient;
1687     Color lightSweepColorBase = Color::WHITE;
1688     Color lightSweepColorEnd = lightSweepColorBase.ChangeOpacity(0.0);
1689     Color lightSweepColorMiddle = lightSweepColorBase.ChangeOpacity(0.2);
1690     GradientColor gradientColorEnd;
1691     isRightToLeft_->Get() ? gradientColorEnd.SetColor(lightSweepColorMiddle)
1692                           : gradientColorEnd.SetColor(lightSweepColorEnd);
1693     gradientColorEnd.SetDimension(Dimension(0.0, DimensionUnit::VP));
1694     gradient.AddColor(gradientColorEnd);
1695 
1696     GradientColor gradientColorMiddle;
1697     isRightToLeft_->Get() ? gradientColorMiddle.SetColor(lightSweepColorEnd)
1698                           : gradientColorMiddle.SetColor(lightSweepColorMiddle);
1699     gradientColorMiddle.SetDimension(SWEEP_WIDTH);
1700     gradient.AddColor(gradientColorMiddle);
1701     return gradient;
1702 }
1703 
PostTask(const TaskExecutor::Task & task,const std::string & name)1704 bool ProgressModifier::PostTask(const TaskExecutor::Task& task, const std::string& name)
1705 {
1706     auto pipeline = PipelineBase::GetCurrentContext();
1707     CHECK_NULL_RETURN(pipeline, false);
1708     auto taskExecutor = pipeline->GetTaskExecutor();
1709     CHECK_NULL_RETURN(taskExecutor, false);
1710     return taskExecutor->PostTask(task, TaskExecutor::TaskType::UI, name);
1711 }
1712 
SetStrokeRadius(float strokeRaidus)1713 void ProgressModifier::SetStrokeRadius(float strokeRaidus)
1714 {
1715     strokeRadius_->Set(strokeRaidus);
1716 }
1717 
SetIsRightToLeft(bool value)1718 void ProgressModifier::SetIsRightToLeft(bool value)
1719 {
1720     if (isRightToLeft_->Get() == value) {
1721         return;
1722     }
1723     isRightToLeft_->Set(value);
1724 }
1725 
PaintScaleRingForApiNine(RSCanvas & canvas,const OffsetF & offset,const SizeF & frameSize) const1726 void ProgressModifier::PaintScaleRingForApiNine(RSCanvas& canvas, const OffsetF& offset, const SizeF& frameSize) const
1727 {
1728     auto scaleWidth = scaleWidth_->Get();
1729     PointF centerPt = PointF(frameSize.Width() / 2, frameSize.Height() / 2) + offset;
1730     double radius = std::min(frameSize.Width() / 2, frameSize.Height() / 2);
1731     double lengthOfScale = strokeWidth_->Get();
1732     if (lengthOfScale > radius) {
1733         lengthOfScale = radius / 2;
1734     }
1735     double pathDistance = 2.0 * M_PI * radius / scaleCount_->Get();
1736     if (scaleWidth > pathDistance) {
1737         PaintRing(canvas, offset, frameSize);
1738         return;
1739     }
1740     double widthOfLine = scaleWidth;
1741     RSPen pen;
1742     RSPath path;
1743     pen.SetWidth(widthOfLine);
1744     path.AddRoundRect({
1745         0, 0, widthOfLine, lengthOfScale }, widthOfLine / 2, widthOfLine / 2, RSPathDirection::CW_DIRECTION);
1746     pen.SetAntiAlias(true);
1747     pen.SetCapStyle(ToRSCapStyle(LineCap::ROUND));
1748     pen.SetPathEffect(RSPathEffect::CreatePathDashEffect(path, pathDistance, 0.0f, RSPathDashStyle::ROTATE));
1749     pen.SetColor(ToRSColor(bgColor_->Get()));
1750     canvas.AttachPen(pen);
1751     canvas.DrawArc({
1752         centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1753         ANGLE_270, ANGLE_360);
1754     canvas.DetachPen();
1755     pen.SetColor(ToRSColor((color_->Get())));
1756     canvas.AttachPen(pen);
1757     double angle = (value_->Get() / maxValue_->Get()) * ANGLE_360;
1758     canvas.DrawArc({
1759         centerPt.GetX() - radius, centerPt.GetY() - radius, centerPt.GetX() + radius, centerPt.GetY() + radius },
1760         ANGLE_270, angle);
1761     canvas.DetachPen();
1762 }
1763 
PaintCapsuleForApiNine(RSCanvas & canvas,const OffsetF & offset,const SizeF & frameSize) const1764 void ProgressModifier::PaintCapsuleForApiNine(RSCanvas& canvas, const OffsetF& offset, const SizeF& frameSize) const
1765 {
1766     double radius = std::min(frameSize.Width() / 2, frameSize.Height() / 2);
1767     double offsetX = offset.GetX();
1768     double offsetY = offset.GetY();
1769     double progressWidth = (value_->Get() / maxValue_->Get()) * frameSize.Width();
1770     RSBrush brush;
1771     brush.SetAlpha(true);
1772     brush.SetColor(ToRSColor(bgColor_->Get()));
1773     RSPath path;
1774     canvas.AttachBrush(brush);
1775     canvas.DrawRoundRect({ { offsetX, offsetY, frameSize.Width() + offsetX, frameSize.Height() + offsetY },
1776         radius, radius });
1777     canvas.DetachBrush();
1778     brush.SetColor(ToRSColor(color_->Get()));
1779     canvas.AttachBrush(brush);
1780     path.AddArc({ offsetX, offsetY, 2 * radius + offsetX, frameSize.Height() + offsetY }, ANGLE_90, ANGLE_180);
1781     if (LessNotEqual(progressWidth, radius)) {
1782         // startAngle:270  sweepAngle:-180
1783         path.AddArc({
1784             offsetX + progressWidth, offsetY, 2 * radius - progressWidth + offsetX, frameSize.Height() + offsetY },
1785             ANGLE_270, -ANGLE_180);
1786     } else if (GreatNotEqual(progressWidth, frameSize.Width() - radius)) {
1787         path.AddRect({ offsetX + radius, offsetY, frameSize.Width() + offsetX - radius,
1788             frameSize.Height() + offsetY });
1789         // startAngle:270  sweepAngle:180
1790         path.AddArc({ offsetX + (frameSize.Width() - radius) * 2.0 - progressWidth, offsetY, offsetX + progressWidth,
1791             frameSize.Height() + offsetY }, ANGLE_270, ANGLE_180);
1792     } else {
1793         path.AddRect({ radius + offsetX, offsetY, progressWidth + offsetX, frameSize.Height() + offsetY });
1794     }
1795     canvas.DrawPath(path);
1796     canvas.DetachBrush();
1797 }
1798 
PaintVerticalCapsuleForApiNine(RSCanvas & canvas,const OffsetF & offset,const SizeF & frameSize) const1799 void ProgressModifier::PaintVerticalCapsuleForApiNine(
1800     RSCanvas& canvas, const OffsetF& offset, const SizeF& frameSize) const
1801 {
1802     double radius = std::min(frameSize.Width() / 2, frameSize.Height() / 2);
1803     double offsetX = offset.GetX();
1804     double offsetY = offset.GetY();
1805     double progressWidth = (value_->Get() / maxValue_->Get()) * frameSize.Height();
1806     RSBrush brush;
1807     brush.SetAlpha(true);
1808     brush.SetColor(ToRSColor(bgColor_->Get()));
1809     RSPath path;
1810     canvas.AttachBrush(brush);
1811     canvas.DrawRoundRect({ {
1812         offsetX, offsetY, frameSize.Width() + offsetX, frameSize.Height() + offsetY }, radius, radius });
1813     canvas.DetachBrush();
1814     brush.SetColor(ToRSColor((color_->Get())));
1815     canvas.AttachBrush(brush);
1816     path.AddArc({ offsetX, offsetY, frameSize.Width() + offsetX, frameSize.Width() + offsetY }, 0, -ANGLE_180);
1817     if (LessNotEqual(progressWidth, radius)) {
1818         // startAngle:180  sweepAngle:180
1819         path.AddArc({ offsetX, offsetY + progressWidth, frameSize.Width() + offsetX,
1820                         frameSize.Width() - progressWidth + offsetY },
1821             ANGLE_180, ANGLE_180);
1822     } else if (GreatNotEqual(progressWidth, frameSize.Height() - radius)) {
1823         path.AddRect({
1824             offsetX, offsetY + radius, frameSize.Width() + offsetX, frameSize.Height() - radius + offsetY });
1825         // startAngle:180  sweepAngle:-180
1826         path.AddArc({ offsetX, offsetY + (frameSize.Height() - radius) * 2.0 - progressWidth,
1827             frameSize.Width() + offsetX, progressWidth + offsetY }, ANGLE_180, -ANGLE_180);
1828     } else {
1829         path.AddRect({ offsetX, radius + offsetY, offsetX + frameSize.Width(), progressWidth + offsetY });
1830     }
1831     canvas.DrawPath(path);
1832     canvas.DetachBrush();
1833 }
1834 } // namespace OHOS::Ace::NG
1835