Files
graphic_ui/frameworks/components/ui_image_view.cpp
T
2021-03-11 18:38:06 +08:00

424 lines
13 KiB
C++
Executable File

/*
* 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 "draw/draw_rect.h"
#include "file.h"
#include "image_info.h"
#include "imgdecode/cache_manager.h"
#include "mem_api.h"
#ifndef VERSION_LITE
#include "gif_lib.h"
#endif
namespace OHOS {
#ifndef VERSION_LITE
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;
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_;
};
const void GifImageAnimator::OpenGifFile(const char* src)
{
int32_t error = D_GIF_SUCCEEDED;
GifFileType* gifFileType = DGifOpenFileName(src, &error);
if (error != D_GIF_SUCCEEDED) {
return;
}
DGifSlurp(gifFileType);
/* 3 : when change single pixel to byte, the buffer should divided by 8, equal to shift right 3 bits. */
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; // 10: change hundredths (1/100) of a second to millisecond
} 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;
bool transparentColor = true;
int32_t loc = 0;
for (int32_t x = 0; x < gifFileType->SHeight; x++) {
for (int32_t y = 0; y < gifFileType->SWidth; y++) {
transparentColor = true;
if ((x >= gifImageDesc->Top) && (x < gifImageDesc->Top + gifImageDesc->Height) &&
(y >= gifImageDesc->Left) && (y < gifImageDesc->Left + gifImageDesc->Width)) {
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; // 4: skip color index, keep last frame color
} 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_(UNKNOW),
blurLevel_(BlurLevel::LEVEL0),
algorithm_(TransformAlgorithm::BILINEAR),
reserve_(0)
{
style_ = &(StyleDefault::GetBackgroundTransparentStyle());
#ifndef VERSION_LITE
gifImageAnimator_ = nullptr;
gifFrameFlag_ = false;
#endif
}
UIImageView::~UIImageView()
{
#ifndef VERSION_LITE
RemoveAndStopGifAnimator();
#endif
}
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(const Rect& invalidatedArea)
{
OpacityType opa = GetMixOpaScale();
DrawRect::Draw(GetRect(), invalidatedArea, *style_, opa);
if ((imageHeight_ == 0) || (imageWidth_ == 0)) {
return;
}
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 ((transMap_ == nullptr) || transMap_->IsInvalid()) {
while (cordsTmp.GetTop() <= viewRect.GetBottom()) {
cordsTmp.SetLeft(viewRect.GetX());
cordsTmp.SetRight(viewRect.GetX() + imageWidth_ - 1);
while (cordsTmp.GetLeft() <= viewRect.GetRight()) {
image_.DrawImage(cordsTmp, trunc, *style_, opa);
cordsTmp.SetLeft(cordsTmp.GetLeft() + imageWidth_);
cordsTmp.SetRight(cordsTmp.GetRight() + imageWidth_);
}
cordsTmp.SetTop(cordsTmp.GetTop() + imageHeight_);
cordsTmp.SetBottom(cordsTmp.GetBottom() + imageHeight_);
}
} else if ((transMap_ != nullptr) && !transMap_->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_)};
Rect origRect = GetOrigRect();
transMap_->SetTransMapRect(origRect);
OpacityType opaScale = DrawUtils::GetMixOpacity(opa, style_->imageOpa_);
DrawUtils::GetInstance()->DrawTransform(invalidatedArea, {0, 0}, Color::Black(), opaScale, *transMap_,
imageTranDataInfo);
}
}
}
}
void UIImageView::SetSrc(const char* src)
{
#ifndef VERSION_LITE
if (src == nullptr) {
return;
}
const static uint8_t IMG_BYTES_TO_CHECK = 4; // 4: check 4 bytes of image file
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();
// 0x47 0x49 0x46: GIF file's header
if ((static_cast<uint8_t>(buf[0]) == 0x47) && (static_cast<uint8_t>(buf[1]) == 0x49) &&
(static_cast<uint8_t>(buf[2]) == 0x46)) { // 2: array index of GIF file's header
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_) {
UIImageView::ReMeasure();
}
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_) {
Resize(imageWidth_, imageHeight_);
Invalidate();
}
}
void UIImageView::SetSrc(const ImageInfo* src)
{
#ifndef VERSION_LITE
if (!gifFrameFlag_ && (gifImageAnimator_ != nullptr)) {
RemoveAndStopGifAnimator();
}
gifFrameFlag_ = false;
#endif
bool updated = image_.SetSrc(src);
if (!updated) {
return;
}
needRefresh_ = true;
if (autoEnable_) {
UIImageView::ReMeasure();
}
Invalidate();
}
#ifndef VERSION_LITE
void UIImageView::AddAndStartGifAnimator()
{
if (gifImageAnimator_ != nullptr) {
AnimatorManager::GetInstance()->Add(gifImageAnimator_);
gifImageAnimator_->Start();
}
}
void UIImageView::RemoveAndStopGifAnimator()
{
if (gifImageAnimator_ != nullptr) {
gifImageAnimator_->Stop();
AnimatorManager::GetInstance()->Remove(gifImageAnimator_);
delete gifImageAnimator_;
gifImageAnimator_ = nullptr;
}
}
#endif
} // namespace OHOS