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