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_image_view.h" 17#include "common/image.h" 18#include "common/typed_text.h" 19#include "draw/draw_image.h" 20#include "draw/draw_label.h" 21#include "engines/gfx/gfx_engine_manager.h" 22#include "gfx_utils/file.h" 23#include "gfx_utils/image_info.h" 24#include "gfx_utils/mem_api.h" 25#include "imgdecode/cache_manager.h" 26#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 27#include "gif_lib.h" 28#endif 29 30namespace OHOS { 31#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 32class GifImageAnimator : public Animator, public AnimatorCallback { 33public: 34 GifImageAnimator(UIView* view, const char* src) 35 : Animator(this, view, 0, true), 36 gifFileType_(nullptr), 37 imageIndex_(0), 38 delayTime_(0), 39 lastRunTime_(0), 40 deltaTime_(0), 41 gifDataSize_(0), 42 src_(src) 43 { 44 } 45 46 virtual ~GifImageAnimator() 47 { 48 CloseGifFile(); 49 } 50 51 void Callback(UIView* view) override; 52 53 void SetGifFileType(GifFileType* gifFileType) 54 { 55 gifFileType_ = gifFileType; 56 } 57 58 uint32_t SetGifFrame(GifFileType* gifFileType, int32_t imageIndex, UIImageView* imageView) const; 59 void DealGifImageData(const GifFileType* gifFileType, 60 const GifImageDesc* gifImageDesc, 61 const SavedImage* savedImage, 62 GraphicsControlBlock gcb, 63 const ColorMapObject* colorMap) const; 64 void OpenGifFile(const char* src); 65 void CloseGifFile(); 66 67private: 68 GifFileType* GetGifFileType() 69 { 70 if (gifFileType_ == nullptr) { 71 OpenGifFile(src_); 72 } 73 return gifFileType_; 74 } 75 76 GifFileType* gifFileType_; 77 int32_t imageIndex_; 78 uint32_t delayTime_; 79 uint32_t lastRunTime_; 80 uint32_t deltaTime_; 81 uint32_t gifDataSize_; 82 uint8_t* gifImageData_ = nullptr; 83 const char* src_; 84}; 85 86void GifImageAnimator::OpenGifFile(const char* src) 87{ 88 int error = D_GIF_SUCCEEDED; 89 GifFileType* gifFileType = DGifOpenFileName(src, &error); 90 if (error != D_GIF_SUCCEEDED) { 91 return; 92 } 93 DGifSlurp(gifFileType); 94 /* 3 : when change single pixel to byte, the buffer should divided by 8, equal to shift right 3 bits. */ 95 uint8_t pixelByteSize = DrawUtils::GetPxSizeByColorMode(ARGB8888) >> 3; 96 gifDataSize_ = gifFileType->SWidth * gifFileType->SHeight * pixelByteSize; 97 gifImageData_ = static_cast<uint8_t*>(UIMalloc(gifDataSize_)); 98 if (gifImageData_ == nullptr) { 99 CloseGifFile(); 100 return; 101 } 102 SetGifFileType(gifFileType); 103} 104 105void GifImageAnimator::CloseGifFile() 106{ 107 GifFileType* gifFileType = GetGifFileType(); 108 if (gifFileType != nullptr) { 109 DGifCloseFile(gifFileType, nullptr); 110 } 111 if (gifImageData_ != nullptr) { 112 UIFree(reinterpret_cast<void*>(const_cast<uint8_t*>(gifImageData_))); 113 gifImageData_ = nullptr; 114 } 115} 116 117void GifImageAnimator::Callback(UIView* view) 118{ 119 if (view == nullptr) { 120 return; 121 } 122 UIImageView* imageView = static_cast<UIImageView*>(view); 123 uint32_t curTime = GetRunTime(); 124 if (curTime != 0) { 125 if (curTime + deltaTime_ - lastRunTime_ >= delayTime_) { 126 deltaTime_ = curTime + deltaTime_ - lastRunTime_ - delayTime_; 127 lastRunTime_ = curTime; 128 } else { 129 return; 130 } 131 } 132 GifFileType* gifFileType = GetGifFileType(); 133 if (gifFileType != nullptr) { 134 delayTime_ = SetGifFrame(gifFileType, imageIndex_, imageView); 135 imageIndex_ = (imageIndex_ < gifFileType->ImageCount - 1) ? (imageIndex_ + 1) : 0; 136 } 137} 138 139uint32_t GifImageAnimator::SetGifFrame(GifFileType* gifFileType, int32_t imageIndex, UIImageView* imageView) const 140{ 141 SavedImage* savedImage = &(gifFileType->SavedImages[imageIndex]); 142 if (savedImage == nullptr) { 143 return 0; 144 } 145 GifImageDesc* gifImageDesc = &(savedImage->ImageDesc); 146 if (gifImageDesc == nullptr) { 147 return 0; 148 } 149 GraphicsControlBlock gcb; 150 int32_t ret = DGifSavedExtensionToGCB(gifFileType, imageIndex, &gcb); 151 if (ret != GIF_OK) { 152 return 0; 153 } 154 ColorMapObject* colorMap = nullptr; 155 if (gifImageDesc->ColorMap != nullptr) { 156 colorMap = gifImageDesc->ColorMap; 157 } else { 158 colorMap = gifFileType->SColorMap; 159 } 160 161 DealGifImageData(gifFileType, gifImageDesc, savedImage, gcb, colorMap); 162 if (gifImageData_ == nullptr) { 163 return 0; 164 } 165 imageView->gifFrameFlag_ = true; 166 ImageInfo gifFrame; 167 gifFrame.header.width = gifFileType->SWidth; 168 gifFrame.header.height = gifFileType->SHeight; 169 gifFrame.header.colorMode = ARGB8888; 170 gifFrame.dataSize = gifDataSize_; 171 gifFrame.data = gifImageData_; 172 imageView->SetSrc(&gifFrame); 173 174 if (gcb.DelayTime >= 0) { 175 return static_cast<uint32_t>(gcb.DelayTime) * 10; // 10: change hundredths (1/100) of a second to millisecond 176 } else { 177 return 0; 178 } 179} 180 181void GifImageAnimator::DealGifImageData(const GifFileType* gifFileType, 182 const GifImageDesc* gifImageDesc, 183 const SavedImage* savedImage, 184 GraphicsControlBlock gcb, 185 const ColorMapObject* colorMap) const 186{ 187 if ((gifFileType == nullptr) || (gifImageDesc == nullptr) || (savedImage == nullptr) || 188 (savedImage->RasterBits == nullptr) || (colorMap == nullptr) || (colorMap->Colors == nullptr)) { 189 return; 190 } 191 uint8_t colorIndex = 0; 192 GifColorType* gifColorType = nullptr; 193 uint32_t index = 0; 194 195 for (int32_t x = 0; x < gifFileType->SHeight; x++) { 196 for (int32_t y = 0; y < gifFileType->SWidth; y++) { 197 bool transparentColor = true; 198 if ((x >= gifImageDesc->Top) && (x < gifImageDesc->Top + gifImageDesc->Height) && 199 (y >= gifImageDesc->Left) && (y < gifImageDesc->Left + gifImageDesc->Width)) { 200 int32_t loc = (x - gifImageDesc->Top) * gifImageDesc->Width + (y - gifImageDesc->Left); 201 colorIndex = savedImage->RasterBits[loc]; 202 203 if ((gcb.DisposalMode != DISPOSE_DO_NOT) || (gcb.TransparentColor == NO_TRANSPARENT_COLOR) || 204 (colorIndex != gcb.TransparentColor)) { 205 transparentColor = false; 206 } 207 } 208 if (transparentColor) { 209 index += 4; // 4: skip color index, keep last frame color 210 } else { 211 gifColorType = &colorMap->Colors[colorIndex]; 212 gifImageData_[index++] = gifColorType->Blue; 213 gifImageData_[index++] = gifColorType->Green; 214 gifImageData_[index++] = gifColorType->Red; 215 gifImageData_[index++] = OPA_OPAQUE; 216 } 217 } 218 } 219} 220#endif 221 222UIImageView::UIImageView() 223 : imageWidth_(0), 224 imageHeight_(0), 225 autoEnable_(true), 226 needRefresh_(false), 227 colorFormat_(UNKNOWN), 228 blurLevel_(BlurLevel::LEVEL0), 229 algorithm_(TransformAlgorithm::BILINEAR), 230 reserve_(0) 231{ 232 style_ = &(StyleDefault::GetBackgroundTransparentStyle()); 233#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 234 gifImageAnimator_ = nullptr; 235 gifFrameFlag_ = false; 236#endif 237} 238 239UIImageView::~UIImageView() 240{ 241#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 242 RemoveAndStopGifAnimator(); 243#endif 244 if (drawTransMap_ != nullptr) { 245 delete drawTransMap_; 246 drawTransMap_ = nullptr; 247 } 248 if (contentMatrix_ != nullptr) { 249 delete contentMatrix_; 250 contentMatrix_ = nullptr; 251 } 252} 253 254void UIImageView::SetResizeMode(ImageResizeMode mode) 255{ 256 // when automatic adaptation is enabled only save the mode, no need to update the DrawtransMap 257 if (autoEnable_) { 258 imageResizeMode_ = mode; 259 } else if (imageResizeMode_ != mode) { 260 needRefresh_ = true; 261 ReMeasure(); 262 // must update the mode, before calling UpdateDrawTransMap 263 imageResizeMode_ = mode; 264 UpdateDrawTransMap(true); 265 } 266} 267 268void UIImageView::AdjustScaleAndTranslate(Vector3<float>& scale, Vector3<int16_t>& translate, 269 int16_t widgetWidth, int16_t widgetHeight) const 270{ 271 // adjust scale 272 float ratio = 1.0f; 273 switch (imageResizeMode_) { 274 case ImageResizeMode::COVER: 275 ratio = MATH_MAX(scale.x_, scale.y_); 276 break; 277 case ImageResizeMode::CONTAIN: 278 ratio = MATH_MIN(scale.x_, scale.y_); 279 break; 280 case ImageResizeMode::CENTER: // ratio is 1.0f 281 break; 282 case ImageResizeMode::SCALE_DOWN: 283 ratio = MATH_MIN(scale.x_, scale.y_); 284 ratio = MATH_MIN(ratio, 1.0f); 285 break; 286 case ImageResizeMode::FILL: // do nothing 287 return; 288 default: 289 break; 290 } 291 if (scale.x_ != ratio) { 292 scale.x_ = ratio; 293 // 0.5: adjust the x-coordinate of the content to the center of widget 294 translate.x_ += (static_cast<float>(widgetWidth) - static_cast<float>(imageWidth_) * ratio) * 0.5f; 295 } 296 if (scale.y_ != ratio) { 297 scale.y_ = ratio; 298 // 0.5: adjust the y-coordinate of the content to the center of widget 299 translate.y_ += (static_cast<float>(widgetHeight) - static_cast<float>(imageHeight_) * ratio) * 0.5f; 300 } 301} 302 303void UIImageView::UpdateContentMatrix() 304{ 305 Rect viewRect = GetOrigRect(); 306 if (autoEnable_ || (imageResizeMode_ == ImageResizeMode::NONE) || 307 (imageWidth_ == viewRect.GetWidth() && imageHeight_ == viewRect.GetHeight()) || 308 imageWidth_ == 0 || imageHeight_ == 0) { 309 if (contentMatrix_ != nullptr) { 310 delete contentMatrix_; 311 contentMatrix_ = nullptr; 312 } 313 return; 314 } 315 if (contentMatrix_ == nullptr) { 316 contentMatrix_ = new Matrix4<float>(); 317 if (contentMatrix_ == nullptr) { 318 GRAPHIC_LOGE("can not new contentMatrix"); 319 return; 320 } 321 } 322 int16_t widgetWidth = viewRect.GetWidth() - style_->paddingLeft_ - style_->paddingRight_ - 323 style_->borderWidth_ * 2; // 2: excludes the border-left and border-right 324 int16_t widgetHeight = viewRect.GetHeight() - style_->paddingTop_ - style_->paddingBottom_ - 325 style_->borderWidth_ * 2; // 2: excludes the border-top and border-bottom 326 327 float scaleX = static_cast<float>(widgetWidth) / static_cast<float>(imageWidth_); 328 float scaleY = static_cast<float>(widgetHeight) / static_cast<float>(imageHeight_); 329 Vector3<float> scale(scaleX, scaleY, 1.0f); 330 Vector3<int16_t> translate(style_->paddingLeft_ + style_->borderWidth_, 331 style_->paddingTop_ + style_->borderWidth_, 0); 332 AdjustScaleAndTranslate(scale, translate, widgetWidth, widgetHeight); 333 334 auto scaleMatrix = Matrix4<float>::Scale(scale, Vector3<float>(viewRect.GetX(), viewRect.GetY(), 0)); 335 auto translateMatrix = Matrix4<float>::Translate(Vector3<float>(translate.x_, translate.y_, 0)); 336 *contentMatrix_ = translateMatrix * scaleMatrix; 337} 338 339void UIImageView::UpdateDrawTransMap(bool updateContentMatrix) 340{ 341 auto viewRect = GetOrigRect(); 342 if (updateContentMatrix || (drawTransMap_ != nullptr && 343 (drawTransMap_->GetTransMapRect().GetX() != viewRect.GetX() || 344 drawTransMap_->GetTransMapRect().GetY() != viewRect.GetY()))) { 345 UpdateContentMatrix(); 346 } 347 // has no transformation 348 if ((contentMatrix_ == nullptr) && ((transMap_ == nullptr) || transMap_->IsInvalid())) { 349 if (drawTransMap_ != nullptr) { 350 delete drawTransMap_; 351 drawTransMap_ = nullptr; 352 } 353 return; 354 } 355 if (drawTransMap_ == nullptr) { 356 drawTransMap_ = new TransformMap(); 357 if (drawTransMap_ == nullptr) { 358 GRAPHIC_LOGE("can not new drawTransMap"); 359 return; 360 } 361 } 362 if (contentMatrix_ != nullptr) { 363 drawTransMap_->SetTransMapRect(Rect(viewRect.GetX(), viewRect.GetY(), 364 viewRect.GetX() + imageWidth_ - 1, viewRect.GetY() + imageHeight_ - 1)); 365 } else { 366 drawTransMap_->SetTransMapRect(viewRect); 367 } 368 // only contentMatrix 369 if (transMap_ == nullptr || transMap_->IsInvalid()) { 370 if (contentMatrix_ == nullptr) { 371 GRAPHIC_LOGE("Text: UpdateDrawTransMap contentMatrix_ is nullptr"); 372 return; 373 } 374 drawTransMap_->SetMatrix(*contentMatrix_); 375 return; 376 } 377 // update the transMap, now the transMap is not nullptr 378 if (!(transMap_->GetTransMapRect() == viewRect)) { 379 transMap_->SetTransMapRect(viewRect); 380 } 381 // only transMap 382 if (contentMatrix_ == nullptr) { 383 *drawTransMap_ = *transMap_; 384 return; 385 } 386 // merge the transMap and content matrix 387 auto rect = transMap_->GetTransMapRect(); 388 auto translate = Matrix4<float>::Translate(Vector3<float>(-rect.GetX(), -rect.GetY(), 0)); 389 auto matrix = transMap_->GetTransformMatrix() * translate; 390 matrix = matrix * (*contentMatrix_); 391 drawTransMap_->SetMatrix(matrix); 392} 393 394void UIImageView::SetHeight(int16_t height) 395{ 396 if (GetHeight() != height) { 397 UIView::SetHeight(height); 398 UpdateDrawTransMap(true); 399 } 400} 401 402void UIImageView::SetWidth(int16_t width) 403{ 404 if (GetWidth() != width) { 405 UIView::SetWidth(width); 406 UpdateDrawTransMap(true); 407 } 408} 409 410bool UIImageView::OnPreDraw(Rect& invalidatedArea) const 411{ 412 if ((image_.GetSrcType() == IMG_SRC_UNKNOWN)) { 413 return true; 414 } 415 416 if ((colorFormat_ == RGB565) || (colorFormat_ == RGB888)) { 417 if (GetRect().IsContains(invalidatedArea)) { 418 return true; 419 } 420 invalidatedArea.Intersect(invalidatedArea, GetRect()); 421 } 422 423 return false; 424} 425 426void UIImageView::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea) 427{ 428 OpacityType opa = GetMixOpaScale(); 429 BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance(); 430 baseGfxEngine->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opa); 431 if ((imageHeight_ == 0) || (imageWidth_ == 0)) { 432 return; 433 } 434 UpdateDrawTransMap(); 435 Rect viewRect = GetContentRect(); 436 Rect trunc(invalidatedArea); 437 if (trunc.Intersect(trunc, viewRect)) { 438 uint8_t srcType = image_.GetSrcType(); 439 if ((srcType == IMG_SRC_FILE) || (srcType == IMG_SRC_VARIABLE)) { 440 Rect cordsTmp; 441 cordsTmp.SetTop(viewRect.GetY()); 442 cordsTmp.SetBottom(viewRect.GetY() + imageHeight_ - 1); 443 444 if ((drawTransMap_ == nullptr) || drawTransMap_->IsInvalid()) { 445 SetCordsTmpRect(gfxDstBuffer, viewRect, trunc, cordsTmp, opa); 446 } else if ((drawTransMap_ != nullptr) && !drawTransMap_->IsInvalid()) { 447 ImageInfo imgInfo; 448 if (srcType == IMG_SRC_FILE) { 449 CacheEntry entry; 450 RetCode ret = CacheManager::GetInstance().Open(GetPath(), *style_, entry); 451 if (ret != RetCode::OK) { 452 return; 453 } 454 imgInfo = entry.GetImageInfo(); 455 } else { 456 imgInfo = *(GetImageInfo()); 457 } 458 uint8_t pxSize = DrawUtils::GetPxSizeByColorMode(imgInfo.header.colorMode); 459 TransformDataInfo imageTranDataInfo = {imgInfo.header, imgInfo.data, pxSize, 460 static_cast<BlurLevel>(blurLevel_), 461 static_cast<TransformAlgorithm>(algorithm_)}; 462 OpacityType opaScale = DrawUtils::GetMixOpacity(opa, style_->imageOpa_); 463 Matrix4<float> scaleMatrix = drawTransMap_->GetScaleMatrix(); 464 int16_t paddingX = style_->paddingLeft_ * scaleMatrix[0][0]; 465 int16_t paddingY = style_->paddingTop_ * scaleMatrix[1][1]; 466 baseGfxEngine->DrawTransform(gfxDstBuffer, trunc, {paddingX, paddingY}, Color::Black(), 467 opaScale, *drawTransMap_, imageTranDataInfo); 468 } 469 } 470 } 471} 472 473void UIImageView::SetCordsTmpRect(BufferInfo& gfxDstBuffer, Rect& viewRect, Rect& trunc, 474 Rect& cordsTmp, OpacityType opa) 475{ 476 while (cordsTmp.GetTop() <= viewRect.GetBottom()) { 477 cordsTmp.SetLeft(viewRect.GetX()); 478 cordsTmp.SetRight(viewRect.GetX() + imageWidth_ - 1); 479 while (cordsTmp.GetLeft() <= viewRect.GetRight()) { 480 image_.DrawImage(gfxDstBuffer, cordsTmp, trunc, *style_, opa); 481 cordsTmp.SetLeft(cordsTmp.GetLeft() + imageWidth_); 482 cordsTmp.SetRight(cordsTmp.GetRight() + imageWidth_); 483 } 484 cordsTmp.SetTop(cordsTmp.GetTop() + imageHeight_); 485 cordsTmp.SetBottom(cordsTmp.GetBottom() + imageHeight_); 486 } 487} 488 489void UIImageView::SetSrc(const char* src) 490{ 491#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 492 if (src == nullptr) { 493 return; 494 } 495 const static uint8_t IMG_BYTES_TO_CHECK = 4; // 4: check 4 bytes of image file 496 char buf[IMG_BYTES_TO_CHECK] = {0}; 497 int32_t fd = open(src, O_RDONLY); 498 if (fd < 0) { 499 return; 500 } 501 if (read(fd, buf, IMG_BYTES_TO_CHECK) != IMG_BYTES_TO_CHECK) { 502 close(fd); 503 return; 504 } 505 close(fd); 506 bool updated = false; 507 RemoveAndStopGifAnimator(); 508 // 0x47 0x49 0x46: GIF file's header 509 if ((static_cast<uint8_t>(buf[0]) == 0x47) && (static_cast<uint8_t>(buf[1]) == 0x49) && 510 (static_cast<uint8_t>(buf[2]) == 0x46)) { // 2: array index of GIF file's header 511 if (gifImageAnimator_ == nullptr) { 512 gifImageAnimator_ = new GifImageAnimator(this, src); 513 if (gifImageAnimator_ == nullptr) { 514 GRAPHIC_LOGE("new GifImageAnimator fail"); 515 return; 516 } 517 } 518 AddAndStartGifAnimator(); 519 updated = true; 520 } else { 521 updated = image_.SetSrc(src); 522 } 523#else 524 bool updated = image_.SetSrc(src); 525#endif 526 if (!updated) { 527 return; 528 } 529 needRefresh_ = true; 530 if (autoEnable_ || (imageResizeMode_ != ImageResizeMode::NONE)) { 531 UIImageView::ReMeasure(); 532 } 533 if (imageResizeMode_ != ImageResizeMode::NONE) { 534 UpdateDrawTransMap(true); 535 } 536 Invalidate(); 537} 538 539void UIImageView::ReMeasure() 540{ 541 if (!needRefresh_) { 542 return; 543 } 544 needRefresh_ = false; 545 546 ImageHeader header = {0}; 547 image_.GetHeader(header); 548 549 imageWidth_ = header.width; 550 imageHeight_ = header.height; 551 colorFormat_ = header.colorMode; 552 553 if (autoEnable_) { 554 Invalidate(); 555 Resize(imageWidth_, imageHeight_); 556 Invalidate(); 557 } 558} 559 560void UIImageView::SetSrc(const ImageInfo* src) 561{ 562#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 563 if (!gifFrameFlag_ && (gifImageAnimator_ != nullptr)) { 564 RemoveAndStopGifAnimator(); 565 } 566 gifFrameFlag_ = false; 567#endif 568 bool updated = image_.SetSrc(src); 569 if (!updated) { 570 return; 571 } 572 needRefresh_ = true; 573 if (autoEnable_ || (imageResizeMode_ != ImageResizeMode::NONE)) { 574 UIImageView::ReMeasure(); 575 } 576 if (imageResizeMode_ != ImageResizeMode::NONE) { 577 UpdateDrawTransMap(true); 578 } 579 Invalidate(); 580} 581 582#if defined(ENABLE_GIF) && (ENABLE_GIF == 1) 583void UIImageView::AddAndStartGifAnimator() 584{ 585 if (gifImageAnimator_ != nullptr) { 586 gifImageAnimator_->Start(); 587 } 588} 589 590void UIImageView::RemoveAndStopGifAnimator() 591{ 592 if (gifImageAnimator_ != nullptr) { 593 gifImageAnimator_->Stop(); 594 delete gifImageAnimator_; 595 gifImageAnimator_ = nullptr; 596 } 597} 598#endif 599} // namespace OHOS 600