微头条丨PAG动效方案使用总结
2023-04-10 17:34:18 来源: 面包芯语

这些PAG常用方法解读也能帮助大家使用起来更轻松:

PAG 运行时编辑

PAG 的运行时编辑主要分为两类:1)修改文本图层的文本信息、替换图片图层中的占位图、修改实色图层中的颜色

//C++std::shared_ptrReplaceImageOrText(){autopagFile=pag::PAGFile::Load("../../assets/test2.pag");if(pagFile==nullptr){returnnullptr;}for(inti=0;inumImages();i++){autopagImage=pag::PAGImage::FromPath("../../assets/scene.png");pagFile->replaceImage(i,pagImage);}for(inti=0;inumTexts();i++){autotextDocumentHandle=pagFile->getTextData(i);textDocumentHandle->text="hah哈哈哈哈哈";pagFile->replaceText(i,textDocumentHandle);}returnpagFile;}

2)渲染树编辑


(资料图片仅供参考)

渲染树编辑指的是通过使用 PAGComposition 的相关接口,完成多个图层、多个 pag 文件的自由组合。具体如何使用可以参考下面的代码:

//以OC版本为例-(PAGComposition*)makeComposition{PAGComposition*compostion=[PAGCompositionMake:self.view.bounds.size];floatitemWidth=self.view.bounds.size.width/5;floatitemHeight=100;for(inti=0;i<20;i++){PAGFile*pagFile=[selfgetPAGFile:i/5clume:i%5name:[NSStringstringWithFormat:@"%d",i]itemWidth:itemWidthitemHeight:itemHeight];[compostionaddLayer:pagFile];}returncompostion;}-(PAGFile*)getPAGFile:(int)rowclume:(int)columename:(NSString*)nameitemWidth:(float)itemWidthitemHeight:(float)itemHeight{PAGFile*pagFile=[PAGFileLoad:[[NSBundlemainBundle]pathForResource:nameofType:@"pag"]];if(pagFile){floatscaleX=itemWidth*1.0f/[pagFilewidth];CGAffineTransformtransform=CGAffineTransformMakeScale(scaleX,scaleX);CGAffineTransformtranflate=CGAffineTransformMakeTranslation(itemWidth*colume,row*itemHeight+150);transform=CGAffineTransformConcat(transform,tranflate);[pagFilesetMatrix:transform];[pagFilesetStartTime:0];[pagFilesetDuration:10000000];}returnpagFile;}
PAG UI场景及列表播放

当一个页面中含有多个pag 文件时,如果使用多个 PAGView,由于每一个 PAGView 都需要一个独立的渲染环境,内存和 CPU 占用相对较高,推荐的处理方法有两种:

1)通过使用 PAG 的组合模式用将多个 PAG 文件添加到同一个 PAGComposition 中,具体使用方法见运行时编辑,从而将多个渲染环境缩减到一个渲染环境,降低内存和 CPU 占用;

2)对于一些 UI 列表场景,需要有多个 View 的存在, pag 文件无法添加到一个 PAGComposition 中,此时可以使用 PAGImageView。

//以OC版本为例#import#defineWIDTH100@interfacePAGCell:UITableViewCell@property(nonatomic,strong)PAGImageView*pagImageView;@property(nonatomic,strong)NSString*filePath;@end@implementationPAGCell-(instancetype)initWithStyle:(UITableViewCellStyle)stylereuseIdentifier:(NSString*)reuseIdentifier{if(self=[superinitWithStyle:stylereuseIdentifier:reuseIdentifier]){self.pagImageView=[[PAGImageViewalloc]initWithFrame:CGRectMake(20,0,WIDTH,WIDTH)];[self.contentViewaddSubview:self.pagImageView];}returnself;}-(void)setFilePath:(NSString*)filePath{if([filePathlength]>0){[self.pagImageViewsetPath:filePath];[self.pagImageViewsetCurrentFrame:0];[self.pagImageViewsetRepeatCount:-1];[self.pagImageViewplay];}}
PAG 字体注册

