这篇博客将会通过对像素的RGB分量做一个处理,然后达到一些特效。并没有很高端大气的代码。也没用使用ImageFilter等一些库。多数参考了别人,大神勿喷。
首先看一下今天的效果图。
由于上传大小限制的关系,只有一小部分。当然,功能中除了光晕,其他都是实现了的。如果可以的话,我回上传到github上gif图。代码请在文末下载。那么接下来,我们就来看下如何实现这些。
再次声明,绝大多数是操作像素实现的。速度上可能会很慢。不过不要紧,要的是思想。
由于代码太多的原因,下面只会给出关键性代码,更多代码请前往github。
public Bitmap doPro(Bitmap src) {
int width,height;
height = src.getHeight();
width = src.getWidth();
Bitmap bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvas.drawBitmap(src, 0, 0, paint);
return bitmap;
}
关于ColorMatrix,参考官方文档。
@Override
public Bitmap doProByPix(Bitmap src) {
int width = src.getWidth();
int height = src.getHeight();
//创建像素点数组
int[] pixels = new int[width*height];
int alpha,grey,red,green,blue;
src.getPixels(pixels,0,width,0,0,width,height);
alpha = 0xFF<<24;
for (int i = 0 ; i < height ; i++){
for (int j = 0 ; j < width ; j++){
grey = pixels[width*i+j];
red = ((grey & 0x00FF0000)>>16);
green = ((grey & 0x0000FF00)>>8);
blue = ((grey & 0x000000FF));
grey = (int)((float)red*0.3+(float)green*0.59+(float)blue*0.11);
grey = alpha | (grey<<16)|(grey<<8)|grey;
pixels[width*i+j]=grey;
}
}
Bitmap pro = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
pro.setPixels(pixels,0,width,0,0,width,height);
return pro;
}
上面这个通过对颜色的 与,左移,右移来拿到颜色分量,当然,也有更简单的方法拿到分离,后面会说。
public Bitmap doPro(Bitmap src) {
int width = src.getWidth();
int height = src.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
ColorMatrix colorMatrix = new ColorMatrix();
//设置饱和度 设置为0.7 范围 0 - 1
//colorMatrix.setSaturation((float) 0.7);
//设置亮度
colorMatrix.set(new float[]{1,0,0,0,70,
0,1,0,0,70,
0,0,1,0,70,
0,0,0,1,0});
//改变对比度
// colorMatrix.set(new float[]{2, 0, 0, 0, 0,
// 0, 2, 0, 0, 0,
// 0, 0, 2, 0, 0,
// 0, 0, 0, 1, 0});
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(src,0,0,paint);
return bitmap;
}
R=0.393r+0.769g+0.189b
G=0.349r+0.686g+0.168b
B=0.272r+0.534g+0.131b
怀旧效果也可以通过颜色矩阵来实现,下面是通过颜色矩阵实现的部分代码
colorMatrix.set(new float[]{
(float) 0.393, (float) 0.768, (float) 0.189, 0, 0,
(float) 0.349, (float) 0.686, (float) 0.168, 0, 0,
(float) 0.272, (float) 0.534, (float) 0.131, 0, 0,
0, 0, 0, 1, 0
});
接下来我们看如何通过操作像素分量来实现
public Bitmap doProByPix(Bitmap src) {
/**
* 怀旧效果原理
* R=0.393r+0.769g+0.189b
* G=0.349r+0.686g+0.168b
* B=0.272r+0.534g+0.131b
*/
long startTime = System.currentTimeMillis();
int width = src.getWidth();
int height = src.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int pixColor,pixR,pixG,pixB,newR,newG,newB;
int[] pixels= new int[width*height];
src.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
//获取对应点的像素
pixColor = pixels[width*i+j];
pixR = Color.red(pixColor);
pixG = Color.green(pixColor);
pixB = Color.blue(pixColor);
newR = (int) (0.393 * pixR + 0.769 * pixG + 0.189 * pixB);
newG = (int) (0.349 * pixR + 0.686 * pixG + 0.168 * pixB);
newB = (int) (0.272 * pixR + 0.534 * pixG + 0.131 * pixB);
int newColor = Color.argb(255,Math.min(255,newR),Math.min(255,newG),Math.min(255, newB));
pixels[width*i+j]=newColor;
}
}
bitmap.setPixels(pixels,0,width,0,0,width,height);
long endTime = System.currentTimeMillis();
Log.e("tag","this is old used time "+(endTime-startTime)+"ms");
return bitmap;
}
代码都很简单,而且相同部分很多,重点就在于颜色分量的处理,这个处理是根据原理 来的。
1.通过RenderScript来实现模糊
public Bitmap RSblur(Bitmap src){
long startTime = System.currentTimeMillis();
Bitmap bitmap = Bitmap.createBitmap(src.getWidth(),src.getHeight(), Bitmap.Config.ARGB_8888);
RenderScript renderScript = RenderScript.create(MyApplication.app);
ScriptIntrinsicBlur scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
Allocation allIn = Allocation.createFromBitmap(renderScript, src);
Allocation allOut = Allocation.createFromBitmap(renderScript,bitmap);
// 控制模糊程度
scriptIntrinsicBlur.setRadius(2.f);
scriptIntrinsicBlur.setInput(allIn);
scriptIntrinsicBlur.forEach(allOut);
allOut.copyTo(bitmap);
src.recycle();
renderScript.destroy();
long endTime = System.currentTimeMillis();
Log.e("tag","this is RenderScript blur use time "+(endTime-startTime)+"ms");
return bitmap;
}
2.操作像素RGB
这里我用了网上的一个FastBlur类,这个类实现了模糊并且做了优化。
pixColor = pixels[(i + n) * width + k + m];
pixR = Color.red(pixColor);
pixG = Color.green(pixColor);
pixB = Color.blue(pixColor);
newR = newR + (int) (pixR * laplacian[idx] * alpha);
newG = newG + (int) (pixG * laplacian[idx] * alpha);
newB = newB + (int) (pixB * laplacian[idx] * alpha);
idx++;
这里利用拉普拉斯矩阵。
oldColor=oldPixels[i-1];
oldPixR = Color.red(oldColor);
oldPixG = Color.green(oldColor);
oldPixB = Color.blue(oldColor);
newColor = newPixels[i];
newPixR = Color.red(newColor);
newPixG = Color.green(newColor);
newPixB = Color.blue(newColor);
//计算
newPixR = (oldPixR-newPixR +127)>255?255:(oldPixR-newPixR +127) ;
newPixG = (oldPixG -newPixG+127)>255?255:(oldPixG -newPixG+127) ;
newPixB = (oldPixB-newPixB+127)>255?255:(oldPixB-newPixB+127);
color = piexls[i];
pixR = 255 - Color.red(color);
pixG = 255 - Color.green(color);
pixB = 255 - Color.blue(color);
核心代码:
color = pixels[j*width+i];
pixR= Color.red(color);
pixG= Color.green(color);
pixB = Color.blue(color);
int distance = (int) (Math.pow((centerY-j),2)+Math.pow((centerX-i),2));
if (distance*radius);
{
//按照距离大小计算增强的光照值
int result = (int) (strength*(1.0-Math.sqrt(distance)/radius));
pixR = pixR+result;
pixG = pixG+result;
pixB = pixB+result;
}
//做左右限制
pixR = Math.min(255,Math.max(0,pixR));
pixG = Math.min(255,Math.max(0,pixG));
pixB = Math.min(255,Math.max(0,pixB));
color=pixels[i];
pixR = Color.red(color);
pixG = Color.green(color);
pixB = Color.blue(color);
//R 分量
pixR = pixR * 128/(pixG+pixB+1);
pixR = Math.min(255,Math.max(0,pixR));
//G 分量
pixG = pixG*128/(pixB+pixR+1);
pixG =Math.min(255,Math.max(0,pixG));
//B 分量
pixB = pixB*128/(pixR+pixG+1);
pixB = Math.min(255,Math.max(0,pixB));
color = pixels[i];
pixR = Color.red(color);
pixG = Color.green(color);
pixB = Color.blue(color);
pixR = Math.abs((pixR - pixG - pixB) * 3 / 2);
pixR = Math.min(255, pixR);
pixG = Math.abs((pixG - pixR - pixB) * 3 / 2);
pixG = Math.min(255, pixG);
pixB = Math.abs((pixB - pixR - pixG) * 3 / 2);
pixB = Math.min(255,pixB);
k = random.nextInt(123456);
int dx = i+k%8;
int dy = j+k%8;
if (dx>=width){
dx=width-1;
}
if (dy>=height){
dy=height-1;
}
pos = dy*width +dx;
pos1 = j*width+i;
pixels[pos1]=pixels[pos];
color = pixels[i];
sum = (Color.red(color)+Color.blue(color)+Color.green(color))/3;
if (sum>=128){
sum = 255;
}else{
sum = 0;
}
pixels[i]=Color.argb(Color.alpha(color),sum,sum,sum);
color=pixels[i];
pixR= Color.red(color);
pixG = Color.green(color);
pixB = Color.blue(color);
//r
pixR = Math.abs(pixG-pixB+pixG+pixR) * pixR /256;
if (pixR>255){
pixR = 255;
}
pixG =Math.abs(pixB-pixG+pixB+pixR) *pixR/256;
if (pixG>255){
pixG = 255;
}
//B=|b-g+b+r|*g/256
pixB = Math.abs(pixB-pixG+pixB+pixR) * pixG/256;
if (pixB>256){
pixB=255;
}
color=pixels[j*width+i];
//获取i+1像素
color_right = pixels[j*width+i+1];
//获取j+1像素
color_bottom = pixels[(j+1)*width+i];
//计算R分量
pixR = (int) (Math.pow((Color.red(color)-Color.red(color_right)),2)
+Math.pow((Color.red(color)-Color.red(color_bottom)),2));
pixR = ((int) (Math.sqrt(pixR)*2));
pixR = Math.min(255,Math.max(0,pixR));
//计算G 分量
pixG = (int) (Math.pow((Color.green(color)-Color.green(color_right)),2)
+Math.pow((Color.green(color)-Color.green(color_bottom)),2));
pixG = ((int) (Math.sqrt(pixG)*2));
pixG = Math.min(255,Math.max(0,pixG));
//计算B分量
pixB = (int) (Math.pow((Color.blue(color)-Color.blue(color_right)),2)
+Math.pow((Color.blue(color)-Color.blue(color_bottom)),2));
pixB = ((int) (Math.sqrt(pixB)*2));
pixB = Math.min(255,Math.max(0,pixB));
pixels[j*width+i] = Color.argb(Color.alpha(color),pixR,pixG,pixB);
到这里就结束了,当然,图片处理特效还有很多,我这里仅仅是一小部分。在这里推荐一个库:ImageFilter
代码地址:github
参考资料:落日小屋
参考资料:其他原理