> 文档中心 > 轻鸿蒙 Image的绘制流程

轻鸿蒙 Image的绘制流程


1.image标签中src的引入

补一下js的UI流程
在这里插入图片描述

在js层我们通过api中src属性指定一张图片的路径来显示图片,在C++层,组件image_component 通过在 ImageComponent::SetPrivateAttribute() 中的imageView_来设置src属性的。具体代码如下:

// src\core\components\image_component.cppbool ImageComponent::SetPrivateAttribute(uint16_t attrKeyId, jerry_value_t attrValue){    bool setResult = true;    switch (attrKeyId) { case K_SRC: {     char *src = const_cast<char *>(ParseImageSrc(attrValue));     imageView_.SetSrc(src);     ACE_FREE(src);     break; } default:     setResult = false;     break;    }    return setResult;}

那么这个设置私有属性又是在哪里调用的呢,这里就要先了解一下component的渲染过程,component的渲染过程会处理属性时的调用方法如下:

  1. Component::Render()
  2. Component::ParseOptions()
  3. Component::ParseAttrs()

在ParseAttrs(),通过加载attrValue,最后,调用了 SetAttribute(attrKeyId, newAttrValue); 函数,代码如下:

bool Component::SetAttribute(uint16_t attrKeyId, jerry_value_t attrValue){    UIView *uiView = GetComponentRootView();    if ((uiView == nullptr) || !KeyParser::IsKeyValid(attrKeyId) || IS_UNDEFINED(attrValue)) { return false;    }    // try private first    bool setResult = `SetPrivateAttribute`(attrKeyId, attrValue);    if (!setResult) { // this means no private attributes matches, so need to try private ones setResult = SetCommonAttribute(*uiView, attrKeyId, attrValue);    }    return setResult;}

也就是说,当我们的组件渲染的时候,父组件component会优先调用虚函数SetPrivateAttribute,再设置公共属性SetCommonAttribute

2.UIImageView 的src函数

在这里我有个疑问:代码中有关于 GifImageAnimator的使用,不知道为什么js层没有相关的api使用。接着来说UIImageView 的SetSrc(const char* src)函数,具体代码如下:

//graphic\ui\frameworks\components\ui_image_view.cppvoid UIImageView::SetSrc(const char* src){#if (ENABLE_GIF == 1)    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();}

这里面定义了src的内容是gif的情况,我们暂不讨论这种情况,这里只讨论,单张图片的情况,上面未定义的应该不会执行,最后调用了:Image的 updated = image_.SetSrc(src);的方法,最后,UIImageView重新测量,最后在绘制

3.image的setSrc函数

那么上面的Image是个什么东西,接着往下看,它继承HeapBase。它的setSrc方法如下:

//graphic\ui\frameworks\common\image.cppbool Image::SetSrc(const char* src){    if (path_ != nullptr) { UIFree(reinterpret_cast<void*>(const_cast<char*>(path_))); path_ = nullptr;    }    if (src != nullptr) { uint32_t imageType = ImageDecodeAbility::GetInstance().GetImageDecodeAbility(); if (((imageType & IMG_SUPPORT_JPEG) == IMG_SUPPORT_JPEG) ||     ((imageType & IMG_SUPPORT_PNG) == IMG_SUPPORT_PNG)) {     return SetStandardSrc(src); } return SetLiteSrc(src);    }    srcType_ = IMG_SRC_UNKNOWN;    return true;}

这里判断了图片的类型,如果是 jpeg或者 png就用SetStandardSrc()而在改该方法下又对png和jepg,这里就以png图片为模板介绍,方法如下:

bool Image::SetPNGSrc(const char* src){    srcType_ = IMG_SRC_UNKNOWN;    png_bytep* rowPointer = nullptr;    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);    if (png == nullptr) { return false;    }    png_infop info = png_create_info_struct(png);    if (info == nullptr) { png_destroy_read_struct(&png, &info, nullptr); return false;    }    FILE* infile = fopen(src, "rb");    if (infile == nullptr) { GRAPHIC_LOGE("can't open %s\n", src); png_destroy_read_struct(&png, &info, nullptr); return false;    }    png_init_io(png, infile);    png_read_info(png, info);    uint8_t pixelByteSize = DrawUtils::GetPxSizeByColorMode(ARGB8888) >> 3; // 3: Shift right 3 bits    uint16_t width = png_get_image_width(png, info);    uint16_t height = png_get_image_height(png, info);    uint8_t colorType = png_get_color_type(png, info);    uint8_t bitDepth = png_get_bit_depth(png, info);    uint32_t dataSize = height * width * pixelByteSize;    if ((colorType == PNG_COLOR_TYPE_GRAY) && (bitDepth < 8)) { // 8: Expand grayscale images to the full 8 bits png_set_expand_gray_1_2_4_to_8(png);    }    if ((colorType == PNG_COLOR_TYPE_GRAY) || (colorType == PNG_COLOR_TYPE_GRAY_ALPHA)) { png_set_gray_to_rgb(png);    }    if (colorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png);    }    if (bitDepth == 16) { // 16: Chop 16-bit depth images to 8-bit depth png_set_strip_16(png);    }    if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png);    }    if (!(colorType & PNG_COLOR_MASK_ALPHA)) { png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);    }    png_set_interlace_handling(png);    png_read_update_info(png, info);    rowPointer = MallocPngBytep(height, png_get_rowbytes(png, info));    if (rowPointer == nullptr) { fclose(infile); png_destroy_read_struct(&png, &info, nullptr); return false;    }    png_read_image(png, rowPointer);    fclose(infile);    png_destroy_read_struct(&png, &info, nullptr);    ImageInfo* imgInfo = static_cast<ImageInfo*>(UIMalloc(sizeof(ImageInfo)));    if (imgInfo == nullptr) { FreePngBytep(&rowPointer, height); return false;    }    uint8_t* srcData = static_cast<uint8_t*>(UIMalloc(dataSize));    if (srcData == nullptr) { FreePngBytep(&rowPointer, height); UIFree(imgInfo); return false;    }     uint32_t n = 0;    for (uint16_t y = 0; y < height; y++) { png_bytep row = rowPointer[y]; for (uint16_t x = 0; x < width * pixelByteSize; x += pixelByteSize) {     srcData[n++] = row[x + 2]; // 2: B channel     srcData[n++] = row[x + 1]; // 1: G channel     srcData[n++] = row[x + 0]; // 0: R channel     srcData[n++] = row[x + 3]; // 3: Alpha channel }    }    FreePngBytep(&rowPointer, height);    imgInfo->header.width = width;    imgInfo->header.height = height;    imgInfo->header.colorMode = ARGB8888;    imgInfo->dataSize = dataSize;    imgInfo->data = srcData;    ReInitImageInfo(imgInfo, true);    srcType_ = IMG_SRC_VARIABLE;    return true;}

这个方法里的代码有点多,是C代码,由文件的路径得到 C层定义的一个文件,最后通过遍历图片的像素,将对应的像素点的值存储在srcData中,最后赋值给 imgInfo 同时将srcType赋值为IMG_SRC_VARIABLE,这里可以看到,当它的值改变后,在Image::DrawImage 函数中会调用DrawImage::DrawCommon方法,具体代码如下:

//graphic\ui\frameworks\draw\draw_image.cppvoid DrawImage::DrawCommon(BufferInfo& gfxDstBuffer, const Rect& coords, const Rect& mask,    const ImageInfo* img, const Style& style, uint8_t opaScale){    if (img == nullptr) { return;    }    OpacityType opa = DrawUtils::GetMixOpacity(opaScale, style.imageOpa_);    uint8_t pxBitSize = DrawUtils::GetPxSizeByColorMode(img->header.colorMode);    DrawUtils::GetInstance()->DrawImage(gfxDstBuffer, coords, mask, img->data, opa, pxBitSize,     static_cast<ColorMode>(img->header.colorMode));}

其实是调用了单例 DrawUtils 的DrawImage方法去实现的,代码如下:

//graphic\ui\frameworks\draw\draw_utils.cppvoid DrawUtils::DrawImage(BufferInfo& gfxDstBuffer,     const Rect& area,     const Rect& mask,     const uint8_t* image,     OpacityType opa,     uint8_t pxBitSize,     ColorMode colorMode) const{    if (image == nullptr) { return;    }    Rect maskedArea;    if (!maskedArea.Intersect(area, mask)) { return;    }    int16_t mapWidth = area.GetWidth();    int16_t imageX = maskedArea.GetLeft() - area.GetLeft();    int16_t imageY = maskedArea.GetTop() - area.GetTop();    uint32_t imageWidthInByte = (static_cast<uint32_t>(mapWidth) * pxBitSize) >> SHIFT_3;    if ((mapWidth * pxBitSize) & 0x7) { // 0x7 : less than 1 byte is counted as 1 byte imageWidthInByte++;    }    BufferInfo src;src.rect = {imageX, imageY, static_cast<int16_t>(imageX + maskedArea.GetWidth() - 1),  static_cast<int16_t>(imageY + maskedArea.GetHeight() - 1)};    src.virAddr = static_cast<void*>(const_cast<uint8_t*>(image));    src.stride = imageWidthInByte;    src.mode = colorMode;    src.color = 0;    Point dstPos = {maskedArea.GetLeft(), maskedArea.GetTop()};    BlendOption blendOption;    blendOption.opacity = opa;    BaseGfxEngine::GetInstance()->Blit(gfxDstBuffer, dstPos, src, maskedArea, blendOption);}

最后通过 BaseGfxEngine::GetInstance()->Blit(gfxDstBuffer, dstPos, src, maskedArea, blendOption); 去显示图片