Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義控件三部曲之繪圖篇(十一)——Paint之setXfermode(二)

自定義控件三部曲之繪圖篇(十一)——Paint之setXfermode(二)

編輯:關於Android編程

這篇文章將逐個講解每個模式的意義。這裡所講的各種模式,在大家理解了之後可以回過頭來看看setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.XXXX));中的混合過程,其實在PorterDuffColorFilter中的混合過程與這裡的setXfermode()設置混合模式的計算方式和效果是完全相同的,只是在PorterDuffColorFilter中只能使用純色彩,而且是完全覆蓋在圖片上方;而setXfermode()則不同,它只會在目標圖像和源圖像交合的位置起作用,而且源圖像不一定是純色的。

在開始講解之前,我們隨便拿一個效果圖來看一下,我們在這個效果圖中需要關注哪兩點

\

對應代碼:

 

canvas.drawBitmap(dstBmp, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
canvas.drawBitmap(srcBmp,width/2,height/2, mPaint);
前面在講canvas的髒區域更新時,已經提到,在最後一句計算效果圖像時,是以源圖像所在區域為計算目標的,把計算後的源圖像更新到對應區域內。
所以如上圖所示,我們在計算源圖像所在區域效果圖時,需要著重關注兩個區域:
1、如圖標示區域一:區域一是源圖像和目標圖像的相交區域,由於在這個區域源圖像和目標圖像像素都不是空白像素,所以可以明顯看出顏色的計算效果。
2、如圖標示區域二:在區域二中,源圖像所在區域的目標圖像是空白像素,所以這塊區域所表示的意義就是,當某一方區域是空白像素時,此時的計算結果。
總而言之:我們在下面的各個模式計算時,只需要關注圖示中的區域一和區域二;其中區域一表示當源圖像和目標圖像像素都不是空白像素時的計算結果,而區域二則表示當某一方區域是空白像素時,此時的計算結果。

一、顏色疊加相關模式

這部分涉及到的幾個模式有Mode.ADD(飽和度相加)、Mode.DARKEN(變暗),Mode.LIGHTEN(變亮)、Mode.MULTIPLY(正片疊底)、Mode.OVERLAY(疊加),Mode.SCREEN(濾色)

1、Mode.ADD(飽和度相加)

它的公式是Saturate(S + D);ADD模式簡單來說就是對SRC與DST兩張圖片相交區域的飽和度進行相加
同樣使用上篇中的示例,一個矩形,一個圓形來做相加
代碼如下:(再次帖出代碼,這將是最後一次帖出示例代碼了,下面都是基於這個例子的基礎上,具體的代碼講解,上篇已經講過,這裡就不再綴述了)
public class MyView extends View {
    private int width = 400;
    private int height = 400;
    private Bitmap dstBmp;
    private Bitmap srcBmp;
    private Paint mPaint;

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
        srcBmp = makeSrc(width, height);
        dstBmp = makeDst(width, height);
        mPaint = new Paint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int layerID = canvas.saveLayer(0,0,width*2,height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
        canvas.drawBitmap(srcBmp,width/2,height/2, mPaint);
        mPaint.setXfermode(null);

        canvas.restoreToCount(layerID);

    }

    // create a bitmap with a circle, used for the "dst" image
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0, w, h), p);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFF66AAFF);
        c.drawRect(0, 0, w, h, p);
        return bm;
    }
}
與上篇文章唯一不同的地方在於,只是把模式改為了Mode.ADD
效果圖如下:
\

 

從效果圖中可以看出,只有源圖與目標圖像相交的部分的圖像的飽和度產生了變化,沒相交的部分是沒有變的,因為對方的飽和度是0,當然不相交的位置飽和度是不會變的。
這個模式的應用范圍比較少,暫時想不到哪裡會用到;

2、Mode.LIGHTEN(變亮)

