* Copyright (c) 2020-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "components/ui_image_view.h"
#include "common/image.h"
#include "common/typed_text.h"
#include "draw/draw_image.h"
#include "draw/draw_label.h"
#include "engines/gfx/gfx_engine_manager.h"
#include "gfx_utils/file.h"
#include "gfx_utils/image_info.h"
#include "gfx_utils/mem_api.h"
#include "imgdecode/cache_manager.h"
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
#include "gif_lib.h"
#endif
namespace OHOS {
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
class GifImageAnimator : public Animator, public AnimatorCallback {
public:
GifImageAnimator(UIView* view, const char* src)
: Animator(this, view, 0, true),
gifFileType_(nullptr),
imageIndex_(0),
delayTime_(0),
lastRunTime_(0),
deltaTime_(0),
gifDataSize_(0),
src_(src)
{
}
virtual ~GifImageAnimator()
{
CloseGifFile();
}
void Callback(UIView* view) override;
void SetGifFileType(GifFileType* gifFileType)
{
gifFileType_ = gifFileType;
}
uint32_t SetGifFrame(GifFileType* gifFileType, int32_t imageIndex, UIImageView* imageView) const;
void DealGifImageData(const GifFileType* gifFileType,
const GifImageDesc* gifImageDesc,
const SavedImage* savedImage,
GraphicsControlBlock gcb,
const ColorMapObject* colorMap) const;
void OpenGifFile(const char* src);
void CloseGifFile();
private:
GifFileType* GetGifFileType()
{
if (gifFileType_ == nullptr) {
OpenGifFile(src_);
}
return gifFileType_;
}
GifFileType* gifFileType_;
int32_t imageIndex_;
uint32_t delayTime_;
uint32_t lastRunTime_;
uint32_t deltaTime_;
uint32_t gifDataSize_;
uint8_t* gifImageData_ = nullptr;
const char* src_;
};
void GifImageAnimator::OpenGifFile(const char* src)
{
int error = D_GIF_SUCCEEDED;
GifFileType* gifFileType = DGifOpenFileName(src, &error);
if (error != D_GIF_SUCCEEDED) {
return;
}
DGifSlurp(gifFileType);
uint8_t pixelByteSize = DrawUtils::GetPxSizeByColorMode(ARGB8888) >> 3;
gifDataSize_ = gifFileType->SWidth * gifFileType->SHeight * pixelByteSize;
gifImageData_ = static_cast<uint8_t*>(UIMalloc(gifDataSize_));
if (gifImageData_ == nullptr) {
CloseGifFile();
return;
}
SetGifFileType(gifFileType);
}
void GifImageAnimator::CloseGifFile()
{
GifFileType* gifFileType = GetGifFileType();
if (gifFileType != nullptr) {
DGifCloseFile(gifFileType, nullptr);
}
if (gifImageData_ != nullptr) {
UIFree(reinterpret_cast<void*>(const_cast<uint8_t*>(gifImageData_)));
gifImageData_ = nullptr;
}
}
void GifImageAnimator::Callback(UIView* view)
{
if (view == nullptr) {
return;
}
UIImageView* imageView = static_cast<UIImageView*>(view);
uint32_t curTime = GetRunTime();
if (curTime != 0) {
if (curTime + deltaTime_ - lastRunTime_ >= delayTime_) {
deltaTime_ = curTime + deltaTime_ - lastRunTime_ - delayTime_;
lastRunTime_ = curTime;
} else {
return;
}
}
GifFileType* gifFileType = GetGifFileType();
if (gifFileType != nullptr) {
delayTime_ = SetGifFrame(gifFileType, imageIndex_, imageView);
imageIndex_ = (imageIndex_ < gifFileType->ImageCount - 1) ? (imageIndex_ + 1) : 0;
}
}
uint32_t GifImageAnimator::SetGifFrame(GifFileType* gifFileType, int32_t imageIndex, UIImageView* imageView) const
{
SavedImage* savedImage = &(gifFileType->SavedImages[imageIndex]);
if (savedImage == nullptr) {
return 0;
}
GifImageDesc* gifImageDesc = &(savedImage->ImageDesc);
if (gifImageDesc == nullptr) {
return 0;
}
GraphicsControlBlock gcb;
int32_t ret = DGifSavedExtensionToGCB(gifFileType, imageIndex, &gcb);
if (ret != GIF_OK) {
return 0;
}
ColorMapObject* colorMap = nullptr;
if (gifImageDesc->ColorMap != nullptr) {
colorMap = gifImageDesc->ColorMap;
} else {
colorMap = gifFileType->SColorMap;
}
DealGifImageData(gifFileType, gifImageDesc, savedImage, gcb, colorMap);
if (gifImageData_ == nullptr) {
return 0;
}
imageView->gifFrameFlag_ = true;
ImageInfo gifFrame;
gifFrame.header.width = gifFileType->SWidth;
gifFrame.header.height = gifFileType->SHeight;
gifFrame.header.colorMode = ARGB8888;
gifFrame.dataSize = gifDataSize_;
gifFrame.data = gifImageData_;
imageView->SetSrc(&gifFrame);
if (gcb.DelayTime >= 0) {
return static_cast<uint32_t>(gcb.DelayTime) * 10;
} else {
return 0;
}
}
void GifImageAnimator::DealGifImageData(const GifFileType* gifFileType,
const GifImageDesc* gifImageDesc,
const SavedImage* savedImage,
GraphicsControlBlock gcb,
const ColorMapObject* colorMap) const
{
if ((gifFileType == nullptr) || (gifImageDesc == nullptr) || (savedImage == nullptr) ||
(savedImage->RasterBits == nullptr) || (colorMap == nullptr) || (colorMap->Colors == nullptr)) {
return;
}
uint8_t colorIndex = 0;
GifColorType* gifColorType = nullptr;
uint32_t index = 0;
for (int32_t x = 0; x < gifFileType->SHeight; x++) {
for (int32_t y = 0; y < gifFileType->SWidth; y++) {
bool transparentColor = true;
if ((x >= gifImageDesc->Top) && (x < gifImageDesc->Top + gifImageDesc->Height) &&
(y >= gifImageDesc->Left) && (y < gifImageDesc->Left + gifImageDesc->Width)) {
int32_t loc = (x - gifImageDesc->Top) * gifImageDesc->Width + (y - gifImageDesc->Left);
colorIndex = savedImage->RasterBits[loc];
if ((gcb.DisposalMode != DISPOSE_DO_NOT) || (gcb.TransparentColor == NO_TRANSPARENT_COLOR) ||
(colorIndex != gcb.TransparentColor)) {
transparentColor = false;
}
}
if (transparentColor) {
index += 4;
} else {
gifColorType = &colorMap->Colors[colorIndex];
gifImageData_[index++] = gifColorType->Blue;
gifImageData_[index++] = gifColorType->Green;
gifImageData_[index++] = gifColorType->Red;
gifImageData_[index++] = OPA_OPAQUE;
}
}
}
}
#endif
UIImageView::UIImageView()
: imageWidth_(0),
imageHeight_(0),
autoEnable_(true),
needRefresh_(false),
colorFormat_(UNKNOWN),
blurLevel_(BlurLevel::LEVEL0),
algorithm_(TransformAlgorithm::BILINEAR),
reserve_(0)
{
style_ = &(StyleDefault::GetBackgroundTransparentStyle());
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
gifImageAnimator_ = nullptr;
gifFrameFlag_ = false;
#endif
}
UIImageView::~UIImageView()
{
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
RemoveAndStopGifAnimator();
#endif
if (drawTransMap_ != nullptr) {
delete drawTransMap_;
drawTransMap_ = nullptr;
}
if (contentMatrix_ != nullptr) {
delete contentMatrix_;
contentMatrix_ = nullptr;
}
}
void UIImageView::SetResizeMode(ImageResizeMode mode)
{
if (autoEnable_) {
imageResizeMode_ = mode;
} else if (imageResizeMode_ != mode) {
needRefresh_ = true;
ReMeasure();
imageResizeMode_ = mode;
UpdateDrawTransMap(true);
}
}
void UIImageView::AdjustScaleAndTranslate(Vector3<float>& scale, Vector3<int16_t>& translate,
int16_t widgetWidth, int16_t widgetHeight) const
{
float ratio = 1.0f;
switch (imageResizeMode_) {
case ImageResizeMode::COVER:
ratio = MATH_MAX(scale.x_, scale.y_);
break;
case ImageResizeMode::CONTAIN:
ratio = MATH_MIN(scale.x_, scale.y_);
break;
case ImageResizeMode::CENTER:
break;
case ImageResizeMode::SCALE_DOWN:
ratio = MATH_MIN(scale.x_, scale.y_);
ratio = MATH_MIN(ratio, 1.0f);
break;
case ImageResizeMode::FILL:
return;
default:
break;
}
if (scale.x_ != ratio) {
scale.x_ = ratio;
translate.x_ += (static_cast<float>(widgetWidth) - static_cast<float>(imageWidth_) * ratio) * 0.5f;
}
if (scale.y_ != ratio) {
scale.y_ = ratio;
translate.y_ += (static_cast<float>(widgetHeight) - static_cast<float>(imageHeight_) * ratio) * 0.5f;
}
}
void UIImageView::UpdateContentMatrix()
{
Rect viewRect = GetOrigRect();
if (autoEnable_ || (imageResizeMode_ == ImageResizeMode::NONE) ||
(imageWidth_ == viewRect.GetWidth() && imageHeight_ == viewRect.GetHeight()) ||
imageWidth_ == 0 || imageHeight_ == 0) {
if (contentMatrix_ != nullptr) {
delete contentMatrix_;
contentMatrix_ = nullptr;
}
return;
}
if (contentMatrix_ == nullptr) {
contentMatrix_ = new Matrix4<float>();
if (contentMatrix_ == nullptr) {
GRAPHIC_LOGE("can not new contentMatrix");
return;
}
}
int16_t widgetWidth = viewRect.GetWidth() - style_->paddingLeft_ - style_->paddingRight_ -
style_->borderWidth_ * 2;
int16_t widgetHeight = viewRect.GetHeight() - style_->paddingTop_ - style_->paddingBottom_ -
style_->borderWidth_ * 2;
float scaleX = static_cast<float>(widgetWidth) / static_cast<float>(imageWidth_);
float scaleY = static_cast<float>(widgetHeight) / static_cast<float>(imageHeight_);
Vector3<float> scale(scaleX, scaleY, 1.0f);
Vector3<int16_t> translate(style_->paddingLeft_ + style_->borderWidth_,
style_->paddingTop_ + style_->borderWidth_, 0);
AdjustScaleAndTranslate(scale, translate, widgetWidth, widgetHeight);
auto scaleMatrix = Matrix4<float>::Scale(scale, Vector3<float>(viewRect.GetX(), viewRect.GetY(), 0));
auto translateMatrix = Matrix4<float>::Translate(Vector3<float>(translate.x_, translate.y_, 0));
*contentMatrix_ = translateMatrix * scaleMatrix;
}
void UIImageView::UpdateDrawTransMap(bool updateContentMatrix)
{
auto viewRect = GetOrigRect();
if (updateContentMatrix || (drawTransMap_ != nullptr &&
(drawTransMap_->GetTransMapRect().GetX() != viewRect.GetX() ||
drawTransMap_->GetTransMapRect().GetY() != viewRect.GetY()))) {
UpdateContentMatrix();
}
if ((contentMatrix_ == nullptr) && ((transMap_ == nullptr) || transMap_->IsInvalid())) {
if (drawTransMap_ != nullptr) {
delete drawTransMap_;
drawTransMap_ = nullptr;
}
return;
}
if (drawTransMap_ == nullptr) {
drawTransMap_ = new TransformMap();
if (drawTransMap_ == nullptr) {
GRAPHIC_LOGE("can not new drawTransMap");
return;
}
}
if (contentMatrix_ != nullptr) {
drawTransMap_->SetTransMapRect(Rect(viewRect.GetX(), viewRect.GetY(),
viewRect.GetX() + imageWidth_ - 1, viewRect.GetY() + imageHeight_ - 1));
} else {
drawTransMap_->SetTransMapRect(viewRect);
}
if (transMap_ == nullptr || transMap_->IsInvalid()) {
if (contentMatrix_ == nullptr) {
GRAPHIC_LOGE("Text: UpdateDrawTransMap contentMatrix_ is nullptr");
return;
}
drawTransMap_->SetMatrix(*contentMatrix_);
return;
}
if (!(transMap_->GetTransMapRect() == viewRect)) {
transMap_->SetTransMapRect(viewRect);
}
if (contentMatrix_ == nullptr) {
*drawTransMap_ = *transMap_;
return;
}
auto rect = transMap_->GetTransMapRect();
auto translate = Matrix4<float>::Translate(Vector3<float>(-rect.GetX(), -rect.GetY(), 0));
auto matrix = transMap_->GetTransformMatrix() * translate;
matrix = matrix * (*contentMatrix_);
drawTransMap_->SetMatrix(matrix);
}
void UIImageView::SetHeight(int16_t height)
{
if (GetHeight() != height) {
UIView::SetHeight(height);
UpdateDrawTransMap(true);
}
}
void UIImageView::SetWidth(int16_t width)
{
if (GetWidth() != width) {
UIView::SetWidth(width);
UpdateDrawTransMap(true);
}
}
bool UIImageView::OnPreDraw(Rect& invalidatedArea) const
{
if ((image_.GetSrcType() == IMG_SRC_UNKNOWN)) {
return true;
}
if ((colorFormat_ == RGB565) || (colorFormat_ == RGB888)) {
if (GetRect().IsContains(invalidatedArea)) {
return true;
}
invalidatedArea.Intersect(invalidatedArea, GetRect());
}
return false;
}
void UIImageView::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
OpacityType opa = GetMixOpaScale();
BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
baseGfxEngine->DrawRect(gfxDstBuffer, GetRect(), invalidatedArea, *style_, opa);
if ((imageHeight_ == 0) || (imageWidth_ == 0)) {
return;
}
UpdateDrawTransMap();
Rect viewRect = GetContentRect();
Rect trunc(invalidatedArea);
if (trunc.Intersect(trunc, viewRect)) {
uint8_t srcType = image_.GetSrcType();
if ((srcType == IMG_SRC_FILE) || (srcType == IMG_SRC_VARIABLE)) {
Rect cordsTmp;
cordsTmp.SetTop(viewRect.GetY());
cordsTmp.SetBottom(viewRect.GetY() + imageHeight_ - 1);
if ((drawTransMap_ == nullptr) || drawTransMap_->IsInvalid()) {
SetCordsTmpRect(gfxDstBuffer, viewRect, trunc, cordsTmp, opa);
} else if ((drawTransMap_ != nullptr) && !drawTransMap_->IsInvalid()) {
ImageInfo imgInfo;
if (srcType == IMG_SRC_FILE) {
CacheEntry entry;
RetCode ret = CacheManager::GetInstance().Open(GetPath(), *style_, entry);
if (ret != RetCode::OK) {
return;
}
imgInfo = entry.GetImageInfo();
} else {
imgInfo = *(GetImageInfo());
}
uint8_t pxSize = DrawUtils::GetPxSizeByColorMode(imgInfo.header.colorMode);
TransformDataInfo imageTranDataInfo = {imgInfo.header, imgInfo.data, pxSize,
static_cast<BlurLevel>(blurLevel_),
static_cast<TransformAlgorithm>(algorithm_)};
OpacityType opaScale = DrawUtils::GetMixOpacity(opa, style_->imageOpa_);
Matrix4<float> scaleMatrix = drawTransMap_->GetScaleMatrix();
int16_t paddingX = style_->paddingLeft_ * scaleMatrix[0][0];
int16_t paddingY = style_->paddingTop_ * scaleMatrix[1][1];
baseGfxEngine->DrawTransform(gfxDstBuffer, trunc, {paddingX, paddingY}, Color::Black(),
opaScale, *drawTransMap_, imageTranDataInfo);
}
}
}
}
void UIImageView::SetCordsTmpRect(BufferInfo& gfxDstBuffer, Rect& viewRect, Rect& trunc,
Rect& cordsTmp, OpacityType opa)
{
while (cordsTmp.GetTop() <= viewRect.GetBottom()) {
cordsTmp.SetLeft(viewRect.GetX());
cordsTmp.SetRight(viewRect.GetX() + imageWidth_ - 1);
while (cordsTmp.GetLeft() <= viewRect.GetRight()) {
image_.DrawImage(gfxDstBuffer, cordsTmp, trunc, *style_, opa);
cordsTmp.SetLeft(cordsTmp.GetLeft() + imageWidth_);
cordsTmp.SetRight(cordsTmp.GetRight() + imageWidth_);
}
cordsTmp.SetTop(cordsTmp.GetTop() + imageHeight_);
cordsTmp.SetBottom(cordsTmp.GetBottom() + imageHeight_);
}
}
void UIImageView::SetSrc(const char* src)
{
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
if (src == nullptr) {
return;
}
const static uint8_t IMG_BYTES_TO_CHECK = 4;
char buf[IMG_BYTES_TO_CHECK] = {0};
int32_t fd = open(src, O_RDONLY);
if (fd < 0) {
return;
}
if (read(fd, buf, IMG_BYTES_TO_CHECK) != IMG_BYTES_TO_CHECK) {
close(fd);
return;
}
close(fd);
bool updated = false;
RemoveAndStopGifAnimator();
if ((static_cast<uint8_t>(buf[0]) == 0x47) && (static_cast<uint8_t>(buf[1]) == 0x49) &&
(static_cast<uint8_t>(buf[2]) == 0x46)) {
if (gifImageAnimator_ == nullptr) {
gifImageAnimator_ = new GifImageAnimator(this, src);
if (gifImageAnimator_ == nullptr) {
GRAPHIC_LOGE("new GifImageAnimator fail");
return;
}
}
AddAndStartGifAnimator();
updated = true;
} else {
updated = image_.SetSrc(src);
}
#else
bool updated = image_.SetSrc(src);
#endif
if (!updated) {
return;
}
needRefresh_ = true;
if (autoEnable_ || (imageResizeMode_ != ImageResizeMode::NONE)) {
UIImageView::ReMeasure();
}
if (imageResizeMode_ != ImageResizeMode::NONE) {
UpdateDrawTransMap(true);
}
Invalidate();
}
void UIImageView::ReMeasure()
{
if (!needRefresh_) {
return;
}
needRefresh_ = false;
ImageHeader header = {0};
image_.GetHeader(header);
imageWidth_ = header.width;
imageHeight_ = header.height;
colorFormat_ = header.colorMode;
if (autoEnable_) {
Invalidate();
Resize(imageWidth_, imageHeight_);
Invalidate();
}
}
void UIImageView::SetSrc(const ImageInfo* src)
{
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
if (!gifFrameFlag_ && (gifImageAnimator_ != nullptr)) {
RemoveAndStopGifAnimator();
}
gifFrameFlag_ = false;
#endif
bool updated = image_.SetSrc(src);
if (!updated) {
return;
}
needRefresh_ = true;
if (autoEnable_ || (imageResizeMode_ != ImageResizeMode::NONE)) {
UIImageView::ReMeasure();
}
if (imageResizeMode_ != ImageResizeMode::NONE) {
UpdateDrawTransMap(true);
}
Invalidate();
}
#if defined(ENABLE_GIF) && (ENABLE_GIF == 1)
void UIImageView::AddAndStartGifAnimator()
{
if (gifImageAnimator_ != nullptr) {
gifImageAnimator_->Start();
}
}
void UIImageView::RemoveAndStopGifAnimator()
{
if (gifImageAnimator_ != nullptr) {
gifImageAnimator_->Stop();
delete gifImageAnimator_;
gifImageAnimator_ = nullptr;
}
}
#endif
}