转载请注明出处: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构造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框架设计了一套完整的框架模型,其复杂的对象继承关系,加深了代码阅读理解难度,在理解其设计思路后,才能知其然知其所以然