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
21namespace OHOS {
22UIBoxProgress::UIBoxProgress()
23    : progressWidth_(0), progressHeight_(0), isValidWidthSet_(false), isValidHeightSet_(false)
24{
25    SetDirection(Direction::DIR_LEFT_TO_RIGHT);
26}
27
28void 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
72void 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
203void 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
239void 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
252void 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
293void 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