天天看点

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

作者:@olinone

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

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

模型分层

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

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构造PAGFile
std::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组件当前时刻对应的Graphic
void 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框架设计了一套完整的框架模型,其复杂的对象继承关系,加深了代码阅读理解难度,在理解其设计思路后,才能知其然知其所以然

继续阅读