PAG 除了支持修改文本图层的文本信息外,还支持修改字体。具体方法如下:

(1)通过 PAGFont 获取字体的相关信息,然后赋值给 PAGText,使用到的接口如下:

获取字体信息

//C++/***Registersafontrequiredbythetextlayersinpagfiles,sothattheycanberenderedas*designed.*/staticPAGFontRegisterFont(conststd::string&fontPath,intttcIndex,conststd::string&fontFamily="",conststd::string&fontStyle="");/***Registersafontrequiredbythetextlayersinpagfiles,sothattheycanberenderedas*designed.*/staticPAGFontRegisterFont(constvoid*data,size_tlength,intttcIndex,conststd::string&fontFamily="",conststd::string&fontStyle="");PAGFont(std::stringfontFamily,std::stringfontStyle):fontFamily(std::move(fontFamily)),fontStyle(std::move(fontStyle)){}/***Astringwiththenameofthefontfamily.**/conststd::stringfontFamily;/***Astringwiththestyleinformation—e.g.,“bold”,“italic”.**/conststd::stringfontStyle;

(2)fontFamlily 和 fontStyle 赋值给 PAGText,PAGText 再填充到 PAGTextLayer 中。

//C++for(inti=0;inumTexts();i++){autotextDocumentHandle=pagFile->getTextData(i);textDocumentHandle->text="hah哈哈哈哈哈";//UsespecialfontautopagFont=pag::PAGFont::RegisterFont("../../resources/font/NotoSansSC-Regular.otf",0);textDocumentHandle->fontFamily=pagFont.fontFamily;textDocumentHandle->fontStyle=pagFont.fontStyle;pagFile->replaceText(i,textDocumentHandle);}

如果使用了特定字体而又没有注册或字体文件中没有包含该字符,PAG 内部(Android、iOS、macOS、Windows 平台)有一个默认字体列表(同时支持外部设置字体回退列表,外部设置时会覆盖默认设置),会回退到 PAG 的默认字体列表中,此时使用那种字体对于业务方而言是不确定的。

//C++/***Resetsthefallbackfontnames.Itshouldbecalledonlyoncewhentheapplicationisbeing*initialized.*/staticvoidSetFallbackFontNames(conststd::vector&fontNames);/***Resetsthefallbackfontpaths.Itshouldbecalledonlyoncewhentheapplicationisbeing*initialized.*/staticvoidSetFallbackFontPaths(conststd::vector&fontPaths,conststd::vector&ttcIndices);
PAG 视频编辑场景

在视频编辑场景,使用的不是 PAGView,而是 PAGPlayer、PAGSurface 和 PAGComposition。

PAGSurface 可以通过 CVPixelBufferRef 或纹理创建,方便快捷的与视频后编辑中的 CVPixelBuffer 或 纹理进行整合。同时 PAGImage 也支持通过 CVPixelBufferRef 或 纹理创建,通过 PAGPlayer 控制播放进度,将视频内容填充进图片图层的占位图。

//OC/***CreatesaPAGImageobjectfromthespecifiedCVPixelBuffer,returnnulliftheCVPixelBufferis*invalid.*/+(PAGImage*)FromPixelBuffer:(CVPixelBufferRef)pixelBuffer;//javapublicstaticPAGImageFromTexture(inttextureID,inttextureTarget,intwidth,intheight);publicstaticPAGImageFromTexture(inttextureID,inttextureTarget,intwidth,intheight,booleanflipY);
PAG 软解注入

为什么会有软解注入?PAG 的导出方式中支持 BMP 预合成导出,在 pag 文件中,如果含有 BMP 预合成,一个 BMP 预合成相当于一个视频,视频则需要解码。在 PAG SDK 中默认使用硬件解码,但硬件解码存在两个问题:

