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_analog_clock.h"
17 
18 #include "components/ui_image_view.h"
19 #include "draw/draw_image.h"
20 #include "engines/gfx/gfx_engine_manager.h"
21 #include "gfx_utils/graphic_log.h"
22 #include "gfx_utils/style.h"
23 #include "imgdecode/cache_manager.h"
24 #include "themes/theme.h"
25 
26 namespace OHOS {
UIAnalogClock()27 UIAnalogClock::UIAnalogClock()
28 {
29     touchable_ = true;
30 }
31 
SetHandImage(HandType type,const UIImageView & img,Point position,Point center)32 void UIAnalogClock::SetHandImage(HandType type, const UIImageView& img, Point position, Point center)
33 {
34     Hand* hand = nullptr;
35     if (type == HandType::HOUR_HAND) {
36         hand = &hourHand_;
37     } else if (type == HandType::MINUTE_HAND) {
38         hand = &minuteHand_;
39     } else {
40         hand = &secondHand_;
41     }
42 
43     hand->center_ = center;
44     hand->position_ = position;
45     hand->initAngle_ = 0;
46     hand->preAngle_ = 0;
47     hand->nextAngle_ = 0;
48     hand->drawtype_ = DrawType::DRAW_IMAGE;
49 
50     if (img.GetSrcType() == IMG_SRC_FILE) {
51         CacheEntry entry;
52         RetCode ret = CacheManager::GetInstance().Open(img.GetPath(), *style_, entry);
53         if (ret != RetCode::OK) {
54             return;
55         }
56         hand->imageInfo_ = entry.GetImageInfo();
57     } else {
58         if (img.GetImageInfo() == nullptr) {
59             hand->imageInfo_.data = nullptr;
60             return;
61         }
62         hand->imageInfo_ = *(img.GetImageInfo());
63     }
64 }
65 
SetHandLine(HandType type,Point position,Point center,ColorType color,uint16_t width,uint16_t height,OpacityType opacity)66 void UIAnalogClock::SetHandLine(HandType type,
67                                 Point position,
68                                 Point center,
69                                 ColorType color,
70                                 uint16_t width,
71                                 uint16_t height,
72                                 OpacityType opacity)
73 {
74     Hand* hand = nullptr;
75     if (type == HandType::HOUR_HAND) {
76         hand = &hourHand_;
77     } else if (type == HandType::MINUTE_HAND) {
78         hand = &minuteHand_;
79     } else {
80         hand = &secondHand_;
81     }
82 
83     hand->color_ = color;
84     hand->height_ = height;
85     hand->width_ = width;
86     hand->position_ = position;
87     hand->center_ = center;
88     hand->opacity_ = opacity;
89     hand->initAngle_ = 0;
90     hand->preAngle_ = 0;
91     hand->nextAngle_ = 0;
92     hand->drawtype_ = DrawType::DRAW_LINE;
93 }
94 
GetHandRotateCenter(HandType type) const95 Point UIAnalogClock::GetHandRotateCenter(HandType type) const
96 {
97     if (type == HandType::HOUR_HAND) {
98         return hourHand_.center_;
99     } else if (type == HandType::MINUTE_HAND) {
100         return minuteHand_.center_;
101     } else {
102         return secondHand_.center_;
103     }
104 }
105 
GetHandPosition(HandType type) const106 Point UIAnalogClock::GetHandPosition(HandType type) const
107 {
108     if (type == HandType::HOUR_HAND) {
109         return hourHand_.position_;
110     } else if (type == HandType::MINUTE_HAND) {
111         return minuteHand_.position_;
112     } else {
113         return secondHand_.position_;
114     }
115 }
116 
GetHandInitAngle(HandType type) const117 uint16_t UIAnalogClock::GetHandInitAngle(HandType type) const
118 {
119     if (type == HandType::HOUR_HAND) {
120         return hourHand_.initAngle_;
121     } else if (type == HandType::MINUTE_HAND) {
122         return minuteHand_.initAngle_;
123     } else {
124         return secondHand_.initAngle_;
125     }
126 }
127 
GetHandCurrentAngle(HandType type) const128 uint16_t UIAnalogClock::GetHandCurrentAngle(HandType type) const
129 {
130     if (type == HandType::HOUR_HAND) {
131         return hourHand_.nextAngle_;
132     } else if (type == HandType::MINUTE_HAND) {
133         return minuteHand_.nextAngle_;
134     } else {
135         return secondHand_.nextAngle_;
136     }
137 }
138 
SetInitTime24Hour(uint8_t hour,uint8_t minute,uint8_t second)139 void UIAnalogClock::SetInitTime24Hour(uint8_t hour, uint8_t minute, uint8_t second)
140 {
141     currentHour_ = hour % ONE_DAY_IN_HOUR;
142     currentMinute_ = minute % ONE_HOUR_IN_MINUTE;
143     currentSecond_ = second % ONE_MINUTE_IN_SECOND;
144 
145     hourHand_.initAngle_ = ConvertHandValueToAngle(currentHour_, HALF_DAY_IN_HOUR, currentMinute_, ONE_HOUR_IN_MINUTE);
146     hourHand_.preAngle_ = hourHand_.initAngle_;
147     hourHand_.nextAngle_ = hourHand_.initAngle_;
148 
149     minuteHand_.initAngle_ =
150         ConvertHandValueToAngle(currentMinute_, ONE_HOUR_IN_MINUTE, currentSecond_, ONE_MINUTE_IN_SECOND);
151     minuteHand_.preAngle_ = minuteHand_.initAngle_;
152     minuteHand_.nextAngle_ = minuteHand_.initAngle_;
153 
154     secondHand_.initAngle_ = ConvertHandValueToAngle(currentSecond_, ONE_MINUTE_IN_SECOND);
155     secondHand_.preAngle_ = secondHand_.initAngle_;
156     secondHand_.nextAngle_ = secondHand_.initAngle_;
157 
158     UpdateClock(true);
159     Invalidate();
160 }
161 
SetInitTime12Hour(uint8_t hour,uint8_t minute,uint8_t second,bool am)162 void UIAnalogClock::SetInitTime12Hour(uint8_t hour, uint8_t minute, uint8_t second, bool am)
163 {
164     SetInitTime24Hour((hour % HALF_DAY_IN_HOUR) + (am ? 0 : HALF_DAY_IN_HOUR), minute, second);
165 }
166 
ConvertHandValueToAngle(uint8_t handValue,uint8_t range,uint8_t secondHandValue,uint8_t ratio) const167 uint16_t UIAnalogClock::ConvertHandValueToAngle(uint8_t handValue,
168                                                 uint8_t range,
169                                                 uint8_t secondHandValue,
170                                                 uint8_t ratio) const
171 {
172     if ((range == 0) || (ratio == 0)) {
173         GRAPHIC_LOGW("UIAnalogClock::ConvertHandValueToAngle Invalid range or ratio\n");
174         return 0;
175     }
176     /*
177      * Example: calculate the angle of hour hand
178      * Assume that the time is 5: 30, then range is 12, radio is 60
179      * angle is [(5 * 60  + 30) / (12 * 60)] * 360
180      */
181     uint32_t degree = (static_cast<uint16_t>(handValue) * ratio + secondHandValue);
182     degree = static_cast<uint32_t>(CIRCLE_IN_DEGREE * degree / (static_cast<uint16_t>(range) * ratio));
183 
184     return static_cast<uint16_t>(degree % CIRCLE_IN_DEGREE);
185 }
186 
ConvertHandValueToAngle(uint8_t handValue,uint8_t range) const187 uint16_t UIAnalogClock::ConvertHandValueToAngle(uint8_t handValue, uint8_t range) const
188 {
189     if (range == 0) {
190         GRAPHIC_LOGW("UIAnalogClock::ConvertHandValueToAngle Invalid range or ratio\n");
191         return 0;
192     }
193     /*
194      * Example: calculate the angle of second hand without millisecond handle
195      * Assume that the time is 5:30:30, then range is 60
196      * angle is (30 / 60) * 360
197      */
198     return (static_cast<uint16_t>(handValue) * CIRCLE_IN_DEGREE / range);
199 }
200 
UpdateClock(bool clockInit)201 void UIAnalogClock::UpdateClock(bool clockInit)
202 {
203     hourHand_.nextAngle_ = ConvertHandValueToAngle(currentHour_, HALF_DAY_IN_HOUR, currentMinute_, ONE_HOUR_IN_MINUTE);
204     minuteHand_.nextAngle_ =
205         ConvertHandValueToAngle(currentMinute_, ONE_HOUR_IN_MINUTE, currentSecond_, ONE_MINUTE_IN_SECOND);
206     secondHand_.nextAngle_ = ConvertHandValueToAngle(currentSecond_, ONE_MINUTE_IN_SECOND);
207 
208     Rect rect = GetRect();
209     CalculateRedrawArea(rect, hourHand_, clockInit);
210     CalculateRedrawArea(rect, minuteHand_, clockInit);
211     if (GetWorkMode() == WorkMode::NORMAL) {
212         CalculateRedrawArea(rect, secondHand_, clockInit);
213     }
214 }
215 
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)216 void UIAnalogClock::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
217 {
218     BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opaScale_);
219 }
220 
OnPostDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)221 void UIAnalogClock::OnPostDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
222 {
223     UpdateClock(true);
224     Rect current = GetOrigRect();
225     DrawHand(gfxDstBuffer, current, invalidatedArea, hourHand_);
226     DrawHand(gfxDstBuffer, current, invalidatedArea, minuteHand_);
227     if (GetWorkMode() == WorkMode::NORMAL) {
228         DrawHand(gfxDstBuffer, current, invalidatedArea, secondHand_);
229     }
230     UIView::OnPostDraw(gfxDstBuffer, invalidatedArea);
231 }
232 
CalculateRedrawArea(const Rect & current,Hand & hand,bool clockInit)233 void UIAnalogClock::CalculateRedrawArea(const Rect& current, Hand& hand, bool clockInit)
234 {
235     /*
236      * Use the current image as an independent rectangular area
237      * to calculate the coordinate conversion coefficient.
238      */
239     int16_t imgWidth = hand.imageInfo_.header.width;
240     int16_t imgHeight = hand.imageInfo_.header.height;
241 
242     int16_t left = hand.position_.x + current.GetLeft();
243     int16_t right = left + imgWidth - 1;
244     int16_t top = hand.position_.y + current.GetTop();
245     int16_t bottom = top + imgHeight - 1;
246     Rect imgRect(left, top, right, bottom);
247     TransformMap backwardMap(imgRect);
248     Vector2<float> pivot;
249     pivot.x_ = hand.center_.x;
250     pivot.y_ = hand.center_.y;
251 
252     /* Rotate the specified angle,  */
253     backwardMap.Rotate(hand.nextAngle_ - hand.initAngle_, pivot);
254     Rect redraw = hand.target_;
255     hand.target_ = backwardMap.GetBoxRect();
256     hand.trans_ = backwardMap;
257     hand.preAngle_ = hand.nextAngle_;
258     if (!clockInit) {
259         /* Prevent old images from being residued */
260         redraw.Join(redraw, hand.target_);
261         InvalidateRect(redraw);
262     }
263 }
264 
DrawHand(BufferInfo & gfxDstBuffer,const Rect & current,const Rect & invalidatedArea,Hand & hand)265 void UIAnalogClock::DrawHand(BufferInfo& gfxDstBuffer, const Rect& current, const Rect& invalidatedArea, Hand& hand)
266 {
267     if (hand.drawtype_ == DrawType::DRAW_IMAGE) {
268         DrawHandImage(gfxDstBuffer, current, invalidatedArea, hand);
269     } else {
270         DrawHandLine(gfxDstBuffer, invalidatedArea, hand);
271     }
272 }
273 
DrawHandImage(BufferInfo & gfxDstBuffer,const Rect & current,const Rect & invalidatedArea,Hand & hand)274 void UIAnalogClock::DrawHandImage(BufferInfo& gfxDstBuffer,
275                                   const Rect& current,
276                                   const Rect& invalidatedArea,
277                                   Hand& hand)
278 {
279     if (hand.imageInfo_.data == nullptr) {
280         return;
281     }
282     uint8_t pxSize = DrawUtils::GetPxSizeByColorMode(hand.imageInfo_.header.colorMode);
283     TransformDataInfo imageTranDataInfo = {hand.imageInfo_.header, hand.imageInfo_.data, pxSize, BlurLevel::LEVEL0,
284                                            TransformAlgorithm::BILINEAR};
285     BaseGfxEngine::GetInstance()->DrawTransform(gfxDstBuffer, invalidatedArea, {0, 0}, Color::Black(), opaScale_,
286                                                 hand.trans_, imageTranDataInfo);
287 }
288 
DrawHandLine(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,Hand & hand)289 void UIAnalogClock::DrawHandLine(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, Hand& hand)
290 {
291     float sinma = Sin(hand.nextAngle_);
292     float cosma = Sin(hand.nextAngle_ + THREE_QUARTER_IN_DEGREE);
293     int32_t handLength = hand.height_;
294     Rect rect = GetRect();
295     Point start;
296     Point end;
297     Point curCenter;
298     curCenter.x = hand.position_.x + hand.center_.x + rect.GetLeft();
299     curCenter.y = hand.position_.y + hand.center_.y + rect.GetTop();
300 
301     int32_t startToCenterLength = hand.center_.y;
302 
303     int32_t xPointLength = static_cast<int32_t>(startToCenterLength * sinma);
304     int32_t yPointLength = static_cast<int32_t>(startToCenterLength * cosma);
305 
306     start.x = xPointLength + curCenter.x;
307     start.y = yPointLength + curCenter.y;
308 
309     /*
310      * @ startToCenterLength: means the length between StartPoint and CenterPoint.
311      * @ handlength: means the hand height.
312      * @ xlength: means X-axis length relative to the center point
313      * @ ylength: means Y-axis length relative to the center point
314      */
315     int32_t xlength = static_cast<int32_t>((startToCenterLength - handLength) * sinma);
316     int32_t ylength = static_cast<int32_t>((startToCenterLength - handLength) * cosma);
317     end.x = xlength + curCenter.x;
318     end.y = ylength + curCenter.y;
319 
320     BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, start, end, invalidatedArea, hand.width_, hand.color_,
321                                            hand.opacity_);
322 }
323 
SetWorkMode(WorkMode newMode)324 void UIAnalogClock::SetWorkMode(WorkMode newMode)
325 {
326     WorkMode oldMode = mode_;
327 
328     if (oldMode != newMode) {
329         /*
330          * After entering the alwayson mode, all child controls are no longer drawn,
331          * making the simplest analog clock.
332          */
333         isViewGroup_ = (newMode == ALWAYS_ON) ? false : true;
334         mode_ = newMode;
335         Invalidate();
336     }
337 }
338 } // namespace OHOS
339