1 /*
2  * Copyright (c) 2020-2021 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 "components/ui_box_progress.h"
17 #include "draw/draw_utils.h"
18 #include "engines/gfx/gfx_engine_manager.h"
19 #include "gfx_utils/graphic_log.h"
20 
21 namespace OHOS {
UIBoxProgress()22 UIBoxProgress::UIBoxProgress()
23     : progressWidth_(0), progressHeight_(0), isValidWidthSet_(false), isValidHeightSet_(false)
24 {
25     SetDirection(Direction::DIR_LEFT_TO_RIGHT);
26 }
27 
DrawValidRect(BufferInfo & gfxDstBuffer,const Image * image,const Rect & rect,const Rect & invalidatedArea,const Style & style,uint16_t radius)28 void UIBoxProgress::DrawValidRect(BufferInfo& gfxDstBuffer,
29                                   const Image* image,
30                                   const Rect& rect,
31                                   const Rect& invalidatedArea,
32                                   const Style& style,
33                                   uint16_t radius)
34 {
35     Rect cordsTmp;
36     if ((image != nullptr) && (image->GetSrcType() != IMG_SRC_UNKNOWN)) {
37         ImageHeader header = {0};
38         image->GetHeader(header);
39 
40         Rect area(rect);
41         switch (direction_) {
42             case Direction::DIR_LEFT_TO_RIGHT:
43                 cordsTmp.SetPosition(area.GetLeft() - radius, area.GetTop());
44                 break;
45             case Direction::DIR_TOP_TO_BOTTOM:
46                 cordsTmp.SetPosition(area.GetLeft(), area.GetTop() - radius);
47                 break;
48             case Direction::DIR_RIGHT_TO_LEFT:
49                 cordsTmp.SetPosition(area.GetRight() + radius - header.width, area.GetTop());
50                 break;
51             case Direction::DIR_BOTTOM_TO_TOP:
52                 cordsTmp.SetPosition(area.GetLeft(), area.GetBottom() + radius - header.height);
53                 break;
54             default:
55                 GRAPHIC_LOGE("UIBoxProgress: DrawValidRect direction Err!\n");
56                 break;
57         }
58         cordsTmp.SetHeight(header.height);
59         cordsTmp.SetWidth(header.width);
60         if (area.Intersect(area, invalidatedArea)) {
61             image->DrawImage(gfxDstBuffer, cordsTmp, area, style, opaScale_);
62         }
63     } else {
64         BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, rect, invalidatedArea, style, opaScale_);
65     }
66 
67     if (style.lineCap_ == CapType::CAP_ROUND) {
68         DrawRoundCap(gfxDstBuffer, image, {cordsTmp.GetX(), cordsTmp.GetY()}, rect, invalidatedArea, radius, style);
69     }
70 }
71 
DrawRoundCap(BufferInfo & gfxDstBuffer,const Image * image,const Point & imgPos,const Rect & rect,const Rect & invalidatedArea,uint16_t radius,const Style & style)72 void UIBoxProgress::DrawRoundCap(BufferInfo& gfxDstBuffer,
73                                  const Image* image,
74                                  const Point& imgPos,
75                                  const Rect& rect,
76                                  const Rect& invalidatedArea,
77                                  uint16_t radius,
78                                  const Style& style)
79 {
80     Point leftTop;
81     Point leftBottom;
82     Point rightTop;
83     Point rightBottom;
84 
85     switch (direction_) {
86         case Direction::DIR_LEFT_TO_RIGHT:
87         case Direction::DIR_RIGHT_TO_LEFT: {
88             leftTop.x = rect.GetLeft() - 1;
89             leftTop.y = rect.GetTop() + radius - 1;
90             leftBottom.x = leftTop.x;
91             leftBottom.y = rect.GetBottom() - radius + 1;
92             rightTop.x = rect.GetRight() + 1;
93             rightTop.y = leftTop.y;
94             rightBottom.x = rightTop.x;
95             rightBottom.y = leftBottom.y;
96             break;
97         }
98 
99         case Direction::DIR_TOP_TO_BOTTOM:
100         case Direction::DIR_BOTTOM_TO_TOP: {
101             leftTop.x = rect.GetLeft() + radius - 1;
102             leftTop.y = rect.GetTop() - 1;
103             rightTop.x = rect.GetRight() - radius + 1;
104             rightTop.y = leftTop.y;
105             leftBottom.x = leftTop.x;
106             leftBottom.y = rect.GetBottom() + 1;
107             rightBottom.x = rightTop.x;
108             rightBottom.y = leftBottom.y;
109             break;
110         }
111         default:
112             GRAPHIC_LOGE("UIBoxProgress: DrawRoundCap direction Err!\n");
113             break;
114     }
115 
116     Style capStyle = style;
117     capStyle.lineWidth_ = radius;
118     capStyle.lineColor_ = style.bgColor_;
119     if ((image != nullptr) && (image->GetSrcType() != IMG_SRC_UNKNOWN)) {
120         capStyle.lineOpa_ = style.imageOpa_;
121     } else {
122         capStyle.lineOpa_ = style.bgOpa_;
123     }
124 
125     ArcInfo arcInfo = {{0}};
126     arcInfo.radius = radius;
127     arcInfo.imgPos = imgPos;
128     arcInfo.imgSrc = image;
129 
130     bool isEvenLen = false;
131     if (direction_ == Direction::DIR_LEFT_TO_RIGHT || direction_ == Direction::DIR_RIGHT_TO_LEFT) {
132         if (rect.GetHeight() % 2 == 0) { // 2: determine the odd or even number of the height
133             isEvenLen = true;
134         }
135     } else if (rect.GetWidth() % 2 == 0) { // 2: determine the odd or even number of the width
136         isEvenLen = true;
137     }
138     BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
139     if (isEvenLen) {
140         arcInfo.center = leftTop;
141         arcInfo.startAngle = THREE_QUARTER_IN_DEGREE;
142         arcInfo.endAngle = 0;
143         baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
144                                CapType::CAP_NONE);
145 
146         arcInfo.center = leftBottom;
147         arcInfo.startAngle = SEMICIRCLE_IN_DEGREE;
148         arcInfo.endAngle = THREE_QUARTER_IN_DEGREE;
149         baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
150                                CapType::CAP_NONE);
151 
152         arcInfo.center = rightTop;
153         arcInfo.startAngle = 0;
154         arcInfo.endAngle = QUARTER_IN_DEGREE;
155         baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
156                                CapType::CAP_NONE);
157 
158         arcInfo.center = rightBottom;
159         arcInfo.startAngle = QUARTER_IN_DEGREE;
160         arcInfo.endAngle = SEMICIRCLE_IN_DEGREE;
161         baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
162                                CapType::CAP_NONE);
163     } else {
164         switch (direction_) {
165             case Direction::DIR_LEFT_TO_RIGHT:
166             case Direction::DIR_RIGHT_TO_LEFT: {
167                 arcInfo.center = leftTop;
168                 arcInfo.startAngle = SEMICIRCLE_IN_DEGREE;
169                 arcInfo.endAngle = 0;
170                 baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
171                                        CapType::CAP_NONE);
172 
173                 arcInfo.center = rightTop;
174                 arcInfo.startAngle = 0;
175                 arcInfo.endAngle = SEMICIRCLE_IN_DEGREE;
176                 baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
177                                        CapType::CAP_NONE);
178                 break;
179             }
180 
181             case Direction::DIR_TOP_TO_BOTTOM:
182             case Direction::DIR_BOTTOM_TO_TOP: {
183                 arcInfo.center = leftTop;
184                 arcInfo.startAngle = THREE_QUARTER_IN_DEGREE;
185                 arcInfo.endAngle = QUARTER_IN_DEGREE;
186                 baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
187                                        CapType::CAP_NONE);
188 
189                 arcInfo.center = leftBottom;
190                 arcInfo.startAngle = QUARTER_IN_DEGREE;
191                 arcInfo.endAngle = THREE_QUARTER_IN_DEGREE;
192                 baseGfxEngine->DrawArc(gfxDstBuffer, arcInfo, invalidatedArea, capStyle, opaScale_,
193                                        CapType::CAP_NONE);
194                 break;
195             }
196             default:
197                 GRAPHIC_LOGE("UIBoxProgress: DrawRoundCap direction Err!\n");
198                 break;
199         }
200     }
201 }
202 
GetBackgroundParam(Point & startPoint,int16_t & width,int16_t & height,uint16_t & radius,const Style & style)203 void UIBoxProgress::GetBackgroundParam(Point& startPoint,
204                                        int16_t& width,
205                                        int16_t& height,
206                                        uint16_t& radius,
207                                        const Style& style)
208 {
209     Rect rect = GetOrigRect();
210     // 2: Half of the gap
211     startPoint.x = rect.GetLeft() + style_->borderWidth_ + style_->paddingLeft_ + (GetWidth() - progressWidth_) / 2;
212     // 2: Half of the gap
213     startPoint.y = rect.GetTop() + style_->borderWidth_ + style_->paddingTop_ + (GetHeight() - progressHeight_) / 2;
214 
215     radius = 0;
216     width = progressWidth_;
217     height = progressHeight_;
218     if (style.lineCap_ == CapType::CAP_ROUND) {
219         switch (direction_) {
220             case Direction::DIR_LEFT_TO_RIGHT:
221             case Direction::DIR_RIGHT_TO_LEFT:
222                 radius = (progressHeight_ + 1) >> 1;
223                 width -= radius << 1;
224                 startPoint.x += radius;
225                 break;
226             case Direction::DIR_TOP_TO_BOTTOM:
227             case Direction::DIR_BOTTOM_TO_TOP:
228                 radius = (progressWidth_ + 1) >> 1;
229                 height -= radius << 1;
230                 startPoint.y += radius;
231                 break;
232             default:
233                 GRAPHIC_LOGE("UIBoxProgress: GetBackgroundParam direction Err!\n");
234                 return;
235         }
236     }
237 }
238 
DrawBackground(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)239 void UIBoxProgress::DrawBackground(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
240 {
241     Point startPoint;
242     int16_t progressWidth;
243     int16_t progressHeight;
244     uint16_t radius;
245     GetBackgroundParam(startPoint, progressWidth, progressHeight, radius, *backgroundStyle_);
246 
247     Rect coords(startPoint.x, startPoint.y, startPoint.x + progressWidth - 1, startPoint.y + progressHeight - 1);
248 
249     DrawValidRect(gfxDstBuffer, backgroundImage_, coords, invalidatedArea, *backgroundStyle_, radius);
250 }
251 
DrawForeground(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,Rect & coords)252 void UIBoxProgress::DrawForeground(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, Rect& coords)
253 {
254     Point startPoint;
255     int16_t progressWidth;
256     int16_t progressHeight;
257     uint16_t radius;
258     GetBackgroundParam(startPoint, progressWidth, progressHeight, radius, *foregroundStyle_);
259     int16_t length;
260 
261     switch (direction_) {
262         case Direction::DIR_LEFT_TO_RIGHT: {
263             length = GetCurrentPos(progressWidth - 1);
264             coords.SetRect(startPoint.x, startPoint.y, startPoint.x + length, startPoint.y + progressHeight - 1);
265             break;
266         }
267         case Direction::DIR_RIGHT_TO_LEFT: {
268             length = GetCurrentPos(progressWidth - 1);
269             coords.SetRect(startPoint.x + progressWidth - 1 - length,
270                 startPoint.y, startPoint.x + progressWidth - 1, startPoint.y + progressHeight - 1);
271             break;
272         }
273         case Direction::DIR_TOP_TO_BOTTOM: {
274             length = GetCurrentPos(progressHeight - 1);
275             coords.SetRect(startPoint.x, startPoint.y, startPoint.x + progressWidth - 1, startPoint.y + length);
276             break;
277         }
278         case Direction::DIR_BOTTOM_TO_TOP: {
279             length = GetCurrentPos(progressHeight - 1);
280             coords.SetRect(startPoint.x, startPoint.y + progressHeight - 1 - length,
281                 startPoint.x + progressWidth - 1, startPoint.y + progressHeight - 1);
282             break;
283         }
284         default: {
285             GRAPHIC_LOGE("UIBoxProgress: DrawForeground direction Err!\n");
286             return;
287         }
288     }
289 
290     DrawValidRect(gfxDstBuffer, foregroundImage_, coords, invalidatedArea, *foregroundStyle_, radius);
291 }
292 
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)293 void UIBoxProgress::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
294 {
295     UIView::OnDraw(gfxDstBuffer, invalidatedArea);
296     if (enableBackground_) {
297         DrawBackground(gfxDstBuffer, invalidatedArea);
298     }
299 
300     if ((lastValue_ - rangeMin_ != 0) || (foregroundStyle_->lineCap_ == CapType::CAP_ROUND)) {
301         Rect coords;
302         DrawForeground(gfxDstBuffer, invalidatedArea, coords);
303     }
304 }
305 } // namespace OHOS
306