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 26namespace OHOS { 27UIAnalogClock::UIAnalogClock() 28{ 29 touchable_ = true; 30} 31 32void 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 66void 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 95Point 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 106Point 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 117uint16_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 128uint16_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 139void 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 162void 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 167uint16_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 187uint16_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 201void 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 216void UIAnalogClock::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea) 217{ 218 BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opaScale_); 219} 220 221void 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 233void 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 265void 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 274void 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 289void 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 324void 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