Paint API - Xfermode 및 PorterDuff에 대한 자세한 설명(2)


이 섹션 소개:

이전 섹션에서 우리는 Xfermode의 사망한(구식) 두 아들에 대해 배웠습니다: AvoidXfermode, PixelXorXfermode, 다소 유용하긴 하지만 결국 제거되었습니다. 이 섹션에서는 아직 살아있는 Xfermode의 셋째 아들에 대해 알아봅니다. PorterDuffXfermode

먼저 공식 API 문서인 PorterDuffXfermode에 주목해 보겠습니다. ! 문서에는 내용이 거의 포함되어 있지 않습니다.

1.png

매개변수는 하나뿐입니다: PorterDuff.Mode 모드이며 Android는 16가지 이미지 혼합 모드를 제공합니다. 두 개의 레이어를 결합하여 서로 다른 결과를 얻을 수 있고 서로 다른 모드에 따라 표시할 수 있다는 것을 이해합니다! 16가지 믹싱 모드의 결과는 다음과 같습니다.

2.png

여기에는 두 개의 레이어가 있습니다. 첫 번째 사진은 대상 사진(DST)이고 두 번째 사진은 원본 사진(SRC)입니다!

물론 문서에서 사용 가능한 모드는 16개가 아니라 18개이며 두 가지 새로운 모드인 ADDOVERLAY

음, 더 이상 말할 필요도 없습니다. 실제로는 코드가 가장 좋습니다. , 이 섹션에서는 이러한 18가지 모드를 확인하는 코드를 작성합니다! 3.gif

PS: PorterDuff라는 이름은 실제로 두 이름의 조합입니다: Tomas Proter와 Tom Duff. SIGGRAPH에서 그래픽 믹싱 개념을 처음 제안한 신급자님. 관심 있으신 분들은 바이두 부탁드려요~


위 사진을 검증하는 예시를 작성해주세요:

자, 위를 검증하는 예시를 작성해보겠습니다. 그림. 다양한 모드를 수정하여 결과를 비교하고 분석해보세요!

코드 구현:

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를 설정한 다음 그냥 캔버스에 그린 것일 뿐인데, 그리는 위치의 계산이 이해가 안 될 수도 있습니다. 실제로는 위치를 어떻게 결정하지? 괜찮을 거예요! 그럼 결과를 하나씩 살펴보겠습니다. PD_MODE 값을 수정하고 다른 모드로 설정하기만 하면 됩니다!

작업 렌더링:

1) PorterDuff.Mode.ADD:

4.pngSaturation overlay

2) PorterDuff.Mode.CLEAR:

5.png그림이 캔버스에 제출되지 않습니다. 결과는... 왜인지는 모르겠지만, 정상입니다 아무것도...

3) PorterDuff.Mode.DARKEN:

6.png 두 레이어의 모든 영역을 가져오고 교차 부분의 색상을 깊게 만듭니다.

4) PorterDuff.Mode.DST:

7.png 유지만 대상 이미지의 알파와 색상이므로 대상 이미지만 그려집니다.

5) PorterDuff.Mode.DST_ATOP:

8.png원본 이미지와 대상 이미지가 교차하는 곳에 대상 이미지가 그려지고, 원본 이미지가 그려집니다.

6) PorterDuff.Mode.DST_IN:

9.png둘이 교차하는 곳에 대상 이미지를 그리면 원본 이미지의 투명도에 따라 그리기 효과가 영향을 받습니다

7) PorterDuff.Mode.DST_OUT :

10.png교차하지 않는 곳에 대상 이미지를 그립니다.

8) PorterDuff.Mode.DST_OVER:

11.png대상 이미지는 위에 그려집니다.

9) PorterDuff.Mode.LIGHTEN:

12.png모든 영역을 차지합니다. 두 레이어를 겹쳐서 교차 색상을 켜세요

10) PorterDuff.Mode.MULTIPLY:

13.png두 레이어를 겹쳐서 레이어의 교차 색상을 겹칩니다

11) PorterDuff.Mode.OVERLAY:

14.pngoverlay

12) PorterDuff.Mode.SCREEN:

15.png두 레이어의 모든 영역을 가져오면 교차 부분이 투명해집니다

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의 다양한 효과를 확인하기 위해 간단한 뷰를 작성했습니다. 시간이 많이 걸리긴 하지만 독자들에게는 확실히 훨씬 더 명확해 보이죠~ 물론 이것은 단지 예비적인 통찰력일 뿐입니다!

PorterDuffXfermode


PorterDuff.Mode

는 사용자 정의 컨트롤에 매우 중요합니다! 이 섹션에서는 예비적인 이해를 가졌습니다. 다음 섹션에서는 연습할 몇 가지 예를 선택하겠습니다.

PorterDuff.Mode에 대한 더 자세한 소개를 보려면 다음을 참조하세요.

Android Paint의 setXfermode PorterDuffXfermode 설명

, 다른 사람들이 쓴 좋은 글! 그럼 됐고 내일 아침에 신체검사를 하고 오늘은 이것까지 쓰겠습니다~