图像混合模式:Android Paint Xfermode 使用和demo
一、setXfermode(Xfermode xfermode)
Xfermode國外有大神稱之為過渡模式,這種翻譯比較貼切但恐怕不易理解,大家也可以直接稱之為圖像混合模式,因為所謂的“過渡”其實就是圖像混合的一種,這個方法跟我們上面講到的setColorFilter蠻相似的。查看API文檔發現其果然有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,這三個子類實現的功能要比setColorFilter的三個子類復雜得多。
由于AvoidXfermode, PixelXorXfermode都已經被標注為過時了,所以這次主要研究的是仍然在使用的PorterDuffXfermode:
?
二、PorterDuffXfermode
該類同樣有且只有一個含參的構造方法PorterDuffXfermode(PorterDuff.Mode mode),雖說構造方法的簽名列表里只有一個PorterDuff.Mode的參數,但是它可以實現很多酷斃的圖形效果!!而PorterDuffXfermode就是圖形混合模式的意思,其概念最早來自于SIGGRAPH的Tomas Proter和Tom Duff,混合圖形的概念極大地推動了圖形圖像學的發展,延伸到計算機圖形圖像學像Adobe和AutoDesk公司著名的多款設計軟件都可以說一定程度上受到影響,而我們PorterDuffXfermode的名字也來源于這倆人的人名組合PorterDuff,那PorterDuffXfermode能做些什么呢?我們先來看一張API DEMO里的圖片:
這張圖片從一定程度上形象地說明了圖形混合的作用,兩個圖形一圓一方通過一定的計算產生不同的組合效果,在API中Android為我們提供了18種(比上圖多了兩種ADD和OVERLAY)模式:
ADD:飽和相加,對圖像飽和度進行相加,不常用
CLEAR:清除圖像
DARKEN:變暗,較深的顏色覆蓋較淺的顏色,若兩者深淺程度相同則混合
DST:只顯示目標圖像
DST_ATOP:在源圖像和目標圖像相交的地方繪制【目標圖像】,在不相交的地方繪制【源圖像】,相交處的效果受到源圖像和目標圖像alpha的影響
DST_IN:只在源圖像和目標圖像相交的地方繪制【目標圖像】,繪制效果受到源圖像對應地方透明度影響
DST_OUT:只在源圖像和目標圖像不相交的地方繪制【目標圖像】,在相交的地方根據源圖像的alpha進行過濾,源圖像完全不透明則完全過濾,完全透明則不過濾
DST_OVER:將目標圖像放在源圖像上方
LIGHTEN:變亮,與DARKEN相反,DARKEN和LIGHTEN生成的圖像結果與Android對顏色值深淺的定義有關
MULTIPLY:正片疊底,源圖像素顏色值乘以目標圖像素顏色值除以255得到混合后圖像像素顏色值
OVERLAY:疊加
SCREEN:濾色,色調均和,保留兩個圖層中較白的部分,較暗的部分被遮蓋
SRC:只顯示源圖像
SRC_ATOP:在源圖像和目標圖像相交的地方繪制【源圖像】,在不相交的地方繪制【目標圖像】,相交處的效果受到源圖像和目標圖像alpha的影響
SRC_IN:只在源圖像和目標圖像相交的地方繪制【源圖像】
SRC_OUT:只在源圖像和目標圖像不相交的地方繪制【源圖像】,相交的地方根據目標圖像的對應地方的alpha進行過濾,目標圖像完全不透明則完全過濾,完全透明則不過濾
SRC_OVER:將源圖像放在目標圖像上方
XOR:在源圖像和目標圖像相交的地方之外繪制它們,在相交的地方受到對應alpha和色值影響,如果完全不透明則相交處完全不繪制
?
而上面這張圖片對應的官方DEMO代碼如下:https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/Xfermodes.java
public class sampleActivity extends AppCompatActivity {// create a bitmap with a circle, used for the "dst" imagestatic 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*3/4, h*3/4), p);return bm;}// create a bitmap with a rect, used for the "src" imagestatic 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(w/3, h/3, w*19/20, h*19/20, p);return bm;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new SampleView(this));}private static class SampleView extends View {private static final int W = 200;private static final int H = 200;private static final int ROW_MAX = 4; // number of samples per rowprivate Bitmap mSrcB;private Bitmap mDstB;private Shader mBG; // background checker-board patternprivate static final Xfermode[] sModes = {new PorterDuffXfermode(PorterDuff.Mode.CLEAR),new PorterDuffXfermode(PorterDuff.Mode.SRC),new PorterDuffXfermode(PorterDuff.Mode.DST),new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),new PorterDuffXfermode(PorterDuff.Mode.DST_IN),new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),new PorterDuffXfermode(PorterDuff.Mode.XOR),new PorterDuffXfermode(PorterDuff.Mode.DARKEN),new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),new PorterDuffXfermode(PorterDuff.Mode.SCREEN)};private static final String[] sLabels = {"Clear", "Src", "Dst", "SrcOver","DstOver", "SrcIn", "DstIn", "SrcOut","DstOut", "SrcATop", "DstATop", "Xor","Darken", "Lighten", "Multiply", "Screen"};public SampleView(Context context) {super(context);mSrcB = makeSrc(W, H);mDstB = makeDst(W, H);// make a ckeckerboard patternBitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,Bitmap.Config.RGB_565);mBG = new BitmapShader(bm,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);Matrix m = new Matrix();m.setScale(6, 6);mBG.setLocalMatrix(m);}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);labelP.setTextAlign(Paint.Align.CENTER);Paint paint = new Paint();paint.setFilterBitmap(false);canvas.translate(15, 35);int x = 0;int y = 0;for (int i = 0; i < sModes.length; i++) {// draw the borderpaint.setStyle(Paint.Style.STROKE);paint.setShader(null);canvas.drawRect(x - 0.5f, y - 0.5f,x + W + 0.5f, y + H + 0.5f, paint);// draw the checker-board patternpaint.setStyle(Paint.Style.FILL);paint.setShader(mBG);canvas.drawRect(x, y, x + W, y + H, paint);// draw the src/dst example into our offscreen bitmapint sc = canvas.saveLayer(x, y, x + W, y + H, null,Canvas.MATRIX_SAVE_FLAG |Canvas.CLIP_SAVE_FLAG |Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |Canvas.FULL_COLOR_LAYER_SAVE_FLAG |Canvas.CLIP_TO_LAYER_SAVE_FLAG);canvas.translate(x, y);canvas.drawBitmap(mDstB, 0, 0, paint);paint.setXfermode(sModes[i]);canvas.drawBitmap(mSrcB, 0, 0, paint);paint.setXfermode(null);canvas.restoreToCount(sc);// draw the labelcanvas.drawText(sLabels[i],x + W/2, y - labelP.getTextSize()/2, labelP);x += W + 10;// wrap around when we've drawn enough for one rowif ((i % ROW_MAX) == ROW_MAX - 1) {x = 0;y += H + 30;}}}} }?
對于上面這些mode的詳細介紹在GA_STUDIO的這篇文章和AIGESTUDIO的這篇文章都有非常詳盡的介紹
?
三、使用場景
以下是PorterDuffXfermode的一些使用場景:
1、自定義loading樣式:
代碼如下:
public class LogoLoadingView extends View {private int totalW,totalH;private Paint paint;private Bitmap bitmap;private int currentTop;private RectF rectF;private PorterDuffXfermode xfermode;public LogoLoadingView(Context context) {super(context);init();}public LogoLoadingView(Context context, AttributeSet attrs) {super(context, attrs);init();}private void init(){paint=new Paint();paint.setAntiAlias(true);//設置抗鋸齒paint.setStyle(Paint.Style.FILL);//設置填充樣式paint.setDither(true);//設定是否使用圖像抖動處理,會使繪制出來的圖片顏色更加平滑和飽滿,圖像更加清晰paint.setFilterBitmap(true);//加快顯示速度,本設置項依賴于dither和xfermode的設置bitmap= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);//從資源文件中解析獲取Bitmapxfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);/*** 設置當前矩形的高度為0*/currentTop=bitmap.getHeight();rectF=new RectF(0,currentTop,bitmap.getWidth(),bitmap.getHeight());}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(bitmap.getWidth(),bitmap.getHeight());}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);rectF.top=currentTop;/*** 設置View的離屏緩沖。在繪圖的時候新建一個“層”,所有的操作都在該層而不會影響該層以外的圖像* 必須設置,否則設置的PorterDuffXfermode會無效,具體原因不明*/int sc=canvas.saveLayer(0,0,totalW,totalH,paint,Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(bitmap,0,0,null);paint.setXfermode(xfermode);paint.setColor(Color.RED);canvas.drawRect(rectF,paint);paint.setXfermode(null);/*** 還原畫布,與canvas.saveLayer配套使用*/canvas.restoreToCount(sc);if (currentTop>0){currentTop--;postInvalidate();}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);totalW=w;totalH=h;} }?
2、圓形圖片
代碼如下: public class CircleImageView extends View {private int resId;private Bitmap bitmap;private Paint paint;private int bitmapWidth,bitmapHeight;private int size;private PorterDuffXfermode xfermode;public CircleImageView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);resId=array.getResourceId(R.styleable.CircleImageView_imageRes,R.mipmap.ic_launcher);array.recycle();init();}private void init(){paint=new Paint(Paint.ANTI_ALIAS_FLAG);paint.setDither(true);//設定是否使用圖像抖動處理,會使繪制出來的圖片顏色更加平滑和飽滿,圖像更加清晰paint.setFilterBitmap(true);//加快顯示速度,本設置項依賴于dither和xfermode的設置bitmap= BitmapFactory.decodeResource(getResources(),resId);bitmapWidth=bitmap.getWidth();bitmapHeight=bitmap.getHeight();xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP);size=Math.min(bitmapWidth,bitmapHeight);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(size,size);}/*** 生成圓形Bitmap* @return*/private Bitmap makeCircle(){Bitmap bitmap=Bitmap.createBitmap(size,size, Bitmap.Config.ARGB_8888);Canvas canvas=new Canvas(bitmap);Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);paint.setColor(Color.BLUE);paint.setStyle(Paint.Style.FILL);int radius=size/2;canvas.drawCircle(size/2,size/2,radius,paint);return bitmap;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int sc=canvas.saveLayer(0,0,size,size,paint,Canvas.ALL_SAVE_FLAG);Bitmap dst=makeCircle();canvas.drawBitmap(dst,0,0,paint);paint.setXfermode(xfermode);canvas.drawBitmap(bitmap,0,0,paint);paint.setXfermode(null);canvas.restoreToCount(sc);} }?
對應屬性定義:
<?xml version="1.0" encoding="utf-8"?> <resources><declare-styleable name="CircleImageView"><attr name="imageRes" format="reference"/></declare-styleable> </resources>關于圓形圖片的詳細介紹可以看鴻洋大大的這篇博文,介紹的非常詳盡:
http://blog.csdn.net/lmj623565791/article/details/42094215
?
關于PorterDuffXfermode的應用還有非常多,這里簡單介紹這幾個。
以上內容大部分參考自:
http://blog.csdn.net/tianjian4592/article/details/44783283? ? ?//詳解Xfermode
http://blog.csdn.net/aigestudio/article/details/41316141? ? ? ?//把api玩出了ps的效果,圖片處理大牛
http://www.cnblogs.com/tianzhijiexian/p/4297172.html? ? ? ? //也是詳解
?
原文鏈接:https://www.cnblogs.com/libertycode/p/6290497.html
總結
以上是生活随笔為你收集整理的图像混合模式:Android Paint Xfermode 使用和demo的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个方法搞定安卓路由跳转
- 下一篇: 某些情况下安卓引入so冲突的解决