写点什么

PAG 动效框架源码笔记 (二)层级视图

作者:olinone
  • 2023-05-16
    上海
  • 本文字数:3201 字

    阅读完需:约 11 分钟

转载请注明出处:http://www.olinone.com/


如上章所言,特效播放主要包括应用逻辑处理和图形渲染两个阶段,其中,逻辑处理又可以看做模型对象的定义与流转

模型分层

PAG 框架模型大致可以分为三部分:


1、组件(PAGCompositon)

PAG 框架支持多文件多图层渲染,PAGCompositon 组件可以同时容纳多个 PAGFile 文件,每个 PAGFile 文件又可以包含多个 Layer 组件


PAGFile 解析自 File 源文件,其中 Layer 组件可以支持元素替换,比如可以替换 PAGImageLayer 中 PAGImage 资源对象,从而实现自定义融合元素

2、图层(PAGStage)

PAGStage 承载了完整的组件图层,记录了每个组件及其资源对象的映射关系,比如 PAGImage 及其关联的 PAGImageLayer 对象,实现通过资源对象查找对应 Layer 对象的能力


此外,PAGStage 通过 SequenceCache 还缓存了资源对象与 Graphic 视图对象的映射关系,类似于渲染缓存的职责

3、图形(LayerGraphic)

组件模块加上时间戳,就生成对应时刻的图形对象 Graphic,比如纹理图形 Picture,或者文本图形 Text


LayerGraphic 作为图形模型的容器,继承自 ComposeGraphic 对象,包含当前播放时刻所有的图像对象,每一个图形对象可以通过装饰器添加裁切或者蒙版等多种处理效果

源码浅析

1、File 文件解析


// 文件二进制解析std::shared_ptr<File> File::Load(const void* bytes, size_t length, const std::string& filePath, const std::string&) {file = Codec::Decode(bytes, static_cast<uint32_t>(length), filePath);...return file;}
// File对象初始化File::File(std::vector<Composition*> compositionList, std::vector<pag::ImageBytes*> imageList) : images(std::move(imageList)), compositions(std::move(compositionList)) {// 每一个File文件都有对应的mainComposition对象,组件元信息其实都存在compositon里面mainComposition = compositions.back();rootLayer = PreComposeLayer::Wrap(mainComposition).release();...}
复制代码


2、PAGFile 构造


// 通过File构造PAGFilestd::shared_ptr<PAGFile> PAGFile::MakeFrom(std::shared_ptr<File> file) {  // 解析File构造组件模型  auto pagLayer = BuildPAGLayer(file, file->getRootLayer());  pagLayer->gotoTime(0);  auto pagFile = std::static_pointer_cast<PAGFile>(pagLayer);  ...  return pagFile;}
// 每一个组件对象其实都持有原始File对象std::shared_ptr<PAGLayer> PAGFile::BuildPAGLayer(std::shared_ptr<File> file, Layer* layer) { PAGLayer* pagLayer; switch (layer->type()) { case LayerType::Text: { pagLayer = new PAGTextLayer(file, static_cast<TextLayer*>(layer)); } break; case LayerType::Image: { pagLayer = new PAGImageLayer(file, static_cast<ImageLayer*>(layer)); } break; case LayerType::PreCompose: { ... if (composition->type() == CompositionType::Vector) { auto& layers = static_cast<VectorComposition*>(composition)->layers; // 遍历组件列表 for (int i = static_cast<int>(layers.size()) - 1; i >= 0; i--) { auto childLayer = layers[i]; auto childPAGLayer = BuildPAGLayer(file, childLayer); ... } } } break; }}
复制代码


3、PAGStage 图层填充


