修改Bitmap的配色
Posted 疯狂小芋头
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了修改Bitmap的配色相关的知识,希望对你有一定的参考价值。
1. 修改Bitmap的配色
对于 Drawable 可以使用的系统提供的方法进行 Drawable 的配色设置
DrawableCompat.setTint(drawable, color);
但是对于Bitmap系统目前没有直接的设置方法。由于图片资源实际上也是一个Bitmap,Drawable的setTint()
生效肯定是进行了某些处理,所以可以参考Bitmap对应的Drawable的处理实现对Bitmap的配色修改。
最终解决方案:
//设置bitmap图片为主题配色
@Nullable
public Bitmap setBitmapTintColor(int colorTint, @Nullable Bitmap bitmap)
if (bitmap != null && colorTint != 0)
//创建一个与原图一样大小的空图片
Bitmap newBmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
//创建一个新的bitmap在上面绘制出指定的颜色的配色,mode使用默认值
ColorFilter filter = new PorterDuffColorFilter(colorTint, PorterDuff.Mode.SRC_IN);
//创建画笔及设置过滤器
Paint paint = new Paint();
paint.setColorFilter(filter);
//创建canvas并绘制出图片
Canvas canvas = new Canvas(newBmp);
canvas.drawBitmap(bitmap, 0, 0, paint);
return newBmp;
else
return null;
1.1. 了解如何设置图片配色
处理Bitmap的Drawable是BitmapDrawable
,查看其setTint()
的方法看是如何实现对Bitmap的配色修改。
public void setTintList(ColorStateList tint)
final BitmapState state = mBitmapState;
if (state.mTint != tint)
state.mTint = tint;
mTintFilter = updateTintFilter(mTintFilter, tint, mBitmapState.mTintMode);
invalidateSelf();
从代码中可以查看到在setTintList()
的方法中,并没有直接对Bitmap进行立即修改,而是更新了mTintFilter
的配色信息,后续通过调用invalidateSelf()
重新绘制自身Drawable才完成对显示图片的配色修改,所以重点在于Drawable的绘制方法中
1.2. 了解如何修改bitmap配色并绘制
Drawable的绘制方法是通过draw()
方法绘制出图形,所以重点在draw()
方法的实现
@Override
public void draw(Canvas canvas)
//....
//设置绘制的paint的tintFilter,在setTintList()方法中修改的信息在此处被使用了
final boolean clearColorFilter;
if (mTintFilter != null && paint.getColorFilter() == null)
paint.setColorFilter(mTintFilter);
clearColorFilter = true;
else
clearColorFilter = false;
//...
//绘制图片对象,使用了上述配置了tint的paint
canvas.drawBitmap(bitmap, null, mDstRect, paint);
省略无关的代码,所以这里可以暂时推断修改Bitmap配色的方法关键在于paint.setColorFilter()
,在这里修改了绘制的画笔的信息,所以导致了图片配色变更。那么接下来要解决tintFilter
这个配色对象如何产生。
1.3. 了解如何生成配色过滤器
根据上面setTintList()
的方法,可以知道在设置配色时,已经调用了方法updateTintFilter()
更新了画笔的colorFilter,那么如何产生这个tintFilter关键就在这个方法。
PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,
@Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode)
if (tint == null || tintMode == null)
return null;
final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
if (tintFilter == null)
return new PorterDuffColorFilter(color, tintMode);
tintFilter.setColor(color);
tintFilter.setMode(tintMode);
return tintFilter;
由于我们是直接对Bitmap进行修改,所以肯定是全新创建一个ColorFilter的,因此参考其中的filter如何创建即可。
这个方法中更新或创建一个tintFilter
的话,ColorStateList
与PorterDuff.Mode
都是必须的;ColorStateList我们可以理解,是配色信息,并且创建PorterDuffColorFilter
时只需要用到颜色值即可。
但是目前我们并不知道要使用什么PorterDuff.Mode,我们通过Drawable的修改配色操作来反推mode的值。
1.4. 获取生成ColorFilter的参数
1.4.1.
修改Drawable的tint时,我们只是调用了DrawableCompat.setTint(drawable, color)
,这个方法并不要求提供mode,所以可以从这个方法查看mode的值如何产生。
public void setTint(@ColorInt int tintColor)
setTintList(ColorStateList.valueOf(tintColor));
从以上的方法我们得到了ColorStateList
如何从一个颜色值转换成ColorStateList的颜色状态类;
1.4.2.
查看setTintList()
方法时,可以发现mode的参数使用的是BitmapDrawable本身状态中保存的mTintMode
。
updateTintFilter(mTintFilter, tint, mBitmapState.mTintMode)
每当通过资源图片获取到一个BitmapDrawable后,我们并没有对drawable的tintMode进行特殊的修改,直接就使用了,所以mTintMode
是默认BitmapDrawable中创建时初始化的值。我们查看该值初始化的值是什么即可。
1.4.3.
查看mTintMode
值的初始值,发现是使用了BitmapState
中的值,并且该值来自于父类ConstantState
final static class BitmapState extends ConstantState
final Paint mPaint;
// Values loaded during inflation.
int[] mThemeAttrs = null;
Bitmap mBitmap = null;
ColorStateList mTint = null;
Mode mTintMode = DEFAULT_TINT_MODE;
//...
由于DEFAULT_TINT_MODE
并不是在BitmapState中定义的,所以查看其父类确认该值的初始
public abstract class Drawable
//...
static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
//...
至此,我们可以已经可以确定生成一个colorFilter的参数值需要是什么了。
1.5. 确定修改Bitmap的步骤
- 加载一个Bitmap
- 使用指定配色创建一个ColorFilter
- 创建一个Paint用于绘制修改配色后的Bitmap
- 使用Canvas绘制出Bitmap
基于以上步骤可以得到以下的实现方式:
//设置bitmap图片为主题配色
public Bitmap setBitmapTintColor(int colorTint, @Nullable Bitmap bitmap)
if (bitmap != null && colorTint != 0)
//创建一个与原图一样大小的空图片
Bitmap newBmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
//创建一个新的bitmap在上面绘制出指定的颜色的配色,mode使用默认值
ColorFilter filter = new PorterDuffColorFilter(colorTint, PorterDuff.Mode.SRC_IN);
//创建画笔及设置过滤器
Paint paint = new Paint();
paint.setColorFilter(filter);
//创建canvas并绘制出图片
Canvas canvas = new Canvas(newBmp);
canvas.drawBitmap(bitmap, 0, 0, paint);
return newBmp;
else
return null;
1.6. 其它
实际上,修改图片配色就是为了将图片中的非透明像素设置成主题色,还有一种方式也可以解决,就是直接修改bitmap中的像素颜色值。大致实现方式如下:
//复制一个图片对象
Bitmap newBmp=bitmap.copy(bitmap.getConfig(),true);
//获取图片的宽高
int width=bitmap.getWidth();
int height=bitmap.getHeight();
for(int i=0;i<width;i++)
for(int j=0;j<height;j++)
//遍历原图的所有像素
int srcColor=bitmap.getPixel(i,j);
if(srcColor!=0)
//非透明像素全部设置为指定配色
newBitmap.setPixel(i,j,colorTint);
注意:实测该方案虽然可行,但是修改后得到的图片可能存在模糊和锯齿的结果,还是使用上述的解决方案更靠谱。
最后,更简单的方式是百度查一下相应的解决方案,会有相关的链接:
https://blog.csdn.net/nongminkouhao/article/details/104659195
以上所有步骤只是解释了如何从源码分析得到链接中的解决方案。感谢互联网的知识与分享。
以上是关于修改Bitmap的配色的主要内容,如果未能解决你的问题,请参考以下文章
Bitmap处理--BitmapShader、目标大小Bitmap、遮罩图层