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