轻鸿蒙 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的渲染过程会处理属性时的调用方法如下:
- Component::Render()
- Component::ParseOptions()
- 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);
去显示图片