它的算法是: [Sa + Da - Sa*Da,Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
圓形和矩形的效果圖為:

 

\

這個效果比較容易理解,兩個圖像重合的區域才會有顏色值變化,所以只有重合區域才有變亮的效果,源圖像非重合的區域,由於對應區域的目標圖像是空白像素,所以直接顯示源圖像。
大家對公式其實不必理解,用過Photoshop的同學,應該都知道圖層模式裡的“變亮”模式,在博客《自定義控件三部曲之繪圖篇(九)——Paint之setColorFilter》中已經講過在photoshop中的操作方法。
所以大家想知道結果,直接拿目標圖像和源圖像在photoshop中模擬一下就可以得到結果;
我們在實際應用中,會有下面的這個情況,當選中一本書時,給這本書加上燈光效果

\

其實它是兩張圖合成的:
DST:目標圖像

\

SRC:源圖像

\

可以看到,在這張圖片的最上方中間的位置有些白色半透明的填充,其它位置都是透明的。
下面我們來看一下代碼:

 

public class LightBookView extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC;

    public LightBookView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.book_bg,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.book_light,null);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        //先畫書架,做為目標圖像
        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
        //再圖光點
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);

    }
}
意思就是先把書架做為目標圖像畫在底層,然後給mBitPaint設置上PorterDuffXfermode,然後將處理過的源圖蓋在目標圖像上。代碼難度不大,就不再細講。

3、Mode.DARKEN(變暗)

對應公式是: [Sa + Da - Sa*Da,Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
示例圖像為:
\

 

同樣是對應photoshop中的混合模式中的變暗模式;同樣大家可以在photoshop中就可以得到效果,就不再細講了。

4、Mode.MULTIPLY(正片疊底)

公式是:[Sa * Da, Sc * Dc]
示例圖像為:

 

\

有些同學會奇怪了,Photoshop中也有正片疊底啊,相交區域正片疊底後的顏色確實是綠色的,但源圖像的非相交區域怎麼沒了?
我們來看下他的計算公式:[Sa * Da, Sc * Dc],在計算alpha值時的公式是Sa * Da,是用源圖像的alpha值乘以目標圖像的alpha值;由於源圖像的非相交區域所對應的目標圖像像素的alpha是0,所以結果像素的alpha值仍是0,所以源圖像的非相交區域在計算後是透明的。
在兩個圖像的相交區域的混合方式是與photoshop中的正片疊底效果是一致的。

5、Mode.OVERLAY(疊加)

這個沒有給出公式……
示例圖像為:

 

\

雖然沒有給出公式,但從效果圖中可以看到,源圖像交合部分有效果,非交合部分依然是存在的,這就可以肯定一點,當目標圖像透明時,在這個模式下源圖像的色值不會受到影響;

6、Mode.SCREEN(濾色)

對應公式是:[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
示例圖像為:

 

\

同樣,只是源圖像與目標圖像交合部分有效果,源圖像非交合部分保持原樣。

到這裡,這六種混合模式就講完了,我們下面總結一下:
1、這幾種模式都是PhotoShop中存在的模式,是通過計算改變交合區域的顏色值的
2、除了Mode.MULTIPLY(正片疊底)會在目標圖像透明時將結果對應區域置為透明,其它圖像都不受目標圖像透明像素影響,即源圖像非交合部分保持原樣。

 

7、示例——twitter標識的描邊效果

由於這些模式在photoshop中都存在,直接拿目標圖像和源圖像在photoshop中就可以演示出來,就沒有多舉例子,其實,在實現時實現兩圖像混合時,也經常會用到這些模式的,比如這裡twitter的暗光效果
我們這裡有兩張源圖:
圖一:

 

\

圖二:

\

然後完成的效果如下:

\

我們先想想這個要實現的效果有哪些特性:
首先,
在圖一中,小鳥整個都是藍色的
在圖二中,只有小鳥的邊緣部分是白色的,中間部分是透明的。
在最終的合成圖中:圖一和圖二中小鳥與邊緣的是顯示的,而且還有某種效果,但小鳥中間的區域變透明了!顯示的是底部Activity的背景色。
想到我們前面學到的幾種樣式中,只有Mode.MULTIPLY(正片疊底)會在兩個圖像的一方透明時,結果像素就是透明的。所以這裡使用的模式就是Mode.MULTIPLY
對應代碼如下:

 

public class TwitterView extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC;
    public TwitterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.twiter_bg,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.twiter_light,null);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
}
源碼在文章底部給出

