寫過自定義view的小伙伴們可能了解或者是用到過PorterDuff.Mode阻塑,第一次接觸這個東西的時候蓝撇,就感覺這玩意兒挺魔性,可以玩一把陈莽。于是乎就查找了一些關(guān)于PorterDuff.Mode的資料以及一些小Demo渤昌,印象最深的應(yīng)該還是刮獎的那個。廢話不多說走搁,下面就說說我對PorterDuff.Mode的理解和體會独柑,僅代表個人觀點,如有錯誤希望大家指正私植。
- PorterDuff.Mode是什么忌栅?
在android SDK Paint類中有一個很重要的方法setXfermode,這個方法用于設(shè)置圖像的過渡模式曲稼,所謂過渡是指圖像的飽和度索绪、顏色值等參數(shù)的計算結(jié)果的圖像表現(xiàn)。在SDK中Xfermode有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode贫悄,前兩個類在API 16被遺棄了瑞驱,所以這里不作介紹。PorterDuffXfermode類主要用于圖形合成時的圖像過渡模式計算窄坦,其概念來自于1984年在ACM SIGGRAPH計算機圖形學(xué)出版物上發(fā)表了“Compositing digital images(合成數(shù)字圖像)”的Tomas Porter和Tom Duff唤反,合成圖像的概念極大地推動了圖形圖像學(xué)的發(fā)展,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.什么是源圖什么是目標圖藕各?
源圖:其實就是你即將要繪制在當前畫布上的圖池摧。
目標圖:就是你在進行下一步繪制之前當前畫布對應(yīng)繪制區(qū)域的圖;
以下是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() {
//創(chuàng)建一個bitmap(一張圖片)
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
//根據(jù)上面的bitmap創(chuàng)建相同大小的bitmap,此時改bitmap所有的像素點的顏色值全為0竭讳,即全透明的一個bitmap
mDstBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
//創(chuàng)建一個Path對象,后面根據(jù)手的touch事件設(shè)置path
mPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);//抗鋸齒
paint.setDither(true);//防止抖動
paint.setAlpha(0);//設(shè)置Alpha為0
paint.setStyle(Paint.Style.STROKE);//設(shè)置風格為描邊
paint.setStrokeWidth(50);//設(shè)置描邊的寬度
paint.setStrokeCap(Paint.Cap.ROUND);//設(shè)置筆觸為圓角
paint.setStrokeJoin(Paint.Join.ROUND);//設(shè)置繪制轉(zhuǎn)折的的地方為圓角
mCanvas = new Canvas(mDstBitmap);//創(chuàng)建一個畫布在透明的bitmap上進行繪制
mCanvas.drawColor(Color.GRAY);//將透明的bitmap繪制成灰色的浙踢,所有像素點的色值對應(yīng)灰色的值
}
@Override
protected void onDraw(Canvas canvas) {
//第一步繪制一個圖片
canvas.drawBitmap(mBitmap, 0, 0, null);
//在灰色的bitmap上繪制路徑
drawTouchPath();
//將灰色的bitmap繪制到原畫布上
canvas.drawBitmap(mDstBitmap, 0, 0, null);
}
/**
* 關(guān)鍵點绢慢,繪制手指的touch路徑
*/
private void drawTouchPath() {
/*
* 手指觸摸后繪制觸摸路徑,在執(zhí)行此操作時候
* 第一次mDstBitmap全部像素色值是灰色(mCanvas.drawColor(Color.GRAY))----目標圖(當前畫布上的圖)
* 將要繪制的路徑mPath----源圖(將要繪制的圖)
* 模式為:PorterDuff.Mode.DST_IN(這里DST_IN的IN即是數(shù)學(xué)中的交集的意思洛波,指針對mPath和mDstBitmap的交集進行合成胰舆,實際上就是mPath)
* 該模式的算法[Sa * Da, Sa * Dc]
* 目標圖(灰色)的透明度通道為1即Da為1,Dc為灰色的色值
* 源圖(畫筆設(shè)置了Alpha為0),即(mPath繪制的區(qū)域)Sa為0
* 在mPath繪制的區(qū)域內(nèi)目標圖Da =1,Dc = Color.GRAY,源圖Sa = 0;
* 則[Sa * Da, Sa * Dc] = [0*1,0*Color.GRAY]=[0,0]
* 即mPath區(qū)域內(nèi)是全透明的
*/
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawPath(mPath, paint);
}
/**
* 根據(jù)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.解釋已經(jīng)在代碼中進行注釋了蹬挤,運行結(jié)果圖如下:
image.png
中獎了一個妹紙缚窿;
最后還要感謝一下各位,獲益匪淺:
http://blog.csdn.net/iispring/article/details/50472485
http://www.reibang.com/p/d11892bbe055