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