写过自定义view的小伙伴们可能了解或者是用到过PorterDuff.Mode,第一次接触这个东西的时候,就感觉这玩意儿挺魔性,可以玩一把。于是乎就查找了一些关于PorterDuff.Mode的资料以及一些小Demo,印象最深的应该还是刮奖的那个。废话不多说,下面就说说我对PorterDuff.Mode的理解和体会,仅代表个人观点,如有错误希望大家指正。
- PorterDuff.Mode是什么?
在android SDK Paint类中有一个很重要的方法setXfermode,这个方法用于设置图像的过渡模式,所谓过渡是指图像的饱和度、颜色值等参数的计算结果的图像表现。在SDK中Xfermode有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前两个类在API 16被遗弃了,所以这里不作介绍。PorterDuffXfermode类主要用于图形合成时的图像过渡模式计算,其概念来自于1984年在ACM SIGGRAPH计算机图形学出版物上发表了“Compositing digital images(合成数字图像)”的Tomas Porter和Tom Duff,合成图像的概念极大地推动了图形图像学的发展,PorterDuffXfermode类名就来源于这俩人的名字组合PorterDuff。下面是android SDK中PorterDuff的Mode枚举类型定义。
- PorterDuff.Mode是什么?
public enum Mode {
/** [0, 0] */
CLEAR (0),
/** [Sa, Sc] */
SRC (1),
/** [Da, Dc] */
DST (2),
/** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
SRC_OVER (3),
/** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
DST_OVER (4),
/** [Sa * Da, Sc * Da] */
SRC_IN (5),
/** [Sa * Da, Sa * Dc] */
DST_IN (6),
/** [Sa * (1 - Da), Sc * (1 - Da)] */
SRC_OUT (7),
/** [Da * (1 - Sa), Dc * (1 - Sa)] */
DST_OUT (8),
/** [Da, Sc * Da + (1 - Sa) * Dc] */
SRC_ATOP (9),
/** [Sa, Sa * Dc + Sc * (1 - Da)] */
DST_ATOP (10),
/** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
XOR (11),
/** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
DARKEN (12),
/** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
LIGHTEN (13),
/** [Sa * Da, Sc * Dc] */
MULTIPLY (14),
/** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
SCREEN (15),
/** Saturate(S + D) */
ADD (16),
OVERLAY (17);
Mode(int nativeInt) {
this.nativeInt = nativeInt;
}
/**
* @hide
*/
public final int nativeInt;
}
上面代码中每种模式的注释都说明了该模式的alpha通道和颜色值的计算方式,要理解各个模式的计算方式需要先弄明白公式中各个元素的具体含义:
Sa:全称为Source alpha,表示源图的Alpha通道;
Sc:全称为Source color,表示源图的颜色;
Da:全称为Destination alpha,表示目标图的Alpha通道;
Dc:全称为Destination color,表示目标图的颜色.
当Alpha通道的值为1时,图像完全可见;当Alpha通道值为0时,图像完全不可见;当Alpha通道的值介于0和1之间时,图像只有一部分可见。Alpha通道描述的是图像的形状,而不是透明度。
- 2.什么是源图什么是目标图?
源图:其实就是你即将要绘制在当前画布上的图。
目标图:就是你在进行下一步绘制之前当前画布对应绘制区域的图;
以下是Google官方Sample中给的16种模式下合成的效果图,事实上Google在这儿是挖了一个坑的(两次绘制的图像都是从最左顶点开始,绘制图形的宽高也写的是不同的,具体详细解释请戳:http://blog.csdn.net/iispring/article/details/50472485),真实的合成样子并不完全是这样的;
image.png
这才是真实的合成效果(蓝色的绘制起点是从黄色的圆心处):
image.png - 3.PorterDuff.Mode的用法以及一个刮奖小Demo
package com.ebanswers.iqcore;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by air on 2017/8/23.
*/
public class GuaJiangView extends View {
private Bitmap mBitmap;
private Bitmap mDstBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint paint;
public GuaJiangView(Context context) {
this(context, null);
}
public GuaJiangView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GuaJiangView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
//创建一个bitmap(一张图片)
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
//根据上面的bitmap创建相同大小的bitmap,此时改bitmap所有的像素点的颜色值全为0,即全透明的一个bitmap
mDstBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
//创建一个Path对象,后面根据手的touch事件设置path
mPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);//抗锯齿
paint.setDither(true);//防止抖动
paint.setAlpha(0);//设置Alpha为0
paint.setStyle(Paint.Style.STROKE);//设置风格为描边
paint.setStrokeWidth(50);//设置描边的宽度
paint.setStrokeCap(Paint.Cap.ROUND);//设置笔触为圆角
paint.setStrokeJoin(Paint.Join.ROUND);//设置绘制转折的的地方为圆角
mCanvas = new Canvas(mDstBitmap);//创建一个画布在透明的bitmap上进行绘制
mCanvas.drawColor(Color.GRAY);//将透明的bitmap绘制成灰色的,所有像素点的色值对应灰色的值
}
@Override
protected void onDraw(Canvas canvas) {
//第一步绘制一个图片
canvas.drawBitmap(mBitmap, 0, 0, null);
//在灰色的bitmap上绘制路径
drawTouchPath();
//将灰色的bitmap绘制到原画布上
canvas.drawBitmap(mDstBitmap, 0, 0, null);
}
/**
* 关键点,绘制手指的touch路径
*/
private void drawTouchPath() {
/*
* 手指触摸后绘制触摸路径,在执行此操作时候
* 第一次mDstBitmap全部像素色值是灰色(mCanvas.drawColor(Color.GRAY))----目标图(当前画布上的图)
* 将要绘制的路径mPath----源图(将要绘制的图)
* 模式为:PorterDuff.Mode.DST_IN(这里DST_IN的IN即是数学中的交集的意思,指针对mPath和mDstBitmap的交集进行合成,实际上就是mPath)
* 该模式的算法[Sa * Da, Sa * Dc]
* 目标图(灰色)的透明度通道为1即Da为1,Dc为灰色的色值
* 源图(画笔设置了Alpha为0),即(mPath绘制的区域)Sa为0
* 在mPath绘制的区域内目标图Da =1,Dc = Color.GRAY,源图Sa = 0;
* 则[Sa * Da, Sa * Dc] = [0*1,0*Color.GRAY]=[0,0]
* 即mPath区域内是全透明的
*/
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawPath(mPath, paint);
}
/**
* 根据touch事件记录手指的触摸路径
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(x, y);
break;
}
invalidate();
return true;
}
}
- 4.解释已经在代码中进行注释了,运行结果图如下:
中奖了一个妹纸;
最后还要感谢一下各位,获益匪浅:
http://blog.csdn.net/iispring/article/details/50472485
http://www.jianshu.com/p/d11892bbe055
网友评论