1)移动端硬件解码器的瞬时存在数目是有限制的,不能无限的创建,如果创建硬件解码器的数目超过限制,就会出现解码器创建失败的情况,在视频编辑场景中需要关注;

2)Android 平台由于机型兼容性、碎片化验证,不能保证所有的机型都能硬件解码成功,因此 Android 平台软解是必须的。

于是,在提供的制品库中, iOS 平台由于没有兼容性的问题,默认是不带软解的,Android 提供了两个包,普通的包默认是内置软解的,noffavc 的包是没有内置软解的。

具体怎么注入软解呢:Android 平台可以选择完整制品库,iOS 平台可以引入 ffavc。

pod"ffavc"

通过如下方法完成软件解码器的注册:

//OC-(void)registerSoftwareDecoder{//注册软件解码器工厂指针autofactory=ffavc::DecoderFactory::GetHandle();[PAGVideoDecoderRegisterSoftwareDecoderFactory:(void*)factory];}

如果接入方自己的 APP 中已经内置了软解库,可以通过外部注入的方式注入软解。

PAG 官网提供了下面这个软解注入接口,需要业务方基于自己的解码器实现,PAG BMP 预合成中的视频编码格式为 h264。

https://github.com/libpag/ffavc/blob/main/vendor/libpag/include/pag/decoder.h

具体的实现方式可以参考:

ffavc/FFAVCDecoder.cpp at main · libpag/ffavc · GitHub

然后自己通过上面提到的方式注入软解。

PAG 服务端渲染

和 Lottie、SVGA 不同的是,PAG 支持服务端渲染,尽管 PAG 的渲染依赖 OpenGL 环境,但 服务端却可以继续使用 CPU 服务器。具体实现层面,PAG 通过 swiftshader 在 CPU 服务器上构建出了 OpenGL 环境,使得 pag 文件可以在 CPU 环境中正常渲染。

在服务端的具体使用如下,获取到的是 BGRA 的数据,该数据可以用于视频编码。

//C++autopagFile=pag::PAGFile::Load("../../assets/test2.pag");autopagSurface=pag::PAGSurface::MakeOffscreen(pagFile->width(),pagFile->height());if(pagSurface==nullptr){printf("---pagSurfaceisnullptr!!!\n");return-1;}autopagPlayer=newpag::PAGPlayer();pagPlayer->setSurface(pagSurface);pagPlayer->setComposition(pagFile);autototalFrames=TimeToFrame(pagFile->duration(),pagFile->frameRate());autocurrentFrame=0;intbytesLength=pagFile->width()*pagFile->height()*4;while(currentFrame<=totalFrames){pagPlayer->setProgress(currentFrame*1.0/totalFrames);autostatus=pagPlayer->flush();//PAG渲染数据读取autodata=newuint8_t[bytesLength];pagSurface->readPixels(pag::ColorType::BGRA_8888,pag::AlphaType::Premultiplied,data,pagFile->width()*4);delete[]data;currentFrame++;}deletepagPlayer;

开始使用 :如何接入

接入使用分为开发部分和设计部分,这里分别为大家介绍下:

(1) 开发者——接入SDK

在PAG的Github Wiki中有非常详细的接入指引(包括Android、iOS、Web等平台的接入方法和范例工程),大家自行学习就好:https://github.com/Tencent/libpag/wiki

img

(2)设计师——下载导出插件和预览工具

设计师想要使用PAG,只需在官网下载预览工具 PAGViewer和 AE 导出插件即可:https://pag.art/docs/install.html

img

整体看来,PAG的核心价值如下:

据官网显示已经有很多头部应用接入使用(如微信、王者荣耀、小红书、知乎等),稳定性应该很有保证,如果有动效上线相关业务的朋友非常值得试试。

最后顺便附上 PAG 相关的资源,大家感兴趣可以进一步了解:

官网: https:/pag.art/

Github:https://github.com/Tencent/libpag

QQ 群:893379574

关键词:
责任编辑: 梅长苏