轉(zhuǎn)載請注明出處:http://www.olinone.com/
如上章所言说订,特效播放主要包括應用邏輯處理和圖形渲染兩個階段吨铸,其中宴霸,邏輯處理又可以看做模型對象的定義與流轉(zhuǎn)
模型分層
PAG框架模型大致可以分為三部分:
1译断、組件(PAGCompositon)
PAG框架支持多文件多圖層渲染萧恕,PAGCompositon組件可以同時容納多個PAGFile文件漾狼,每個PAGFile文件又可以包含多個Layer組件
PAGFile解析自File源文件鸠蚪,其中Layer組件可以支持元素替換今阳,比如可以替換PAGImageLayer中PAGImage資源對象,從而實現(xiàn)自定義融合元素
2茅信、圖層(PAGStage)
PAGStage承載了完整的組件圖層盾舌,記錄了每個組件及其資源對象的映射關(guān)系,比如PAGImage及其關(guān)聯(lián)的PAGImageLayer對象蘸鲸,實現(xiàn)通過資源對象查找對應Layer對象的能力
此外妖谴,PAGStage通過SequenceCache還緩存了資源對象與Graphic視圖對象的映射關(guān)系,類似于渲染緩存的職責
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構(gòu)造
// 通過File構(gòu)造PAGFile
std::shared_ptr<PAGFile> PAGFile::MakeFrom(std::shared_ptr<File> file) {
? // 解析File構(gòu)造組件模型
? 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));
? }
? // 堆棧模式處理每個視圖及其子視圖,保證每個視圖及其子視圖渲染環(huán)境一致性砸彬,比如matrix變化等
? for (int i = 0; i < count; i++) {
? ? DrawChildLayer(recorder, childLayer.get());
? }
? if (hasClip()) {
? ? recorder->restore();
? }
}
總結(jié)
為了支持多文件多圖層渲染颠毙,PAG框架設計了一套完整的框架模型,其復雜的對象繼承關(guān)系砂碉,加深了代碼閱讀理解難度蛀蜜,在理解其設計思路后,才能知其然知其所以然