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