二、SRC相關模式

在講完了photoshop中存在的幾個模式以後,還有幾個是在處理結果時以源圖像顯示為主的幾個模式,所以大家在遇到當圖像相交時,需要顯示源圖像時,就需要從這幾個模式中考慮了
這部分所涉及的模式有:Mode.SRC、Mode.SRC_IN、Mode.SRC_OUT、Mode.SRC_OVER、Mode.SRC_ATOP

1、Mode.SRC

計算公式為:[Sa, Sc]
從公式中也可以看出,在處理源圖像所在區域的相交問題時,全部以源圖像顯示
所以示例圖像為:
\

 

 

2、Mode.SRC_IN

計算公式為:[Sa * Da, Sc * Da]
在這個公式中結果值的透明度和顏色值都是由Sa,Sc分別乘以目標圖像的Da來計算的。所以當目標圖像為空白像素時,計算結果也將會為空白像素。
示例圖像為:

 

\

大家注意SRC_IN模式與SRC模式的區別,一般而言,是在相交區域時無論SRC_IN還是SRC模式都是顯示源圖像,而唯一不同的是,當目標圖像是空白像素時,在SRC_IN所對應的區域也將會變成空白像素;
其實更嚴格的來講,SRC_IN模式是在相交時利用目標圖像的透明度來改變源圖像的透明度和飽和度。當目標圖像透明度為0時,源圖像就完全不顯示。
利用這個特性,我們能完成很多功能,比如:

示例1:圖形圓角

我們有兩張圖像:
圖像一:(我們熟悉的小狗 )

 

\

圖像二:(去角遮罩)

\

效果為:

\

可以看到這個遮罩的四個角都是圓形切角,都是透明的。
現在我們需要利用SRC_IN的特性,顯示SRC圖像,但會把目標圖像中空白像素的部分去掉的特性來做了。
由於我們需要最終顯示小狗圖像,所以這裡需要將小狗 圖像做為SRC,將遮罩做為目標圖像
代碼為:

 

public class RoundImageView_SRCIN extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC;

    public RoundImageView_SRCIN(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.dog_shade,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.dog,null);
    }



    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
}

示例2:圖片倒影

前面我們講過,SRC_IN模式是在相交時利用目標圖像的透明度來改變源圖像的透明度和飽和度。所以當目標圖像的透明度在0-255之間時,就會把源圖像的透明度和顏色值都會變小。利用這個特性,我們可以做出倒影效果
\

用到的遮罩為:

\

這個遮罩好像還不太清,它是一個從上到下的白色填充漸變;白色的透明度從49%到0;
對應的代碼為:

 

public class InvertedImageView_SRCIN  extends View{
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC,BmpRevert;
    public InvertedImageView_SRCIN(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.dog_invert_shade,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.dog,null);

        Matrix matrix = new Matrix();
        matrix.setScale(1F, -1F);
        // 生成倒影圖
        BmpRevert = Bitmap.createBitmap(BmpSRC, 0, 0, BmpSRC.getWidth(), BmpSRC.getHeight(), matrix, true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //先畫出小狗圖片
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        //再畫出倒影
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.translate(0,BmpSRC.getHeight());

        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(BmpRevert,0,0,mBitPaint);

        mBitPaint.setXfermode(null);

        canvas.restoreToCount(layerId);
    }
}
代碼中稍微有點難度的地方,可能是在利用小狗圖片生成對應的倒影圖像的位置:
Matrix matrix = new Matrix();
matrix.setScale(1F, -1F);
// 生成倒影圖
BmpRevert = Bitmap.createBitmap(BmpSRC, 0, 0, BmpSRC.getWidth(), BmpSRC.getHeight(), matrix, true);
這裡主要是利用matrix將圖像做一個翻轉,有關位移matrix的知識,我們會在講解canvas的變換時細講,這裡就不再講解了,繼續關注吧。
SRC_IN模式就講這兩個例子吧,其實它的用途是很多的,比如扣圖,你能否利用我們圓角示例的來實現一個扣圖效果呢?自己試試吧

