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 (ENABLE_GIF == 1)
27 #include "gif_lib.h"
28 #endif
29
30 namespace OHOS {
31 #if (ENABLE_GIF == 1)
32 class GifImageAnimator : public Animator, public AnimatorCallback {
33 public:
GifImageAnimator(UIView * view,const char * src)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
~GifImageAnimator()46 virtual ~GifImageAnimator()
47 {
48 CloseGifFile();
49 }
50
51 void Callback(UIView* view) override;
52
SetGifFileType(GifFileType * gifFileType)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 const void OpenGifFile(const char* src);
65 void CloseGifFile();
66
67 private:
GetGifFileType()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
OpenGifFile(const char * src)86 const void 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
CloseGifFile()105 void 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
Callback(UIView * view)117 void 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
SetGifFrame(GifFileType * gifFileType,int32_t imageIndex,UIImageView * imageView) const139 uint32_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
DealGifImageData(const GifFileType * gifFileType,const GifImageDesc * gifImageDesc,const SavedImage * savedImage,GraphicsControlBlock gcb,const ColorMapObject * colorMap) const181 void 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 bool transparentColor = true;
195 int32_t loc = 0;
196 for (int32_t x = 0; x < gifFileType->SHeight; x++) {
197 for (int32_t y = 0; y < gifFileType->SWidth; y++) {
198 transparentColor = true;
199 if ((x >= gifImageDesc->Top) && (x < gifImageDesc->Top + gifImageDesc->Height) &&
200 (y >= gifImageDesc->Left) && (y < gifImageDesc->Left + gifImageDesc->Width)) {
201 loc = (x - gifImageDesc->Top) * gifImageDesc->Width + (y - gifImageDesc->Left);
202 colorIndex = savedImage->RasterBits[loc];
203
204 if ((gcb.DisposalMode != DISPOSE_DO_NOT) || (gcb.TransparentColor == NO_TRANSPARENT_COLOR) ||
205 (colorIndex != gcb.TransparentColor)) {
206 transparentColor = false;
207 }
208 }
209 if (transparentColor) {
210 index += 4; // 4: skip color index, keep last frame color
211 } else {
212 gifColorType = &colorMap->Colors[colorIndex];
213 gifImageData_[index++] = gifColorType->Blue;
214 gifImageData_[index++] = gifColorType->Green;
215 gifImageData_[index++] = gifColorType->Red;
216 gifImageData_[index++] = OPA_OPAQUE;
217 }
218 }
219 }
220 }
221 #endif
222
UIImageView()223 UIImageView::UIImageView()
224 : imageWidth_(0),
225 imageHeight_(0),
226 autoEnable_(true),
227 needRefresh_(false),
228 colorFormat_(UNKNOW),
229 blurLevel_(BlurLevel::LEVEL0),
230 algorithm_(TransformAlgorithm::BILINEAR),
231 reserve_(0)
232 {
233 style_ = &(StyleDefault::GetBackgroundTransparentStyle());
234 #if (ENABLE_GIF == 1)
235 gifImageAnimator_ = nullptr;
236 gifFrameFlag_ = false;
237 #endif
238 }
239
~UIImageView()240 UIImageView::~UIImageView()
241 {
242 #if (ENABLE_GIF == 1)
243 RemoveAndStopGifAnimator();
244 #endif
245 if (drawTransMap_ != nullptr) {
246 delete drawTransMap_;
247 drawTransMap_ = nullptr;
248 }
249 if (contentMatrix_ != nullptr) {
250 delete contentMatrix_;
251 contentMatrix_ = nullptr;
252 }
253 }
254
SetResizeMode(ImageResizeMode mode)255 void UIImageView::SetResizeMode(ImageResizeMode mode)
256 {
257 // when automatic adaptation is enabled only save the mode, no need to update the DrawtransMap
258 if (autoEnable_) {
259 imageResizeMode_ = mode;
260 } else if (imageResizeMode_ != mode) {
261 needRefresh_ = true;
262 ReMeasure();
263 // must update the mode, before calling UpdateDrawTransMap
264 imageResizeMode_ = mode;
265 UpdateDrawTransMap(true);
266 }
267 }
268
AdjustScaleAndTranslate(Vector3<float> & scale,Vector3<int16_t> & translate,int16_t widgetWidth,int16_t widgetHeight) const269 void UIImageView::AdjustScaleAndTranslate(Vector3<float>& scale, Vector3<int16_t>& translate,
270 int16_t widgetWidth, int16_t widgetHeight) const
271 {
272 // adjust scale
273 float ratio = 1.0f;
274 switch (imageResizeMode_) {
275 case ImageResizeMode::COVER:
276 ratio = MATH_MAX(scale.x_, scale.y_);
277 break;
278 case ImageResizeMode::CONTAIN:
279 ratio = MATH_MIN(scale.x_, scale.y_);
280 break;
281 case ImageResizeMode::CENTER: // ratio is 1.0f
282 break;
283 case ImageResizeMode::SCALE_DOWN:
284 ratio = MATH_MIN(scale.x_, scale.y_);
285 ratio = MATH_MIN(ratio, 1.0f);
286 break;
287 case ImageResizeMode::FILL: // do nothing
288 return;
289 default:
290 break;
291 }
292 if (scale.x_ != ratio) {
293 scale.x_ = ratio;
294 // 0.5: adjust the x-coordinate of the content to the center of widget
295 translate.x_ += (static_cast<float>(widgetWidth) - static_cast<float>(imageWidth_) * ratio) * 0.5f;
296 }
297 if (scale.y_ != ratio) {
298 scale.y_ = ratio;
299 // 0.5: adjust the y-coordinate of the content to the center of widget
300 translate.y_ += (static_cast<float>(widgetHeight) - static_cast<float>(imageHeight_) * ratio) * 0.5f;
301 }
302 }
303
UpdateContentMatrix()304 void UIImageView::UpdateContentMatrix()
305 {
306 Rect viewRect = GetOrigRect();
307 if (autoEnable_ || (imageResizeMode_ == ImageResizeMode::NONE) ||
308 (imageWidth_ == viewRect.GetWidth() && imageHeight_ == viewRect.GetHeight()) ||
309 imageWidth_ == 0 || imageHeight_ == 0) {
310 if (contentMatrix_ != nullptr) {
311 delete contentMatrix_;
312 contentMatrix_ = nullptr;
313 }
314 return;
315 }
316 if (contentMatrix_ == nullptr) {
317 contentMatrix_ = new Matrix4<float>();
318 if (contentMatrix_ == nullptr) {
319 GRAPHIC_LOGE("can not new contentMatrix");
320 return;
321 }
322 }
323 int16_t widgetWidth = viewRect.GetWidth() - style_->paddingLeft_ - style_->paddingRight_ -
324 style_->borderWidth_ * 2; // 2: excludes the border-left and border-right
325 int16_t widgetHeight = viewRect.GetHeight() - style_->paddingTop_ - style_->paddingBottom_ -
326 style_->borderWidth_ * 2; // 2: excludes the border-top and border-bottom
327
328 float scaleX = static_cast<float>(widgetWidth) / static_cast<float>(imageWidth_);
329 float scaleY = static_cast<float>(widgetHeight) / static_cast<float>(imageHeight_);
330 Vector3<float> scale(scaleX, scaleY, 1.0f);
331 Vector3<int16_t> translate(style_->paddingLeft_ + style_->borderWidth_,
332 style_->paddingTop_ + style_->borderWidth_, 0);
333 AdjustScaleAndTranslate(scale, translate, widgetWidth, widgetHeight);
334
335 auto scaleMatrix = Matrix4<float>::Scale(scale, Vector3<float>(viewRect.GetX(), viewRect.GetY(), 0));
336 auto translateMatrix = Matrix4<float>::Translate(Vector3<float>(translate.x_, translate.y_, 0));
337 *contentMatrix_ = translateMatrix * scaleMatrix;
338 }
339
UpdateDrawTransMap(bool updateContentMatrix)340 void UIImageView::UpdateDrawTransMap(bool updateContentMatrix)
341 {
342 auto viewRect = GetOrigRect();
343 if (updateContentMatrix || (drawTransMap_ != nullptr &&
344 (drawTransMap_->GetTransMapRect().GetX() != viewRect.GetX() ||
345 drawTransMap_->GetTransMapRect().GetY() != viewRect.GetY()))) {
346 UpdateContentMatrix();
347 }
348 // has no transformation
349 if ((contentMatrix_ == nullptr) && ((transMap_ == nullptr) || transMap_->IsInvalid())) {
350 if (drawTransMap_ != nullptr) {
351 delete drawTransMap_;
352 drawTransMap_ = nullptr;
353 }
354 return;
355 }
356 if (drawTransMap_ == nullptr) {
357 drawTransMap_ = new TransformMap();
358 if (drawTransMap_ == nullptr) {
359 GRAPHIC_LOGE("can not new drawTransMap");
360 return;
361 }
362 }
363 if (contentMatrix_ != nullptr) {
364 drawTransMap_->SetTransMapRect(Rect(viewRect.GetX(), viewRect.GetY(),
365 viewRect.GetX() + imageWidth_ - 1, viewRect.GetY() + imageHeight_ - 1));
366 } else {
367 drawTransMap_->SetTransMapRect(viewRect);
368 }
369 // only contentMatrix
370 if (transMap_ == nullptr || transMap_->IsInvalid()) {
371 drawTransMap_->SetMatrix(*contentMatrix_);
372 return;
373 }
374 // update the transMap, now the transMap is not nullptr
375 if (!(transMap_->GetTransMapRect() == viewRect)) {
376 transMap_->SetTransMapRect(viewRect);
377 }
378 // only transMap
379 if (contentMatrix_ == nullptr) {
380 *drawTransMap_ = *transMap_;
381 return;
382 }
383 // merge the transMap and content matrix
384 auto rect = transMap_->GetTransMapRect();
385 auto translate = Matrix4<float>::Translate(Vector3<float>(-rect.GetX(), -rect.GetY(), 0));
386 auto matrix = transMap_->GetTransformMatrix() * translate;
387 matrix = matrix * (*contentMatrix_);
388 drawTransMap_->SetMatrix(matrix);
389 }
390
SetHeight(int16_t height)391 void UIImageView::SetHeight(int16_t height)
392 {
393 if (GetHeight() != height) {
394 UIView::SetHeight(height);
395 UpdateDrawTransMap(true);
396 }
397 }
398
SetWidth(int16_t width)399 void UIImageView::SetWidth(int16_t width)
400 {
401 if (GetWidth() != width) {
402 UIView::SetWidth(width);
403 UpdateDrawTransMap(true);
404 }
405 }
406
OnPreDraw(Rect & invalidatedArea) const407 bool UIImageView::OnPreDraw(Rect& invalidatedArea) const
408 {
409 if ((image_.GetSrcType() == IMG_SRC_UNKNOWN)) {
410 return true;
411 }
412
413 if ((colorFormat_ == RGB565) || (colorFormat_ == RGB888)) {
414 if (GetRect().IsContains(invalidatedArea)) {
415 return true;
416 }
417 invalidatedArea.Intersect(invalidatedArea, GetRect());
418 }
419
420 return false;
421 }
422
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)423 void UIImageView::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
424 {
425 OpacityType opa = GetMixOpaScale();
426 BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opa);
427 if ((imageHeight_ == 0) || (imageWidth_ == 0)) {
428 return;
429 }
430 UpdateDrawTransMap();
431 Rect viewRect = GetContentRect();
432 Rect trunc(invalidatedArea);
433 if (trunc.Intersect(trunc, viewRect)) {
434 uint8_t srcType = image_.GetSrcType();
435 if ((srcType == IMG_SRC_FILE) || (srcType == IMG_SRC_VARIABLE)) {
436 Rect cordsTmp;
437 cordsTmp.SetTop(viewRect.GetY());
438 cordsTmp.SetBottom(viewRect.GetY() + imageHeight_ - 1);
439
440 if ((drawTransMap_ == nullptr) || drawTransMap_->IsInvalid()) {
441 while (cordsTmp.GetTop() <= viewRect.GetBottom()) {
442 cordsTmp.SetLeft(viewRect.GetX());
443 cordsTmp.SetRight(viewRect.GetX() + imageWidth_ - 1);
444 while (cordsTmp.GetLeft() <= viewRect.GetRight()) {
445 image_.DrawImage(gfxDstBuffer, cordsTmp, trunc, *style_, opa);
446 cordsTmp.SetLeft(cordsTmp.GetLeft() + imageWidth_);
447 cordsTmp.SetRight(cordsTmp.GetRight() + imageWidth_);
448 }
449 cordsTmp.SetTop(cordsTmp.GetTop() + imageHeight_);
450 cordsTmp.SetBottom(cordsTmp.GetBottom() + imageHeight_);
451 }
452 } else if ((drawTransMap_ != nullptr) && !drawTransMap_->IsInvalid()) {
453 ImageInfo imgInfo;
454 if (srcType == IMG_SRC_FILE) {
455 CacheEntry entry;
456 RetCode ret = CacheManager::GetInstance().Open(GetPath(), *style_, entry);
457 if (ret != RetCode::OK) {
458 return;
459 }
460 imgInfo = entry.GetImageInfo();
461 } else {
462 imgInfo = *(GetImageInfo());
463 }
464 uint8_t pxSize = DrawUtils::GetPxSizeByColorMode(imgInfo.header.colorMode);
465 TransformDataInfo imageTranDataInfo = {imgInfo.header, imgInfo.data, pxSize,
466 static_cast<BlurLevel>(blurLevel_),
467 static_cast<TransformAlgorithm>(algorithm_)};
468 OpacityType opaScale = DrawUtils::GetMixOpacity(opa, style_->imageOpa_);
469 BaseGfxEngine::GetInstance()->DrawTransform(gfxDstBuffer, trunc, {0, 0}, Color::Black(),
470 opaScale, *drawTransMap_, imageTranDataInfo);
471 }
472 }
473 }
474 }
475
SetSrc(const char * src)476 void UIImageView::SetSrc(const char* src)
477 {
478 #if (ENABLE_GIF == 1)
479 if (src == nullptr) {
480 return;
481 }
482 const static uint8_t IMG_BYTES_TO_CHECK = 4; // 4: check 4 bytes of image file
483 char buf[IMG_BYTES_TO_CHECK] = {0};
484 int32_t fd = open(src, O_RDONLY);
485 if (fd < 0) {
486 return;
487 }
488 if (read(fd, buf, IMG_BYTES_TO_CHECK) != IMG_BYTES_TO_CHECK) {
489 close(fd);
490 return;
491 }
492 close(fd);
493 bool updated = false;
494 RemoveAndStopGifAnimator();
495 // 0x47 0x49 0x46: GIF file's header
496 if ((static_cast<uint8_t>(buf[0]) == 0x47) && (static_cast<uint8_t>(buf[1]) == 0x49) &&
497 (static_cast<uint8_t>(buf[2]) == 0x46)) { // 2: array index of GIF file's header
498 if (gifImageAnimator_ == nullptr) {
499 gifImageAnimator_ = new GifImageAnimator(this, src);
500 if (gifImageAnimator_ == nullptr) {
501 GRAPHIC_LOGE("new GifImageAnimator fail");
502 return;
503 }
504 }
505 AddAndStartGifAnimator();
506 updated = true;
507 } else {
508 updated = image_.SetSrc(src);
509 }
510 #else
511 bool updated = image_.SetSrc(src);
512 #endif
513 if (!updated) {
514 return;
515 }
516 needRefresh_ = true;
517 if (autoEnable_) {
518 UIImageView::ReMeasure();
519 }
520 Invalidate();
521 }
522
ReMeasure()523 void UIImageView::ReMeasure()
524 {
525 if (!needRefresh_) {
526 return;
527 }
528 needRefresh_ = false;
529
530 ImageHeader header = {0};
531 image_.GetHeader(header);
532
533 imageWidth_ = header.width;
534 imageHeight_ = header.height;
535 colorFormat_ = header.colorMode;
536
537 if (autoEnable_) {
538 Invalidate();
539 Resize(imageWidth_, imageHeight_);
540 Invalidate();
541 }
542 }
543
SetSrc(const ImageInfo * src)544 void UIImageView::SetSrc(const ImageInfo* src)
545 {
546 #if (ENABLE_GIF == 1)
547 if (!gifFrameFlag_ && (gifImageAnimator_ != nullptr)) {
548 RemoveAndStopGifAnimator();
549 }
550 gifFrameFlag_ = false;
551 #endif
552 bool updated = image_.SetSrc(src);
553 if (!updated) {
554 return;
555 }
556 needRefresh_ = true;
557 if (autoEnable_) {
558 UIImageView::ReMeasure();
559 }
560 Invalidate();
561 }
562
563 #if (ENABLE_GIF == 1)
AddAndStartGifAnimator()564 void UIImageView::AddAndStartGifAnimator()
565 {
566 if (gifImageAnimator_ != nullptr) {
567 gifImageAnimator_->Start();
568 }
569 }
570
RemoveAndStopGifAnimator()571 void UIImageView::RemoveAndStopGifAnimator()
572 {
573 if (gifImageAnimator_ != nullptr) {
574 gifImageAnimator_->Stop();
575 delete gifImageAnimator_;
576 gifImageAnimator_ = nullptr;
577 }
578 }
579 #endif
580 } // namespace OHOS
581