// PAGComposition可以容纳多个PAGFile- (PAGComposition *)makeComposition {    PAGComposition* compostion = [PAGComposition Make:self.view.bounds.size];    PAGFile* file = [PAGFile Load:[[NSBundle mainBundle] pathForResource:@"data-TimeStretch" ofType:@"pag"]];    // 可以替换PAGImage资源对象,底层其实是操作PAGImageLayer    [file replaceImage:0 data:[PAGImage FromPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"png"]]];    [compostion addLayer:file];        file = [PAGFile Load:[[NSBundle mainBundle] pathForResource:@"data_video" ofType:@"pag"]];    [compostion addLayer:file atIndex:0];    return compostion;}
void PAGPlayer::setComposition(std::shared_ptr<PAGComposition> newComposition) { ... pagComposition = newComposition; if (pagComposition) { // 填充容器 stage->doAddLayer(pagComposition, 0); }}
// 建立索引缓存void PAGStage::addReference(PAGLayer* pagLayer) { addToReferenceMap(pagLayer->uniqueID(), pagLayer); addToReferenceMap(pagLayer->layer->uniqueID, pagLayer); if (pagLayer->layerType() == LayerType::PreCompose) { auto composition = static_cast<PreComposeLayer*>(pagLayer->layer)->composition; addToReferenceMap(composition->uniqueID, pagLayer); } else if (pagLayer->layerType() == LayerType::Image) { auto imageBytes = static_cast<ImageLayer*>(pagLayer->layer)->imageBytes; addToReferenceMap(imageBytes->uniqueID, pagLayer); auto pagImage = static_cast<PAGImageLayer*>(pagLayer)->getPAGImage(); if (pagImage != nullptr) { addReference(pagImage.get(), pagLayer); } } ...}
// 可以通过资源ID查找渲染缓存std::shared_ptr<Graphic> PAGStage::getSequenceGraphic(Composition* composition, Frame compositionFrame) { ... SequenceCache cache = {}; cache.graphic = RenderSequenceComposition(composition, compositionFrame); cache.compositionFrame = compositionFrame; sequenceCache[composition->uniqueID] = cache; return cache.graphic;}
复制代码


4、Graphic 图形生成


// 播放进度void PAGPlayer::setProgress(double percent) {  auto pagComposition = stage->getRootComposition();  ...  pagComposition->setProgressInternal(realProgress);}
// 生成图形void PAGPlayer::prepareInternal() { renderCache->beginFrame(); auto result = updateStageSize(); if (result && contentVersion != stage->getContentVersion()) { contentVersion = stage->getContentVersion(); Recorder recorder = {}; // 通过recorder记录每个可绘制组件Layer stage->draw(&recorder); // 导出所有图形对象 lastGraphic = recorder.makeGraphic(); }}
// recorder类似于二叉树,记录了每个Layer组件当前时刻对应的Graphicvoid PAGComposition::draw(Recorder* recorder) { ... auto composition = preComposeLayer->composition; if (composition->type() == CompositionType::Bitmap || composition->type() == CompositionType::Video) { auto layerFrame = layer->startTime + contentFrame; auto compositionFrame = preComposeLayer->getCompositionFrame(layerFrame); auto graphic = stage->getSequenceGraphic(composition, compositionFrame); recorder->drawGraphic(graphic); } ... if (hasClip()) { // 裁切装饰器 recorder->saveClip(0, 0, static_cast<float>(_width), static_cast<float>(_height)); } // 堆栈模式处理每个视图及其子视图,保证每个视图及其子视图渲染环境一致性,比如matrix变化等 for (int i = 0; i < count; i++) { DrawChildLayer(recorder, childLayer.get()); } if (hasClip()) { recorder->restore(); }}
复制代码

总结

为了支持多文件多图层渲染,PAG 框架设计了一套完整的框架模型,其复杂的对象继承关系,加深了代码阅读理解难度,在理解其设计思路后,才能知其然知其所以然

用户头像

olinone

关注

还未添加个人签名 2019-04-10 加入

还未添加个人简介

评论

发布
暂无评论
PAG动效框架源码笔记 (二)层级视图_ios_olinone_InfoQ写作社区