ペイントAPI - XfermodeとPorterDuffの詳しい解説(2)
このセクションの紹介:
前のセクションでは、Xfermode の 2 人の亡くなった (時代遅れの) 息子について学びました: AvoidXfermode、PixelXorXfermode、 多少便利ではありますが、結局削除されました。このセクションでは、まだ生きている Xfermode の三男について学びます: PorterDuffXfermode
まず、公式 API ドキュメント:PorterDuffXfermode に注目してみましょう。 !ドキュメントには非常に少ないコンテンツが含まれています。その構築方法は
パラメーターは 1 つだけです:PorterDuff.Mode モード。Android は 16 個の画像混合モードを提供します。 2 つのレイヤーを異なる結果に結合し、異なるモードに従って表示できることがわかります。 16 のミキシング モードの結果は次のとおりです:
ここには 2 つのレイヤーがあります: 最初の画像はデスティネーション ピクチャ (DST)、2 番目の画像は ソース ピクチャ (SRC) です。
もちろん、ドキュメントでは、使用可能なモードは 16 ではなく 18 であることがわかりました。ADD と OVERLAY という 2 つの新しいモードが追加されました
まあ、これ以上言っても無駄です。コードは次のとおりです。実際、このセクションでは、これら 18 のモードを検証するコードを作成します。 追記:ポーターダフという名前は、実際には2つの名前を組み合わせたものです:トーマス・プロターとトム・ダフ、彼らは最初に SIGGRAPH でグラフィック ミキシングの概念を最初に提案した神レベルの人です。興味がある場合は、Baidu をご覧ください~
上の図を検証するための例を書いてください:
それでは、上記を検証するための例を書いてみましょう。さまざまなモードを変更して結果を比較および分析してください。
コード実装:
ステップ 1: まず、画面の幅と高さを取得するツールクラスを作成しましょう。 ScreenUtil.java:
/** * Created by Jay on 2015/10/23 0023. */ public class ScreenUtil { /** * 获取屏幕宽高,sdk17后不建议采用 * * @param context */ public static int[] getScreenHW(Context context) { WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); int width = display.getWidth(); int height = display.getHeight(); int[] HW = new int[] { width, height }; return HW; } /** * 获取屏幕宽高,建议采用 * * @param context */ public static int[] getScreenHW2(Context context) { WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; int height = dm.heightPixels; int[] HW = new int[] { width, height }; return HW; } /** * 获取屏幕的宽度 * * @param context * @return */ public static int getScreenW(Context context) { return getScreenHW2(context)[0]; } /** * 获取屏幕的高度 * * @param context * @return */ public static int getScreenH(Context context) { return getScreenHW2(context)[1]; } }ステップ 2: カスタム View クラスを作成し、ここで実験してください。
XfermodeView.java:
/** * Created by Jay on 2015/10/23 0023. */ public class XfermodeView extends View { private PorterDuffXfermode pdXfermode; //定义PorterDuffXfermode变量 //定义MODE常量,等下直接改这里即可进行测试 private static PorterDuff.Mode PD_MODE = PorterDuff.Mode.ADD; private int screenW, screenH; //屏幕宽高 private int width = 200; //绘制的图片宽高 private int height = 200; private Bitmap srcBitmap, dstBitmap; //上层SRC的Bitmap和下层Dst的Bitmap public XfermodeView(Context context) { this(context, null); } public XfermodeView(Context context, AttributeSet attrs) { super(context, attrs); screenW = ScreenUtil.getScreenW(context); screenH = ScreenUtil.getScreenH(context); //创建一个PorterDuffXfermode对象 pdXfermode = new PorterDuffXfermode(PD_MODE); //实例化两个Bitmap srcBitmap = makeSrc(width, height); dstBitmap = makeDst(width, height); } public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } //定义一个绘制圆形Bitmap的方法 private 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(0xFF26AAD1); c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p); return bm; } //定义一个绘制矩形的Bitmap的方法 private 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(0xFFFFCE43); c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p); return bm; } @Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setFilterBitmap(false); paint.setStyle(Paint.Style.FILL); canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2, (screenH / 2 - height) / 2, paint); canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3, (screenH / 2 - height) / 2, paint); //创建一个图层,在图层上演示图形混合后的效果 int sc = canvas.saveLayer(0, 0, screenW, screenH, 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.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2, (screenH / 2 - height) / 2, paint); //绘制i //设置Paint的Xfermode paint.setXfermode(pdXfermode); canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2, (screenH / 2 - height) / 2, paint); paint.setXfermode(null); // 还原画布 canvas.restoreToCount(sc); } }このコードは複雑に見えますが、実際はそうではありません。画面の幅と高さを取得して、長方形と円を描画するだけです。 位置を計算し、下位レイヤー(固定書き込み方式)を設定し、下位ブラシsetXfermodeを設定し、 キャンバス上に描画しているだけでは理解できないのですが、実際はどうやって位置を決めるのでしょうか? きっと大丈夫だよ!結果を 1 つずつ見てみましょう。
PD_MODE の値を変更するだけで、別のモードに設定できます。
オペレーションレンダリング:
1) PorterDuff.Mode.ADD:
彩度オーバーレイ
2) PorterDuff.Mode.CLEAR:
描画はキャンバスに送信されません。結果は...理由はわかりませんが、それは正常です 何も...
3) PorterDuff.Mode.DARKEN:
2 つのレイヤーのすべての領域を取得し、交差部分の色を濃くします
4) PorterDuff.Mode.DST:
のみ保持しますターゲット画像のアルファとカラーなので、ターゲット画像のみが描画されます
5) PorterDuff.Mode.DST_ATOP:
ソース画像とターゲット画像が交差する場所にターゲット画像が描画され、ソース画像が描画されます交差しない場所
6) PorterDuff.Mode.DST_IN:
2 つが交差する場所にターゲット画像を描画します。描画効果は元の画像の透明度の影響を受けます
7) PorterDuff.Mode.DST_OUT :
交差しない場所にターゲット画像を描画します
8) PorterDuff.Mode.DST_OVER:
ターゲット画像は上に描画されます
9) PorterDuff.Mode.LIGHTEN:
2つのレイヤーを重ねて交差部分の色を点灯
10) PorterDuff.Mode.MULTIPLY:
Take two レイヤーの交差部分を重ね合わせた後の色
11) PorterDuff.Mode.OVERLAY:
Overlay
12 ) PorterDuff.Mode.SCREEN:
2つのレイヤーの領域をすべて取り、交差部分が透明になります
13 ) PorterDuff.Mode.SRC:
ソース画像のアルファとカラーのみを保持するため、ソース画像のみが描画されます
... 図16) PorterDuff.Mode.SRC_OUT: ソース画像を交差しない位置に描画17) PorterDuff.Mode.SRC_OVER: ソース画像を上に描画18) PorterDuff。 Mode.XOR: ソース画像とターゲット画像は、ばらばらの場所にそのまま描画されますこのセクションのサンプル コードをダウンロードします:
PorterDuffXfermodeDemo.zip
このセクションの概要:まあこのセクションでは、これら 18 の異なる PorterDuff.Mode のさまざまな効果を検証するための単純な 1 つのビューを作成しました。 かなり時間はかかりますが、読者にとっては間違いなくより明確に見えますよね~ もちろん、これらは単なる予備的な洞察にすぎません。 の
PorterDuff.Mode
はカスタム コントロールにとって非常に重要です。 このセクションでは予備的な理解ができました。次のセクションでは、練習するための例をいくつか取り上げます。
PorterDuff.Mode についてさらに詳しく知りたい場合は、他の人が書いた優れた記事、Android Paint の setXfermode PorterDuffXfermode の説明
を参照してください。 さて、それでは、明日の朝健康診断があります、今日はここまで書きます~