3、Mode.SRC_OUT

計算公式為:[Sa * (1 - Da), Sc * (1 - Da)]
從公式中可以看出,計算結果的透明度=Sa * (1 - Da);也就是說當目標圖像圖像完全透明時,計算結果將是透明的;
示例圖像為:
\

 

從示例圖中也可以看出,源圖像與目標圖像的相交部分由於目標圖像的透明度為100%,所以相交部分的計算結果為空白像素。在目標圖像為空白像素時,完全以源圖像顯示。
所以這個模式的特性可以概括為:以目標圖像的透明度的補值來調節源圖像的透明度和色彩飽和度。即當目標圖像為空白像素時,就完全顯示源圖像,當目標圖像的透明度為100%時,交合區域為空像素。
Mode.SRC_OUT簡單來說,當目標圖像有圖像時結果顯示空白像素,當目標圖像沒有圖像時,結果顯示源圖像。

1、橡皮擦效果

利用這個特性,我們可以實現橡皮擦效果

 

\

原理:我們說了簡單來講Mode.SRC_OUT模式,當目標圖像有圖像時計算結果為空白像素,當目標圖像沒有圖像時,顯示源圖像;
所以我們把手指軌跡做為目標圖像,在與源圖像計算時,有手指軌跡的地方就變為空白像素了,看起來的效果就是被擦除了。
代碼如下:

 

public class DogView_SRCOUT extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC;
    private Path mPath;
    private float mPreX,mPreY;
    public DogView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);

        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(45);

        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.dog,null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        //先把手指軌跡畫到目標Bitmap上
        Canvas c = new Canvas(BmpDST);
        c.drawPath(mPath,mBitPaint);

        //然後把目標圖像畫到畫布上
        canvas.drawBitmap(BmpDST,0,0,mBitPaint);

        //計算源圖像區域
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float endX = (mPreX+event.getX())/2;
                float endY = (mPreY+event.getY())/2;
                mPath.quadTo(mPreX,mPreY,endX,endY);
                mPreX = event.getX();
                mPreY =event.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        postInvalidate();
        return super.onTouchEvent(event);
    }
}

代碼難度不大就不再講了,既然我們實現了擦除效果,那我們稍微加以改進,就變成了刮刮卡效果了

2、刮刮卡效果

首先,搞一個刮刮卡圖片:(guaguaka_pic.png)
\

 

然後再搞個中獎結果:(guagua_text.png)

\

結果如下:

\

對應代碼為:

 

public class GuaGuaCardView_SRCOUT extends View{
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC,BmpText;
    private Path mPath;
    private float mPreX,mPreY;
    public GuaGuaCardView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);

        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(45);

        BmpText = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka_text,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka_pic,null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawBitmap(BmpText,0,0,mBitPaint);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        //先把手指軌跡畫到目標Bitmap上
        Canvas c = new Canvas(BmpDST);
        c.drawPath(mPath,mBitPaint);

        //然後把目標圖像畫到畫布上
        canvas.drawBitmap(BmpDST,0,0,mBitPaint);

        //計算源圖像區域
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float endX = (mPreX+event.getX())/2;
                float endY = (mPreY+event.getY())/2;
                mPath.quadTo(mPreX,mPreY,endX,endY);
                mPreX = event.getX();
                mPreY =event.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        postInvalidate();
        return super.onTouchEvent(event);
    }
}
與上面橡皮擦效果不同的是,在繪圖時,在特效前先把刮刮卡的中獎文字繪在底部,這時候當橡皮擦把刮刮卡的圖片給擦除掉時,就露出底部的刮刮卡的中獎文字了。

