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/rating/rating_modifier.h"
17
18 #include "base/geometry/ng/offset_t.h"
19 #include "core/components/rating/rating_theme.h"
20 #include "core/components_ng/render/drawing_prop_convertor.h"
21 #include "core/components_ng/render/image_painter.h"
22
23 namespace OHOS::Ace::NG {
RatingModifier()24 RatingModifier::RatingModifier()
25 : needDraw_(AceType::MakeRefPtr<PropertyBool>(false)), starNum_(AceType::MakeRefPtr<PropertyInt>(0)),
26 touchStar_(AceType::MakeRefPtr<PropertyInt>(0)), drawScore_(AceType::MakeRefPtr<PropertyFloat>(.0f)),
27 stepSize_(AceType::MakeRefPtr<PropertyFloat>(.0f)),
28 useContentModifier_(AceType::MakeRefPtr<PropertyBool>(false)),
29 contentOffset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
30 contentSize_(AceType::MakeRefPtr<PropertySizeF>(SizeF())),
31 boardColor_(AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color::TRANSPARENT))),
32 reverse_(AceType::MakeRefPtr<PropertyBool>(false))
33 {
34 AttachProperty(needDraw_);
35 AttachProperty(starNum_);
36 AttachProperty(touchStar_);
37 AttachProperty(drawScore_);
38 AttachProperty(stepSize_);
39 AttachProperty(useContentModifier_);
40 AttachProperty(contentOffset_);
41 AttachProperty(contentSize_);
42 AttachProperty(boardColor_);
43 AttachProperty(reverse_);
44 }
45
onDraw(DrawingContext & context)46 void RatingModifier::onDraw(DrawingContext& context)
47 {
48 if (useContentModifier_->Get()) {
49 return;
50 }
51 CHECK_NULL_VOID(foregroundImageCanvas_);
52 CHECK_NULL_VOID(secondaryImageCanvas_);
53 CHECK_NULL_VOID(backgroundImageCanvas_);
54 // step1: check if touch down any stars.
55 PaintBoard(context);
56 if (reverse_ && reverse_->Get()) {
57 PaintReverseStar(context);
58 } else {
59 PaintStar(context);
60 }
61 SetNeedDraw(false);
62 }
63
PaintBoard(DrawingContext & context)64 void RatingModifier::PaintBoard(DrawingContext& context)
65 {
66 auto pipeline = PipelineBase::GetCurrentContext();
67 CHECK_NULL_VOID(pipeline);
68 auto ratingTheme = pipeline->GetTheme<RatingTheme>();
69 CHECK_NULL_VOID(ratingTheme);
70 // animate color
71 LinearColor bgColor = boardColor_->Get();
72 auto pressBorderRadius = ratingTheme->GetFocusBorderRadius();
73 auto& canvas = context.canvas;
74 auto starNum = starNum_->Get();
75 CHECK_EQUAL_VOID(starNum, 0);
76 auto singleStarImagePaintConfig = foregroundImageCanvas_->GetPaintConfig();
77 const float singleStarWidth = contentSize_->Get().Width() / static_cast<float>(starNum);
78 const float singleStarHeight = contentSize_->Get().Height();
79 auto offset = contentOffset_->Get();
80 auto touchStar = touchStar_->Get();
81 if (touchStar >= 0 && touchStar < starNum) {
82 RSBrush rsBrush(ToRSColor(bgColor));
83 rsBrush.SetAntiAlias(true);
84 const RSRect rsRect(offset.GetX() + singleStarWidth * static_cast<float>(touchStar), offset.GetY(),
85 offset.GetX() + singleStarWidth * static_cast<float>((touchStar + 1)), offset.GetY() + singleStarHeight);
86 const RSRoundRect rsRoundRect(rsRect, static_cast<float>(pressBorderRadius.ConvertToPx()),
87 static_cast<float>(pressBorderRadius.ConvertToPx()));
88 canvas.AttachBrush(rsBrush);
89 canvas.DrawRoundRect(rsRoundRect);
90 canvas.DetachBrush();
91 }
92 }
93
PaintStar(DrawingContext & context)94 void RatingModifier::PaintStar(DrawingContext& context)
95 {
96 const ImagePainter foregroundImagePainter(foregroundImageCanvas_);
97 const ImagePainter secondaryImagePainter(secondaryImageCanvas_);
98 const ImagePainter backgroundPainter(backgroundImageCanvas_);
99
100 auto& canvas = context.canvas;
101 auto offset = contentOffset_->Get();
102 auto starNum = starNum_->Get();
103 CHECK_EQUAL_VOID(starNum, 0);
104 auto drawScore = drawScore_->Get();
105 auto config = foregroundImageCanvas_->GetPaintConfig();
106 const float singleStarWidth = contentSize_->Get().Width() / static_cast<float>(starNum);
107 const float singleStarHeight = contentSize_->Get().Height();
108 // step2: calculate 3 images repeat times.
109 const int32_t foregroundImageRepeatNum = ceil(drawScore);
110 const float secondaryImageRepeatNum = foregroundImageRepeatNum - drawScore;
111 const int32_t backgroundImageRepeatNum = starNum - foregroundImageRepeatNum;
112 // step3: draw the foreground images.
113 canvas.Save();
114 auto offsetTemp = offset;
115 auto contentSize = SizeF(singleStarWidth, singleStarHeight);
116 // step2.1: calculate the clip area in order to display the secondary image.
117 auto clipRect1 = RSRect(offset.GetX(), offsetTemp.GetY(),
118 static_cast<float>(offset.GetX() + singleStarWidth * drawScore), offset.GetY() + singleStarHeight);
119 canvas.ClipRect(clipRect1, RSClipOp::INTERSECT);
120 for (int32_t i = 0; i < foregroundImageRepeatNum; i++) {
121 foregroundImagePainter.DrawImage(canvas, offsetTemp, contentSize);
122 offsetTemp.SetX(static_cast<float>(offsetTemp.GetX() + singleStarWidth));
123 }
124 canvas.Restore();
125
126 // step3: if drawScore is a decimal, it needs to draw the secondary image.
127 if (secondaryImageRepeatNum != 0) {
128 canvas.Save();
129 auto clipRect2 = RSRect(static_cast<float>(offset.GetX() + singleStarWidth * drawScore), offsetTemp.GetY(),
130 static_cast<float>(offset.GetX() + singleStarWidth * static_cast<float>(foregroundImageRepeatNum)),
131 offset.GetY() + singleStarHeight);
132 // step3.1: calculate the clip area which already occupied by the foreground image.
133 canvas.ClipRect(clipRect2, RSClipOp::INTERSECT);
134 offsetTemp.SetX(static_cast<float>(offsetTemp.GetX() - singleStarWidth));
135 secondaryImagePainter.DrawImage(canvas, offsetTemp, contentSize);
136 offsetTemp.SetX(offsetTemp.GetX() + singleStarWidth);
137 canvas.Restore();
138 }
139
140 // step4: draw background image.
141 for (int32_t i = 0; i < backgroundImageRepeatNum; i++) {
142 backgroundPainter.DrawImage(canvas, offsetTemp, contentSize);
143 if (i < backgroundImageRepeatNum - 1) {
144 offsetTemp.SetX(offsetTemp.GetX() + singleStarWidth);
145 }
146 }
147 }
148
PaintReverseStar(DrawingContext & context)149 void RatingModifier::PaintReverseStar(DrawingContext& context)
150 {
151 const ImagePainter foregroundImagePainter(foregroundImageCanvas_);
152 const ImagePainter secondaryImagePainter(secondaryImageCanvas_);
153 const ImagePainter backgroundPainter(backgroundImageCanvas_);
154
155 auto& canvas = context.canvas;
156 auto offset = contentOffset_->Get();
157 auto starNum = starNum_->Get();
158 CHECK_EQUAL_VOID(starNum, 0);
159 auto drawScore = drawScore_->Get();
160 auto config = foregroundImageCanvas_->GetPaintConfig();
161 const float singleStarWidth = contentSize_->Get().Width() / static_cast<float>(starNum);
162 const float singleStarHeight = contentSize_->Get().Height();
163 const int32_t foregroundImageRepeatNum = ceil(drawScore);
164 const float secondaryImageRepeatNum = foregroundImageRepeatNum - drawScore;
165 const int32_t backgroundImageRepeatNum = starNum - foregroundImageRepeatNum;
166 canvas.Save();
167 auto offsetTemp = offset;
168 auto contentSize = SizeF(singleStarWidth, singleStarHeight);
169 for (int32_t i = 0; i < backgroundImageRepeatNum; i++) {
170 backgroundPainter.DrawImage(canvas, offsetTemp, contentSize);
171 offsetTemp.SetX(offsetTemp.GetX() + singleStarWidth);
172 }
173 if (secondaryImageRepeatNum != 0) {
174 canvas.Save();
175 auto clipRect2 =
176 RSRect(static_cast<float>(offset.GetX() + singleStarWidth * backgroundImageRepeatNum), offsetTemp.GetY(),
177 static_cast<float>(offset.GetX() + singleStarWidth * (static_cast<float>(starNum) - drawScore)),
178 offset.GetY() + singleStarHeight);
179 canvas.ClipRect(clipRect2, RSClipOp::INTERSECT);
180 secondaryImagePainter.DrawImage(canvas, offsetTemp, contentSize);
181 canvas.Restore();
182 }
183 auto clipRect1 =
184 RSRect(static_cast<float>(offset.GetX() + singleStarWidth * (static_cast<float>(starNum) - drawScore)),
185 offsetTemp.GetY(), offset.GetX() + singleStarWidth * starNum, offset.GetY() + singleStarHeight);
186 canvas.ClipRect(clipRect1, RSClipOp::INTERSECT);
187 for (int32_t i = 0; i < foregroundImageRepeatNum; i++) {
188 foregroundImagePainter.DrawImage(canvas, offsetTemp, contentSize);
189 offsetTemp.SetX(static_cast<float>(offsetTemp.GetX() + singleStarWidth));
190 }
191 canvas.Restore();
192 }
193 } // namespace OHOS::Ace::NG
194