Android高仿抖音照片电影功能的实现代码
photomovie (https://github.com/yellowcath/photomovie) 可轻松实现类似抖音、微视、美拍的照片电影功能。
效果如下
滤镜效果
转场效果
使用
首先在项目根目录的build.gradle文件里添加maven库
allprojects { repositories { ... maven { url 'https://www.jitpack.io' } } }
再在需要用的module的build.gradle里添加依赖
dependencies { compile 'com.github.yellowcath:photomovie:1.5.0' }
基本用法
可参照 demopresenter
//添加图片 list<photodata> photodatalist = new linkedlist<photodata>(); photodatalist.add(new simplephotodata(context,photopath1,photodata.state_local)); ... photodatalist.add(new simplephotodata(context,photopathn,photodata.state_local)); //生成图片源 photosource photosource = new photosource(photodatalist); //生成照片电影(使用预定义的水平转场动画) photomovie photomovie = photomoviefactory.generatephotomovie(photosource, photomoviefactory.photomovietype.horizontal_trans); //生成负责绘制电影内容的movierenderer movierenderer movierenderer = new gltexturemovierender(gltextureview); /** * or movierenderer movierenderer = new glsurfacemovierenderer(glsurfaceview); */ //照片电影播放器 photomovieplayer photomovieplayer = new photomovieplayer(context); photomovieplayer.setmovierenderer(mmovierenderer); photomovieplayer.setmovielistener(...); photomovieplayer.setloop(true); photomovieplayer.setonpreparedlistener(new photomovieplayer.onpreparedlistener() { @override public void onpreparing(photomovieplayer movieplayer, float progress) { } @override public void onprepared(photomovieplayer movieplayer, int prepared, int total) { mphotomovieplayer.start(); } @override public void onerror(photomovieplayer movieplayer) { } }); photomovieplayer.prepare();
轻松扩展
photomovie使用模块化的设计,每个部分都可以自定义然后替换,
- moviesegment:电影片段,每个电影片段都有特定的时长,在这段时间之内以特定的方式播放图片,例如scalesegment会对图片做缩放动画、endgaussianblursegment会对图片做从清晰到模糊的高斯模糊动画
- photomovie:核心类,代表照片电影本身,由图片源(photosource)和若干电影片段(moviesegment)组成一个完整的照片电影,图片通过photoallocator分配给moviesegment
- movielayer:为moviesegment扩展绘制多层特效的功能,例如subtitlelayer提供字幕展示
- imoviefilter:为整个照片电影提供滤镜
- movierenderer:负责把照片电影渲染到指定的输出界面,例如textureview(gltexturemovierender)、glsurfaceview(glsurfacemovierenderer)
- photomovieplayer:提供类似mediaplayer的接口,负责播放照片电影,播放进度由imovietimer控制
扩展电影类型
目前内置了6种类型,后两种即是抖音的左右切换和上下切换,thaw和window仿自美拍
public enum photomovietype { thaw, //融雪 scale, //缩放 scale_trans, //缩放 & 平移 window, //窗扉 horizontal_trans,//横向平移 vertical_trans//纵向平移 }
这里以微视的渐变特效为例展示如何扩展
分析得出,渐变特效首先图片居中放置,然后全程做一个微弱的放大动画,后半部分同时透明度变化消失
可见需要两个不同的片段类型
首先创建fitcenterscalesegment,继承fitcentersegment,实现单张图片的放大动画
public class fitcenterscalesegment extends fitcentersegment { /** * 缩放动画范围 */ private float mscalefrom; private float mscaleto; private float mprogress; /** * @param duration 片段时长 * @param scalefrom 缩放范围 * @param scaleto 缩放范围 */ public fitcenterscalesegment(int duration, float scalefrom, float scaleto) { super(duration); mscalefrom = scalefrom; mscaleto = scaleto; } @override protected void ondataprepared() { super.ondataprepared(); } @override public void drawframe(glescanvas canvas, float segmentprogress) { mprogress = segmentprogress; if (!mdataprepared) { return; } drawbackground(canvas); float scale = mscalefrom + (mscaleto - mscalefrom) * mprogress; //fitcentersegment已经具有缩放能力,这里传缩放值即可 drawcontent(canvas, scale); } //提升这两个函数的访问权限,供转场时使用 @override public void drawcontent(glescanvas canvas, float scale) { super.drawcontent(canvas, scale); } @override public void drawbackground(glescanvas canvas) { super.drawbackground(canvas); } }
然后创建转场片段gradienttransfersegment,其父类transitionsegment同时持有上一个与下一个片段,可以在其基础上实现任意转场功能
public class gradienttransfersegment extends transitionsegment<fitcenterscalesegment, fitcenterscalesegment> { /** * 缩放动画范围 */ private float mprescalefrom; private float mprescaleto; private float mnextscalefrom; private float mnextscaleto; public gradienttransfersegment(int duration, float prescalefrom, float prescaleto, float nextscalefrom, float nextscaleto) { mprescalefrom = prescalefrom; mprescaleto = prescaleto; mnextscalefrom = nextscalefrom; mnextscaleto = nextscaleto; setduration(duration); } @override protected void ondataprepared() { } @override public void drawframe(glescanvas canvas, float segmentprogress) { //下一个片段开始放大 float nextscale = mnextscalefrom + (mnextscaleto - mnextscalefrom) * segmentprogress; mnextsegment.drawcontent(canvas, nextscale); //上一个片段继续放大同时变透明 float prescale = mprescalefrom + (mprescaleto - mprescalefrom) * segmentprogress; float alpha = 1 - segmentprogress; mpresegment.drawbackground(canvas); canvas.save(); canvas.setalpha(alpha); mpresegment.drawcontent(canvas, prescale); canvas.restore(); }
创建照片电影
private static photomovie initgradientphotomovie(photosource photosource) { list<moviesegment> segmentlist = new arraylist<>(photosource.size()); for (int i = 0; i < photosource.size(); i++) { if (i == 0) { segmentlist.add(new fitcenterscalesegment(1600, 1f, 1.1f)); } else { segmentlist.add(new fitcenterscalesegment(1600, 1.05f, 1.1f)); } if (i < photosource.size() - 1) { segmentlist.add(new gradienttransfersegment(800, 1.1f, 1.15f, 1.0f, 1.05f)); } } return new photomovie(photosource, segmentlist); }
然后将这个photomovie正常播放即可,效果如下
扩展滤镜
目前内置了9个滤镜
public enum filtertype { none, cameo,//浮雕 gray,//黑白 kuwahara,//水彩 snow,//飘雪(动态) lut1, lut2, lut3, lut4, lut5, }
先看imoviefilter
public interface imoviefilter { void dofilter(photomovie photomovie,int elapsedtime, fbotexture inputtexture, fbotexture outputtexture); void release(); }
外部会提供一个输入纹理,然后由imoviefilter处理之后绘制到输出纹理上,即实现了滤镜效果
basemoviefilter已经实现了基本的输入输出流程,例如要做最基本的黑白滤镜,只需更换fragment_shader即可
public class graymoviefilter extends basemoviefilter { protected static final string fragment_shader = "" + "varying highp vec2 texturecoordinate;\n" + " \n" + "uniform sampler2d inputimagetexture;\n" + " \n" + "void main()\n" + "{\n" + " mediump vec4 color = texture2d(inputimagetexture, texturecoordinate);\n" + " mediump float gray = color.r*0.3+color.g*0.59+color.b*0.11;\n"+ " gl_fragcolor = vec4(gray,gray,gray,1.0);\n"+ "}"; public graymoviefilter(){ super(vertex_shader,fragment_shader); } }
同时photomovie提供了对lut滤镜的支持
lut其实就是lookup table(颜色查找表),根据原图的rgb值去相应的lut图里面查找对应转换后的rgb值,从而实现各种滤镜效果
lut(原图)
lut_2.jpg
原图
滤镜效果图
public class lutmoviefilter extends twotexturemoviefilter { public lutmoviefilter(bitmap lutbitmap){ super(loadshaderfromassets("shader/two_vertex.glsl"),loadshaderfromassets("shader/lut.glsl")); setbitmap(lutbitmap); } }
在lutmoviefilter的构造函数传入上面表格里的lut图,即可实现相应的滤镜效果,前面提到的黑白滤镜也可用这个方式实现
录制功能
glmovierecorder 提供了将照片电影录制为mp4的功能
可参照 demopresenter 的savevideo()函数
glmovierecorder recorder = new glmovierecorder(); recorder.configoutput(width, height(), bitrate,framerate,iframeinterval, outputpath); recorder.setdatasource(movierenderer); recorder.startrecord(new glmovierecorder.onrecordlistener() { @override public void onrecordfinish(boolean success) { ...... } @override public void onrecordprogress(int recordedduration, int totalduration) { ...... } });
背景音乐
mphotomovieplayer.setmusic(context, mmusicuri);
photomovie只提供了播放背景音乐的功能,录制完成之后需自行合成,demo里使用了
videoprocessor 进行合成
videoprocessor.mixaudiotrack(context, videpath, audiopath,outputpath, null, null, 0,100, 1f, 1f);
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 关键词清零!百度开始大范围打击快排