一、Bitmap(位图)
在Android开发中,一般对图像的处理就是Bitmap(位图),它包含了图像的全部数据,即点阵和颜色值,点阵就是包含像素点的矩阵,而颜色值就是ARGB,分别代表透明、红色、绿色、蓝色通道,它们共同决定了像素点的颜色。
-
位图(Bitmap): 位图(Bitmap)是一种数字图像表示,它由像素矩阵组成,每个像素点都包含颜色信息。在计算机图形学中,位图通常指的是一种图像文件格式,如BMP、PNG、JPEG等,它们包含了图像的完整数据。
-
像素点(Pixel): 像素是构成数字图像的最小单元。在位图中,每个像素点都有一个特定的位置(x, y坐标)和颜色值。像素点的集合构成了图像的完整画面。
-
点阵(Pixel Array): 点阵是组成位图的像素点的集合。在二维空间中,这些像素点排列成一个矩形网格,每个像素点都有一个唯一的坐标位置。
-
颜色值(ARGB): 在Android中,颜色值通常以ARGB(Alpha, Red, Green, Blue)的形式表示。每个颜色通道都有8位(即256级),所以ARGB颜色值可以表示为一个32位的整数。Alpha通道控制像素的透明度,取值范围从0(完全透明)到255(完全不透明)。红色、绿色和蓝色通道则控制颜色的强度,它们的组合可以产生几乎所有可见的颜色。
-
Bitmap.Config:
Bitmap.Config
是一个枚举类型,用于指定Bitmap
的配置。它定义了Bitmap
的像素数据的存储格式。常见的配置有ARGB_8888
(每个像素包含32位,即8位Alpha, 8位Red, 8位Green, 8位Blue),RGB_565
(每个像素包含16位,即5位Red, 6位Green, 5位Blue,没有Alpha通道),以及ARGB_4444
(每个像素包含16位,即4位Alpha, 4位Red, 4位Green, 4位Blue)等。
二、颜色矩阵
上面已经讲到,像素点是构成图像的最小单元。每个像素点除了包含位置坐标外,还包含一定的颜色信息,这个颜色信息是用一个颜色矩阵分量来保存的,即下图的RGBA1(矩阵C).
而在Android系统中,颜色矩阵是用一个4*5的数字矩阵来表示的(矩阵A,由一维数组构成)。
它们的乘积(矩阵R)即为屏幕上显示的图像颜色,这里的RGBA取值应在0~255之间。
R=(R1, G1, B1, A1)
特别的,当A为如下矩阵时,根据上面的矩阵相乘的公式,R=C,原来像素点的颜色矩阵没有改变,因此称该矩阵为单位矩阵。一般初始化图像的时候,我们会构建它。
单位矩阵
我们上面说了矩阵R是矩阵A和矩阵C的乘积,即为屏幕上所显示的图像颜色。矩阵C是我们原来Bitmap图片的颜色,因此当我们想改变屏幕上所显示的图像颜色(矩阵R)时,我们可以通过改变矩阵C,来实现各种变换效果。
关于矩阵R,谷歌中帮我们将它的属性和方法封装成类ColorMatrix,其中有许多简单的操作方法。其中最核心的代码如下:
//构建出ColorMatrix对象
ColorMatrix colorMatrix = new ColorMatrix();
//通过set方法把对应的4 * 5数字矩阵(一维数组)传入
colorMatrix.set(float[] src);
//构建出颜色过滤器ColorMatrixColorFilter,再通过ImageView设置
mImageView.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
其实重点就在于这个4 * 5数组矩阵的数值的搭配。除了以上简单粗暴的set对应的颜色矩阵,ColorMatrix类中还提供了许多方法,比如可以调整图像的色相、饱和度、灰度等。下面整理了一些具体的实现。
三、实现案例
(基于EasyPhotos)
(一)Activity中调用逻辑
case R.id.photo_greymasks:
//这里检查selectedPhotoList列表是否为空。如果用户没有选择任何照片,那么接下来的操作将不会执行。
if (selectedPhotoList.isEmpty()) {
Toast.makeText(this, "没选图片", Toast.LENGTH_SHORT).show();
return true;
}
//BitmapFactory.decodeFile方法用于将文件路径转换为Bitmap。
Bitmap originalBitmap = BitmapFactory.decodeFile(selectedPhotoList.get(0).getAvailablePath());
//创建了原始Bitmap的一个副本,确保在应用滤镜时不会修改原始图片。
originalBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
// 应用灰度滤镜
if(originalBitmap!=null){
Bitmap filteredBitmap = getHuiDu(originalBitmap);
// 将应用了滤镜的Bitmap设置到bitmapView上,这样用户就可以看到处理后的效果。
bitmapView.setVisibility(View.VISIBLE);
bitmapView.setImageBitmap(filteredBitmap);
// 在不再需要原始图片时回收它
BitmapUtils.recycle(originalBitmap);
//弹出一个短暂的提示框,告诉用户图片已经成功应用了灰度滤镜。
Toast.makeText(SampleActivity.this, "图片已应用灰度滤镜", Toast.LENGTH_SHORT).show();
}
//关闭侧边栏
drawer.closeDrawer(GravityCompat.START);
break;
其他几种Bitmap处理效果的调用逻辑同上。
(二)Bitmap类型图片几种滤镜/特效实现函数及效果图
1.获取灰度图片
//获取灰度图片
public static Bitmap getHuiDu(Bitmap bitMap) {
int width, height;
height = bitMap.getHeight();
width = bitMap.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
//创建一个新的Canvas对象c,它将用于在新的Bitmap上绘制图像。
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
//创建一个新的ColorMatrix对象cm,用于定义颜色变换矩阵。
ColorMatrix cm = new ColorMatrix();
//设置颜色矩阵的饱和度为0,将所有颜色都将被转换为灰度。
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
//使用Canvas对象c在新的Bitmap``bmpGrayscale上绘制原始Bitmap``bitMap,位置为(0, 0),使用带有颜色过滤的Paint对象paint。
c.drawBitmap(bitMap, 0, 0, paint);
return bmpGrayscale;
}
效果图
2.获取黑白图片
public static Bitmap convertToBMW(Bitmap bmp) {
int width = bmp.getWidth(); // 获取位图的宽
int height = bmp.getHeight(); // 获取位图的高
int[] pixels = new int[width * height]; // 通过位图的大小创建像素点数组
// 设定二值化的域值,默认值为100
int tmp = 150;
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
int alpha = 0xFF << 24;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int grey = pixels[width * i + j];
// 分离三原色
alpha = ((grey & 0xFF000000) >> 24);
int red = ((grey & 0x00FF0000) >> 16);
int green = ((grey & 0x0000FF00) >> 8);
int blue = (grey & 0x000000FF);
int i1 = 0xFFFF;
if (red > tmp) {
red = 255;
} else {
red = 0;
}
if (blue > tmp) {
blue = 255;
} else {
blue = 0;
}
if (green > tmp) {
green = 255;
} else {
green = 0;
}
pixels[width * i + j] = alpha << 24 | red << 16 | green << 8
| blue;
if (pixels[width * i + j] == -1) {
pixels[width * i + j] = -1;
} else {
pixels[width * i + j] = -16777216;
}
}
}
// 新建图片
Bitmap newBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 设置图片数据
newBmp.setPixels(pixels, 0, width, 0, 0, width, height);
Bitmap resizeBmp = ThumbnailUtils.extractThumbnail(newBmp, width, height);
// return getHuiDu(resizeBmp, AppApplication.getIntance());
return resizeBmp;
}
效果图
3.生成底片效果
/**
* 通过更改图片像素点的RGBA值,生成底片效果
* @param scrBitmap
* @return
*/
public static Bitmap beautyImage(Bitmap scrBitmap) {
int width = scrBitmap.getWidth();
int height = scrBitmap.getHeight();
int count = width * height;
int[] oldPixels = new int[count];
int[] newPixels = new int[count];
scrBitmap.getPixels(oldPixels, 0, width, 0, 0, width, height);
for (int i = 0; i < oldPixels.length; i++) {
int pixel = oldPixels[i];
int r = Color.red(pixel);
int g = Color.green(pixel);
int b = Color.blue(pixel);
r = 255 - r;
g = 255 - g;
b = 255 - b;
if (r > 255) {
r = 255;
}
if (g > 255) {
g = 255;
}
if (b > 255) {
b = 255;
}
if (r < 0) {
r = 0;
}
if (g < 0) {
g = 0;
}
if (b < 0) {
b = 0;
}
newPixels[i] = Color.rgb(r, g, b);
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(newPixels, 0, width, 0, 0, width, height);
return bitmap;
}
效果图
4.怀旧风格图片效果
/**
* 生成怀旧风格
* @param bmp
* @return
*/
private Bitmap oldRemeber(Bitmap bmp)
{
// 速度测试
long start = System.currentTimeMillis();
int width = bmp.getWidth();
int height = bmp.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
int pixColor = 0;
int pixR = 0;
int pixG = 0;
int pixB = 0;
int newR = 0;
int newG = 0;
int newB = 0;
int[] pixels = new int[width * height];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < height; i++)
{
for (int k = 0; k < width; k++)
{
pixColor = pixels[width * i + k];
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, newR > 255 ? 255 : newR, newG > 255 ? 255 : newG, newB > 255 ? 255 : newB);
pixels[width * i + k] = newColor;
}
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
long end = System.currentTimeMillis();
return bitmap;
}
效果图
5.图片模糊效果
// 图片模糊效果
public static Bitmap convertBlur(Bitmap origin) {
int width = origin.getWidth();
int height = origin.getHeight();
int hRadius = width > 150 ? width / 150 : 1; // 水平方向模糊度
int vRadius = height > 150 ? height / 150 : 1; // 垂直方向模糊度
int iterations = 7; // 模糊迭代度
int[] inPixels = new int[width * height];
int[] outPixels = new int[width * height];
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
origin.getPixels(inPixels, 0, width, 0, 0, width, height);
for (int i = 0; i < iterations; i++) {
blur(inPixels, outPixels, width, height, hRadius);
blur(outPixels, inPixels, height, width, vRadius);
}
blurFractional(inPixels, outPixels, width, height, hRadius);
blurFractional(outPixels, inPixels, height, width, vRadius);
bitmap.setPixels(inPixels, 0, width, 0, 0, width, height);
return bitmap;
}
private static void blur(int[] in, int[] out, int width, int height, int radius) {
int widthMinus1 = width - 1;
int tableSize = 2 * radius + 1;
int[] divide = new int[256 * tableSize];
for (int i = 0; i < 256 * tableSize; i++) {
divide[i] = i / tableSize;
}
int inIndex = 0;
for (int y = 0; y < height; y++) {
int outIndex = y;
int ta = 0, tr = 0, tg = 0, tb = 0;
for (int i = -radius; i <= radius; i++) {
int rgb = in[inIndex + clamp(i, 0, width - 1)];
ta += (rgb >> 24) & 0x99; // 调整灰度。0x99表示半透明
tr += (rgb >> 16) & 0xff; // 调整红色
tg += (rgb >> 8) & 0xff; // 调整绿色
tb += rgb & 0xff; // 调整蓝色
}
for (int x = 0; x < width; x++) {
out[outIndex] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb];
int i1 = x + radius + 1;
if (i1 > widthMinus1) {
i1 = widthMinus1;
}
int i2 = x - radius;
if (i2 < 0) {
i2 = 0;
}
int rgb1 = in[inIndex + i1];
int rgb2 = in[inIndex + i2];
ta += ((rgb1 >> 24) & 0xff) - ((rgb2 >> 24) & 0xff);
tr += ((rgb1 & 0xff0000) - (rgb2 & 0xff0000)) >> 16;
tg += ((rgb1 & 0xff00) - (rgb2 & 0xff00)) >> 8;
tb += (rgb1 & 0xff) - (rgb2 & 0xff);
outIndex += height;
}
inIndex += width;
}
}
public static void blurFractional(int[] in, int[] out, int width, int height, float radius) {
radius -= (int) radius;
float f = 1.0f / (1 + 2 * radius);
int inIndex = 0;
for (int y = 0; y < height; y++) {
int outIndex = y;
out[outIndex] = in[0];
outIndex += height;
for (int x = 1; x < width - 1; x++) {
int i = inIndex + x;
int rgb1 = in[i - 1];
int rgb2 = in[i];
int rgb3 = in[i + 1];
int a1 = (rgb1 >> 24) & 0xff;
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int a2 = (rgb2 >> 24) & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
int a3 = (rgb3 >> 24) & 0xff;
int r3 = (rgb3 >> 16) & 0xff;
int g3 = (rgb3 >> 8) & 0xff;
int b3 = rgb3 & 0xff;
a1 = a2 + (int) ((a1 + a3) * radius);
r1 = r2 + (int) ((r1 + r3) * radius);
g1 = g2 + (int) ((g1 + g3) * radius);
b1 = b2 + (int) ((b1 + b3) * radius);
a1 *= f;
r1 *= f;
g1 *= f;
b1 *= f;
out[outIndex] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
outIndex += height;
}
out[outIndex] = in[width - 1];
inIndex += width;
}
}
效果图
6.LOMO特效
public static Bitmap lomoFilter(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int dst[] = new int[width * height];
bitmap.getPixels(dst, 0, width, 0, 0, width, height);
int ratio = width > height ? height * 32768 / width : width * 32768 / height;
int cx = width >> 1;
int cy = height >> 1;
int max = cx * cx + cy * cy;
int min = (int) (max * (1 - 0.8f));
int diff = max - min;
int ri, gi, bi;
int dx, dy, distSq, v;
int R, G, B;
int value;
int pos, pixColor;
int newR, newG, newB;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pos = y * width + x;
pixColor = dst[pos];
R = Color.red(pixColor);
G = Color.green(pixColor);
B = Color.blue(pixColor);
value = R < 128 ? R : 256 - R;
newR = (value * value * value) / 64 / 256;
newR = (R < 128 ? newR : 255 - newR);
value = G < 128 ? G : 256 - G;
newG = (value * value) / 128;
newG = (G < 128 ? newG : 255 - newG);
newB = B / 2 + 0x25;
// ==========边缘黑暗==============//
dx = cx - x;
dy = cy - y;
if (width > height)
dx = (dx * ratio) >> 15;
else
dy = (dy * ratio) >> 15;
distSq = dx * dx + dy * dy;
if (distSq > min) {
v = ((max - distSq) << 8) / diff;
v *= v;
ri = newR * v >> 16;
gi = newG * v >> 16;
bi = newB * v >> 16;
newR = ri > 255 ? 255 : (ri < 0 ? 0 : ri);
newG = gi > 255 ? 255 : (gi < 0 ? 0 : gi);
newB = bi > 255 ? 255 : (bi < 0 ? 0 : bi);
}
// ==========边缘黑暗end==============//
dst[pos] = Color.rgb(newR, newG, newB);
}
}
Bitmap acrossFlushBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
acrossFlushBitmap.setPixels(dst, 0, width, 0, 0, width, height);
return acrossFlushBitmap;
}
效果图
7.图片倒影特效
//设置倒影
public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap){
final int reflectionGap = 4;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionImage = Bitmap.createBitmap(bitmap,
0, height/2, width, height/2, matrix, false);
Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height/2), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmapWithReflection);
canvas.drawBitmap(bitmap, 0, 0, null);
Paint deafalutPaint = new Paint();
canvas.drawRect(0, height,width,height + reflectionGap,
deafalutPaint);
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0,
bitmap.getHeight(), 0, bitmapWithReflection.getHeight()
+ reflectionGap, 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ reflectionGap, paint);
return bitmapWithReflection;
}
效果图
参考:
【2】android实现滤镜 android滤镜开发https://blog.51cto.com/u_93011/6591870
【3】Android之多种Bitmap效果(4)https://www.cnblogs.com/wangxiuheng/p/4500579.html
【4】Android-Bitmap特效https://blog.youkuaiyun.com/ygc973797893/article/details/7207134