安卓多媒体编程算法
1,计算机图形的表示方式方法编程
1.1canvas
①像素点形式(单色位图),一个像素点至关于1*1个像素,8个像素点就是8个0011占据一个byte的位置,200*200=40000 40000/8 = 5000byte字节api
②24位位图,一个像素点有24位(2的24次方)来表示颜色 ,3个byte.40000*24/8 = 120000byte字节异步
③256位图,一个像素点占256色,为(2的8次方),一个byte.40000byte就能够表示.ide
1.2工具
矢量图形:储存的是指令,而不是像素点布局
位图的缺陷:占据体积比较大post
常见位图格式:JPEG,PNG,BMP,前面两种经过图片压缩算法减小储存空间(即相同的颜色进行压缩,由于人眼没法识别这些细微差异)ui
2,加载图片到内存
①经过BitmapFactory.docodeFile(path)解码图片到内存上
BitmapFactory.docodeXX,还能够转换流,文件(默认都是32位的位图,a(透明度)rgb(颜色))
②若是加载过大的图片,会出现OOM:Out of MemoryError 内存不足异常
虽然一个图片看起来不大,可是经过BitMapFactory加载出来的图片,是跟图片的分辨率有关,一个像素就须要4个byte去表示
建立模拟器的时候有一个选项VM heep一个应用程序能申请的最大内存空间,默认16MB
2.2 加载大图片到手机中
对图片进行缩放:BitMapFactory.docodeFIle(path,opts(能够是空,也能够压缩));
Option opts = new Option();
opts.inSampleSize=xx,设置采样率,若是设置的值大于1,就会从新采样原图,返回一个小一点的图片用来节约内存,若是等于4,就把原来的图片宽高缩小为原来四分之一,至关于总像素个数占原来的十六分之一.
通常的计算方式:根据当前手机的分辨率进行计算
①获取手机的宽高信息,和图片的分辨率
②获取图片的分辨率(宽高信息)
ExifInterface exif =new xxxx(filepath);//获得照片的头信息(拍摄信息,时间,地点,类型)
exif.getAttribute(ExifInterface.XXXX);//有一些常量能够获取信息.
exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH,默认值(0))
//有的文件是经过图片软件生成的,或画出来的,没有头信息
这时候就要采用另外一种作法
Option opt1 = new Option();
Opt1.inJustDecodeBounds = true;//设置为false就没有把bitmap加载到内存
//不把图片加载到内存,而是先获取宽高,再进行缩放比
//注意这里须要先让程序解析一次
BitmapFactory.decodeFile(img.getAbsolutePath(), opts);
Opt1.outHeight; opt1.outWidth;
//设置为false以后再解析一次
BitmapFactory.decodeFile(img.getAbsolutePath(), opts);
③获取手机屏幕的宽高
//获取屏幕管理器
WindowManager wm = getSystemService(WINDOW_SERVICE);
wm.getDefultDisplay().getXXX()//宽高,(推荐使用getSize(Point outsize)大小)
Point outsize = new Point();//api13开始使用的
④计算缩放比,按大一点的缩放比比较好一些,按公司的业务需求来
3,图形的缩放
需求,对图片进行缩放并显示在页面上
①获取原图,
BitMap srcMap= BitMapFactory.decodeFile(path);
把原图加载进内存(通常都不对原图进行处理,而是处理原图在内存中的拷贝)建立原图在内存中的拷贝:
//建立一个等大小,类型的空白图片
copyedBitmap = BitMap.createBitMap(宽,高,图形的类型)//返回一个能够编辑的bmp图片,指定宽高和类型,通常在安卓里指定32位位图,获取原图的类型,srcMap.getConfig();
//建立一个画板,传入建立的空白图片
Canvas canvas = new Canvas(copyedBitmap);
//建立一个画笔
Paint paint new Paint();
//设置画笔的颜色
paint.setColor(Color.Black);
//开始画画
Matrix matrix = new Matrix();//按照1:1的比例做画
matrix.setScale(sx(0.2f),sy)//水平,竖直方向的缩放比,单位f
canvas.drawBitMap(原图,变化矩阵,paint);
//把拷贝出来的图片显示在界面上,
setImageBitMap()会自动处理图片适应屏幕
3.2
3.2.1图片的平移
matrix.setTranslate(dx,dy);//水平,竖直方向的偏移量,移动图片的像素点(会丢失图片内容)
3.2.2图片的旋转
matrix.setRotate(旋转的角度);以左上角为原点进行旋转
matrix.setRotate(旋转的角度,px,py)//以指定的坐标旋转(若是想让它以中心旋转,坐标设置宽高的二分之一)
3.2.3镜面效果,本质上是X坐标轴取反,Y轴不变,而后平移过来(或者覆盖原有图片)
matrix.setScale(-1,1)//宽度变成原来的负值,这时候图片会超出屏幕,须要移动回来
matrix.setTranslate(srcbitmap.getWidth(),0)//移动对应图片的宽度
setXXX都是设置的方法,不能同时生效,后面的会覆盖前面的
若是但愿后面的方法实现,须要使用postTranslate(XXXXX)方法,在上一次修改以后的基础上进行变化.
3.2.4倒影效果,上下翻转(Y轴取反,X轴不变)(跟旋转180度效果不是同样的)
4,练习,随手涂鸦
4.1 ui布局
第一排,工具栏,供用户选择画笔的颜色,并显示对应颜色的区块,
画笔的粗细(用户输入,或经过SeekBar设置也能够)
setMax(最大值),默认值progress(5,其它也能够)
剩余区域绘画区域
4.2 在MainActivity中,找到这些控件,设置对应的点击事件
①建立一个画笔
Paint paint = new Paint();
paint.setColor( 指定画笔的颜色);
paint.setStorkeWidth(width);//设置画笔的宽度
②SeekBar相关
SeekBar.setOnSeekBarChangeListener();//拖动变化监听器
seekbar.getProgress()//获取当前拖动的状态
4.3①做画位置实际是在一个ImageView中做画
要提早指定ImageView的宽高,由于一旦被加载就固定了
//给imageView注册一个触摸事件
iv.setOnTouchListener(new OnTouchListener(){
//当被触摸的时候调用
public boolean onTouch(View v,MotionEvent event){
//经过按下的动做判断(按下触摸的时候,保持拖动的时候,松开触摸的时候)
switch(event.getAction){
case: MotionEvent.Action_MOVE
每移动一个像素点调用一次
break;
case: MotionEvent.Action_DOWN
break;
case: MotionEvent.Action_UP
break;
}
//返回值默认返回一个false,表明:事件没有处理完毕,要等待事件处理完毕.
//若是为flase移动和松开手指的事件就不会执行,一直在等待按下事件结束.
//true:表明事件已经处理完毕,没有处理完就不会触发松开和拖动事件
//
return true;
}
});
②须要一个能够被修改的图片
BitMap alertMap ;//须要的图片
Canvas canvas;//定义画板,画笔已经有了
alertMap = BitMap.createBitMap(320,320,BitMap.ARGB_8888);//8888高质量的位图.
Canvas = new Canvas(alertMap);
③在控件触摸事件方法中
定义横纵坐标int x int y .
在按下的位置,获取坐标
在拖动的时候,获取坐标
canvas.drawLine(sx,sy,nx,ny,画笔);//执行完这个方法后就画出了一条线
Iv.setImageBitMap(alertMap)//把线显示在控件上
//可是这样作,会出现开始坐标固定的状况下画出一个不想要的图形,须要从新初始化手指在屏幕上的坐标(实际上一条线是由无数个短线拼接,也就是两个像素点之间的拼接,而不改变初始位置,就会像画了个大圆饼)
4.4 其它细节问题
(0)拿取控件的宽高拿不到,由于控件的绘制是跟Activity异步的,就算执行了onCreate,onStart,onResume()都拿不到.
(1)要经过view绘制成功的监听获取,但在监听的时候若是绘制的view发生改变了,就会从新绘制一次,因此能够在获取到一次数据以后把监听注销掉
(2)获取绘制监听事件iv.getViewTreeObs
(3)LinearLayout绘制顺序,先绘制最外层的Liner 再按设置的顺序,从上至下绘制,树形结构的绘制,开发中不建议布局LinearLayOut的嵌套,由于比较消耗时间
//注册View绘制监听事件
iv_show.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//注销监听
iv_show.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
①在Activity中,onCreateOptionMenu(Menu menu)//建立一个选项菜单调用的方法
//本质上就是一个在底部出现的ListView视图,里面的控件都是item
判断用户点击的是哪个条目menu.getItemId();
//保存图片,把BitMap转换为流,能够保存在内存中,内存输出流
alertBitMap.compress(format(保存的格式),quality(图片的质量1-100),stream输出流);
记得关流
//这样输出的图片背景颜色是黑色,由于BitMap默认都是黑色的,因此建立好了画板以后
canvas.drawColor(Color.WHITE);//画一个白色的背景
②在图库应用中找不到画的图片,由于系统图库是不知道这个图片被添加了,只有在开机或SD卡拔从新插入添加以后才会扫描添加
这时候就要告诉图库,图片被添加了
经过模拟SD卡被插入的广播,告诉图库须要从新扫描一次 4.4版本以后就不能用了
Intent intent = new Intent();//建立意图
intent.setAction(Intent.ACTION.MEDIA_MOUNTED);//设置动做
intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));//设置文件
sendBroadCast(intent);//发送广播
5.撕衣服的小应用
①对图片进行缩放处理,代码拷贝图片,不对原图进行操做
本质上是两张图片相互覆盖,若是在图片上滑动,就把滑动的区域设置为透明.
//不要修改原图,先建立一个原图的拷贝
建立空白图片,建立画板,建立画笔,画一个跟原图同样的图片
②设置触摸事件
onTouch方法返回值记得设置为true
触摸的点设置为透明
设置按下点为透明,alertBitmap.setPixel(x,y,Color.TRANSPARENT)//设置为透明的
而后从新设置图片lv.setImageBitMap(图片)便可
③撕衣服用户体验差,须要提升撕衣服的范围
//经过 for循环把对应坐标点的周围的点也设置为透明
这样每次的透明块是一个矩形块,很差看,要每次的透明块为一个圆形更好
由此图可知,须要一个圆形,就表明这个像素点的坐标到圆心的距离小于等于3(经过勾股定理能够算出)
Math.sqrt()开平方
④若是超出图片范围就崩溃.
处理方式:把异常捕获进来,而后超出图片就不会出异常(尴尬)
⑤getX()和getrawX()的区别raw(未经处理的)
getrawX()距离屏幕左边的距离.getrawY()距离屏幕上边的距离
getX()距离当前控件的左边距离,getY()距离当前控件上边的距离
6,图片颜色的处理
图像颜色的处理:把每一个像素点的颜色按照必定的规则进行变化,就会出现新的效果
颜色矩阵模板代码:
ColorMatrix cm = new ColocrMartix();//颜色矩阵
Cm.set(new float[]{
1*result(变化比例),0,0,0,0, //red
0,1,0,0,0, //green
0,0,1,0,0, //blue
0,0,0,1,0 //透明度
});
paint.setColorFilter(new ColorMatrixColorFilter(cm));//设置颜色过滤器
而后根据拖动条改变对应的颜色便可