刚开始研究图片修改,去网上查了点方法,然后写了个小 demo, 写下本篇博客,总结一下最基本修改图片的方式,希望对大家有帮助.
我将用下面2种方式来修改我们的图片,以及叙述下图片为何会有变化.
- ColorMatrix
- RGBA~利用自带的方法修改色调,饱和度,亮度来修改图片
- 矩阵~利用矩阵计算得到新的矩阵修改图片
- 像素点~根据原像素点数组,经过计算得到新的像素点数组,再重新绘制图片
上面这些不太明白没关系,只是抽重点讲一下,真正的内容在下面,需要记住的关键字已经标记出来了,矩阵 Matrix, 像素点等,下面会配合代码以及图片实例来详细讲一下,加深印象.
从名字就能知道,颜色矩阵,为改变图片颜色所使用的矩阵,要熟悉一个新的类,最好的方法就是去看它的源码,贴上源码,还好不是很长.
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/package android.graphics;
import java.util.Arrays;
/**
* 4x5 matrix for transforming the color and alpha components of a Bitmap.
* The matrix can be passed as single array, and is treated as follows:
*
*
*[ a, b, c, d, e,
*f, g, h, i, j,
*k, l, m, n, o,
*p, q, r, s, t ]
*
* * When applied to a color [R, G, B, A]
, the resulting color
* is computed as:
*
*
*
*R’
= a*R + b*G + c*B + d*A + e;
*G’
= f*R + g*G + h*B + i*A + j;
*B’
= k*R + l*G + m*B + n*A + o;
*A’
= p*R + q*G + r*B + s*A + t;
*
* * That resulting color [R’
, G’
, B’
, A’
]
* then has each channel clamped to the 0
to 255
* range.
*
*
* * The sample ColorMatrix below inverts incoming colors by scaling each
* channel by -1
, and then shifting the result up by
* 255
to remain in the standard color space.
*
*
*
*[ -1, 0, 0, 0, 255,
*0, -1, 0, 0, 255,
*0, 0, -1, 0, 255,
*0, 0, 0, 1, 0 ]
*/
@SuppressWarnings({ "MismatchedReadAndWriteOfArray", "PointlessArithmeticExpression" })
public class ColorMatrix {
private final float[] mArray = new float[20];
/**
* Create a new colormatrix initialized to identity (as if reset() had
* been called).
*/
public ColorMatrix() {
reset();
}/**
* Create a new colormatrix initialized with the specified array of values.
*/
public ColorMatrix(float[] src) {
System.arraycopy(src, 0, mArray, 0, 20);
}/**
* Create a new colormatrix initialized with the specified colormatrix.
*/
public ColorMatrix(ColorMatrix src) {
System.arraycopy(src.mArray, 0, mArray, 0, 20);
}/**
* Return the array of floats representing this colormatrix.
*/
public final float[] getArray() { return mArray;
}/**
* Set this colormatrix to identity:
*
* [ 1 0 0 0 0- red vector
*0 1 0 0 0- green vector
*0 0 1 0 0- blue vector
*0 0 0 1 0 ] - alpha vector
*
*/
public void reset() {
final float[] a = mArray;
Arrays.fill(a, 0);
a[0] = a[6] = a[12] = a[18] = 1;
}/**
* Assign the src colormatrix into this matrix, copying all of its values.
*/
public void set(ColorMatrix src) {
System.arraycopy(src.mArray, 0, mArray, 0, 20);
}/**
* Assign the array of floats into this matrix, copying all of its values.
*/
public void set(float[] src) {
System.arraycopy(src, 0, mArray, 0, 20);
}/**
* Set this colormatrix to scale by the specified values.
*/
public void setScale(float rScale, float gScale, float bScale,
float aScale) {
final float[] a = mArray;
for (int i = 19;
i > 0;
--i) {
a[i] = 0;
}
a[0] = rScale;
a[6] = gScale;
a[12] = bScale;
a[18] = aScale;
}/**
* Set the rotation on a color axis by the specified values.
* * axis=0
correspond to a rotation around the RED color
* axis=1
correspond to a rotation around the GREEN color
* axis=2
correspond to a rotation around the BLUE color
*
*/
public void setRotate(int axis, float degrees) {
reset();
double radians = degrees * Math.PI / 180d;
float cosine = (float) Math.cos(radians);
float sine = (float) Math.sin(radians);
switch (axis) {
// Rotation around the red color
case 0:
mArray[6] = mArray[12] = cosine;
mArray[7] = sine;
mArray[11] = -sine;
break;
// Rotation around the green color
case 1:
mArray[0] = mArray[12] = cosine;
mArray[2] = -sine;
mArray[10] = sine;
break;
// Rotation around the blue color
case 2:
mArray[0] = mArray[6] = cosine;
mArray[1] = sine;
mArray[5] = -sine;
break;
default:
throw new RuntimeException();
}
}
.
.
.
}
从注释中看到,这个是个4*5的矩阵与[R, G, B, A]相乘,然后得到新的 R,G,B,A 值.看个公式图,会比较清楚一点.
文章图片
由此可见,通过颜色矩阵修改了原图像的RGBA值,从而达到了改变图片颜色效果的目的。并且,通过所示的运算可知,
颜色矩阵M的第一行参数abcde决定了图像的红色成分,
第二行参数fghij决定了图像的绿色成分,
第三行参数klmno决定了图像的蓝色成分,
第四行参数pqrst决定了图像的透明度,
第五列参数ejot是颜色的偏移量。
这里用一张图片来说明一下何为 rgba.
文章图片
基本的三色调,配上了 alpha 透明度,最常使用到的地方就是PS了,我们图片也不例外需要这些元素.
继续来看源码,先看构造法,了解 ColorMatrix 构造需要什么条件,总共就3个方法.
无参数方法,则构建对象之后,会先调用 reset() 方法,重置成最原始的状态,我们来调整参数值,得到新的矩阵,修改图片;
float[],ColorMatrix这2个方法则是直接传入了已经计算好的值.本文主要用的是无参数的构造方法.
还有 setScale,setRotate,setSaturation,postConcat 等方法,将在下文配合代码一起讲解.
1.1 RGBA
- 色调
ColorMatrix A = new Matrix();
A.setRotate(axis,degree);
其中 n 的值从源码中就可以看到分为0-RED(红),1-GREEN(绿),2-BLUE(蓝),degree 就是对当前色值进行修改的度. - 饱和度
ColorMatrix B = new Matrix();
B.setSaturation(sat);
sat是饱和度的值. - 亮度
ColorMatrix C = new Matrix();
c.setScale(rScale,gScale, bScale,aScale);
而对图片修改呢,则需要一个完整的矩阵
则需要下面的方法,将3个值糅在一起,形成一个新的矩阵.
ColorMatrix D = new Matrix();
D.postConcat(A);
D.postConcat(B);
D.postConcat(C);
这样就是完整的利用 rgba 值来修改图片.附上代码
/**
* 利用 rgba 来修改图片
* @param bm所需修改的图片
* @param hue色调值
* @param saturation饱和度
* @param lum亮度
* @return修改完成的图片
*/
public static Bitmap getHandleImageForARGB(Bitmap bm, float hue, float saturation, float lum) {
Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//色调 0-R 1-G 2-B
ColorMatrix hueMatrix = new ColorMatrix();
hueMatrix.setRotate(0, hue);
hueMatrix.setRotate(1, hue);
hueMatrix.setRotate(2, hue);
//饱和度
ColorMatrix satMatrix = new ColorMatrix();
satMatrix.setSaturation(saturation);
//亮度
ColorMatrix lumMatrix = new ColorMatrix();
lumMatrix.setScale(lum, lum, lum, 1);
//将色调,饱和度,亮度全糅合要一起
ColorMatrix imageMatrix = new ColorMatrix();
imageMatrix.postConcat(hueMatrix);
imageMatrix.postConcat(satMatrix);
imageMatrix.postConcat(lumMatrix);
paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
canvas.drawBitmap(bm, 0, 0, paint);
return bmp;
}
这里我们需要用到我们熟悉的Canvas,以及Paint画笔,将我们的图片重新绘制出来,什么样的Paint呢?
就是经过setRotate,setSaturation,setScale之后利用postConcat方法得到新的ColorMatrix
这样我们画笔所需要的Paint完备了,接下来就可以在画布上面画出我们的bitmap了.
我把这个方法放到一个叫**ImageHelper**的类中,这样直接调用即可.
正确 | 错误 |
---|---|
文章图片 |
文章图片 |
进度条拖动时正确的效果 | 莫名出现的黑屏 |
文章图片 |
文章图片 |
右边是我第一次写的时候发现的bug,拖动进度条的时候莫名的出现黑屏,当时以为是值传递错误,尝试了各种方法,例如打log,传入值判断等等,依然黑屏.
后来在机(删)缘(了)巧(重)合(写)下,就修改成了左边部分代码那样.......运行正常了!!!黑屏也不见了!!!我就只是把监听事件写在前面了而已.
文章图片
不管怎么样,bug是解决了,具体的原因后面再去探讨,碰到此类问题的朋友也可以去了解一下为什么.本文不做深究.
看到配图之中,有一个修改的图片以及3个进度条,拖动进度条,时时修改图片.
进度条的值我们设置为0-255,默认进度条居中,设置为127.
当进度条拖动之后对应的progress值也就发生改变,我们获得到progress值后进过公式计算.
具体操作我们放在进度条的onProgressChanged中处理.
.
.
.
private static final int MAX_VALUE = https://www.it610.com/article/255;
private static final int MID_VALUE = 127;
.
.
.
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
switch (seekBar.getId()) {
case R.id.primary_hue_seekbar:
//获得色调
mHue = (progress - MID_VALUE) * 1.0f / MID_VALUE * 180;
break;
case R.id.primary_sat_seekbar:
//获得饱和度
mSat = progress * 1.0f / MID_VALUE;
break;
case R.id.primary_lum_seekbar:
//获得亮度
mLum = progress * 1.0f / MID_VALUE;
break;
}
mImageview.setImageBitmap(ImageHelper.getHandleImageForARGB(mBitmap, mHue, mSat, mLum));
}
这样就可以拖动进度条而时时修改图片了.
1.2 矩阵
这里就直接用上效果图来讲解吧.
文章图片
下方的20个edittext就是代表上面那个矩阵公式,忘了的朋友可以往上面翻一翻.
private float[] mColorMatrix = new float[20];
private void setImageMatrix() {
Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.set(mColorMatrix);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(mBitmap, 0, 0, paint);
mImageview.setImageBitmap(bmp);
}
这里直接用到set(float[] f)的方法将mColorMatrix直接传入,就不用像1.1那样一个一个值设置过去.
这里用到的方法可以点击进入源码,传入float数组即可,非常简单,而得到新的bitmap和上面一下,需要画布画笔来绘制.
在网上查阅资料的时候,看到别人分享的一个实现图片怀旧效果的算法,直接看图片吧,各个值也在图片上可以看到.
文章图片
这样就实现了怀旧风格图片的效果了.这些数值可不是凭空编出来的,都是有算法专家去算出来的,所以说,学好数理化,走遍天下都不怕!
2 像素点
像素的基本概念可以去百度百科看一下,这里我们只需要知道图片是有一个像素点数组组成的,而我们要修改图片,则需要对这个数组进行修改,形成一个新的数组,然后在赋给图片即可.
获得图片像素点数组的方法:
int width = bm.getWidth();
int height = bm.getHeight();
int color;
int r, g, b, a;
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
bm.getPixels(oldPx, 0, width, 0, 0, width, height);
for (int i = 0;
i < width * height;
i++) {
color = oldPx[i];
r = Color.red(color);
g = Color.green(color);
b = Color.blue(color);
a = Color.alpha(color);
}
在贴上几个算法公式,达到一些特定的效果.
- 底片效果
bmp.r = 255-bmp.r;
bmp.g = 255-bmp.g;
bmp.b = 255-bmp.b; - 浮雕效果
bmp.r = bmpBefore.r - bmp.r+127;
bmp.g = bmpBefore.g - bmp.g+127;
bmp.b = bmpBefore.b - bmp.b+127; - 老照片效果
bmp.r = (int) (0.393 * bmp.r + 0.769 * bmp.g + 0.189 * bmp.b);
bmp.g = (int) (0.349 * bmp.r + 0.686 * bmp.g + 0.168 * bmp.b);
bmp.b = (int) (0.272 * bmp.r + 0.534 * bmp.g + 0.131 * bmp.b);
实际效果如下图所示:
第一次写这么长的博客,如果哪里写的不清楚,请多包涵!!!
【Android|Android使用 argb, 矩阵,像素点对图片进行修改】附上github地址:地址