ペイントAPI - XfermodeとPorterDuffの詳しい解説(2)


このセクションの紹介:

前のセクションでは、Xfermode の 2 人の亡くなった (時代遅れの) 息子について学びました: AvoidXfermodePixelXorXfermode、 多少便利ではありますが、結局削除されました。このセクションでは、まだ生きている Xfermode の三男について学びます: PorterDuffXfermode

まず、公式 API ドキュメント:

PorterDuffXfermode に注目してみましょう。 !ドキュメントには非常に少ないコンテンツが含まれています。その構築方法は

1.png パラメーターは 1 つだけです:

PorterDuff.Mode モード。Android は 16 個の画像混合モードを提供します。 2 つのレイヤーを異なる結果に結合し、異なるモードに従って表示できることがわかります。 16 のミキシング モードの結果は次のとおりです:

2.png ここには 2 つのレイヤーがあります: 最初の画像は

デスティネーション ピクチャ (DST)、2 番目の画像は ソース ピクチャ (SRC) です。

もちろん、ドキュメントでは、使用可能なモードは 16 ではなく 18 であることがわかりました。

ADDOVERLAY という 2 つの新しいモードが追加されました

まあ、これ以上言っても無駄です。コードは次のとおりです。実際、このセクションでは、これら 18 のモードを検証するコードを作成します。

3.gif

追記:ポーターダフという名前は、実際には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:

4.png彩度オーバーレイ

2) PorterDuff.Mode.CLEAR:

5.png 描画はキャンバスに送信されません。結果は...理由はわかりませんが、それは正常です 何も...

3) PorterDuff.Mode.DARKEN:

6.png 2 つのレイヤーのすべての領域を取得し、交差部分の色を濃くします

4) PorterDuff.Mode.DST:

7.png のみ保持しますターゲット画像のアルファとカラーなので、ターゲット画像のみが描画されます

5) PorterDuff.Mode.DST_ATOP:

8.pngソース画像とターゲット画像が交差する場所にターゲット画像が描画され、ソース画像が描画されます交差しない場所

6) PorterDuff.Mode.DST_IN:

9.png2 つが交差する場所にターゲット画像を描画します。描画効果は元の画像の透明度の影響を受けます

7) PorterDuff.Mode.DST_OUT :

10.png交差しない場所にターゲット画像を描画します

8) PorterDuff.Mode.DST_OVER:

11.pngターゲット画像は上に描画されます

9) PorterDuff.Mode.LIGHTEN:

12.png 2つのレイヤーを重ねて交差部分の色を点灯

10) PorterDuff.Mode.MULTIPLY:

13.pngTake two レイヤーの交差部分を重ね合わせた後の色

11) PorterDuff.Mode.OVERLAY:

14.pngOverlay

12 ) PorterDuff.Mode.SCREEN:

15.png 2つのレイヤーの領域をすべて取り、交差部分が透明になります

13 ) PorterDuff.Mode.SRC:

16.pngソース画像のアルファとカラーのみを保持するため、ソース画像のみが描画されます

... 図

16) PorterDuff.Mode.SRC_OUT:

000.png

ソース画像を交差しない位置に描画

17) PorterDuff.Mode.SRC_OVER:

17.png

ソース画像を上に描画

18) PorterDuff。 Mode.XOR:

18.png

ソース画像とターゲット画像は、ばらばらの場所にそのまま描画されます

このセクションのサンプル コードをダウンロードします: 19.png

PorterDuffXfermodeDemo.zip

20.png

このセクションの概要:

まあこのセクションでは、これら 18 の異なる PorterDuff.Mode のさまざまな効果を検証するための単純な 1 つのビューを作成しました。 かなり時間はかかりますが、読者にとっては間違いなくより明確に見えますよね~ もちろん、これらは単なる予備的な洞察にすぎません。

PorterDuffXfermode


PorterDuff.Mode

はカスタム コントロールにとって非常に重要です。 このセクションでは予備的な理解ができました。次のセクションでは、練習するための例をいくつか取り上げます。

PorterDuff.Mode についてさらに詳しく知りたい場合は、他の人が書いた優れた記事、

Android Paint の setXfermode PorterDuffXfermode の説明

を参照してください。 さて、それでは、明日の朝健康診断があります、今日はここまで書きます~