4、Mode.SRC_OVER

計算公式為:[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc]
示例圖像為:
\
我們可以看到,在計算結果中,源圖像沒有變。它的意思就是在目標圖像的頂部繪制源圖像。從公式中也可以看出目標圖像的透明度為Sa + (1 - Sa)*Da;即在源圖像的透明度基礎上增加一部分目標圖像的透明度。增加的透明度是源圖像透明度的補量;目標圖像的色彩值的計算方式同理,所以當源圖像透明度為100%時,就原樣顯示源圖像;

5、Mode.SRC_ATOP

計算公式為:[Da, Sc * Da + (1 - Sa) * Dc]
示例圖像為:

 

\

很奇怪,它的效果圖竟然與SRC_IN模式是相同的,我們來對比一下它們的公式:
SRC_IN: [Sa * Da, Sc * Da]
SRC_ATOP:[Da, Sc * Da + (1 - Sa) * Dc]
先看透明度:在SRC_IN中是Sa * Da,在SRC_ATOP是Da
SRC_IN是源圖像透明度乘以目標圖像的透明度做為結果透明度,而SRC_ATOP是直接使用目標圖像的透明度做為結果透明度
再看顏色值:
SRC_IN的顏色值為 Sc * Da,SRC_ATOP的顏色值為Sc * Da + (1 - Sa) * Dc;SRC_ATOP在SRC_IN的基礎上還增加了(1 - Sa) * Dc;
所以總結來了:

1、當透明度只有100%和0%時,SRC_ATOP是SRC_IN是通用的
2、當透明度不只有100%和0%時,SRC_ATOP相比SRC_IN源圖像的飽和度會增加,即會顯得更亮!

所以,前面利用SRC_IN實現的圓角效果是完全可以使用SRC_OUT模式來實現的

1、圓角效果(當透明度只有100%和0%時,SRC_ATOP是SRC_IN是通用的)

效果圖不變:

 

\

代碼中僅將SRC_IN模式改為SRC_OUT模式即可:

public class RoundImageView_SRCOUT extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC;

    public RoundImageView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.dog_shade,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.dog,null);
    }



    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
}

示例2:圖片倒影(顏色飽和度增加)

前面我們講過,當目標圖像的透明度不是100%和0%時,源圖像的顏色值就會比SRC_IN模式的顏色值飽和度增加。
我們來對比下效果:
同樣只需要在將原圖片倒影代碼的SRC_IN模式改成SRC_OUT即可,代碼如下:
public class InvertedImageView_SRCOUT extends View{
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC,BmpRevert;
    public InvertedImageView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.dog_invert_shade,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.dog,null);

        Matrix matrix = new Matrix();
        matrix.setScale(1F, -1F);
        // 生成倒影圖
        BmpRevert = Bitmap.createBitmap(BmpSRC, 0, 0, BmpSRC.getWidth(), BmpSRC.getHeight(), matrix, true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //先畫出小狗圖片
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        //再畫出倒影
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.translate(0,BmpSRC.getHeight());

        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(BmpRevert,0,0,mBitPaint);

        mBitPaint.setXfermode(null);

        canvas.restoreToCount(layerId);
    }
}
效果圖如下:
\

 

然後再來看看原來SRC_IN的效果圖,對比一下:

\

明顯亮度是有增加的。
所以對於SRC_ATOP的結論就出來了,一般而言SRC_ATOP是可以和SRC_IN通用的,但SRC_ATOP所產生的效果圖在目標圖不是透明度不是0或100%的時候,會比SRC_IN模式產生的圖像更亮些;
好了,這篇就先到這裡了,下篇繼續給大家講解xfermode的應用。

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved