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