動腦學院UI預習資料-Paint(Xfermode)

weixin_34377065發表於2018-06-20

前言
影象混合模式
在之前的Paint的使用當中我們提到了高階渲染和濾鏡,那麼今天我們來學習最後一個內容點Xfermode,我們能通過使用Xfermode能夠完成影象組合的效果

1.XFermode
在使用Paint的時候,我們能通過使用Xfermode能夠完成影象組合的效果將繪製的圖形的畫素和Canvas上對應位置的畫素按照一定的規則進行混合,形成新的畫素,再更新到Canvas中形成最終的圖形,那圖具體效果見下圖

11462765-330723c65f0fe4fd.png
谷歌官方-PorterDuffXfermode.png
11462765-353f4ed4614ddecb.png
這個是我寫的一個demo.png

看到上述圖形,其實這個時候我們能夠很明顯的知道我們的XFermode其實實際上就是在使用圖形之間的相互組合以達到自己的想要的目的, 他提供了16種組合模式

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和色值影響,如果完全不透明則相交處完全不繪製

這個方法跟我們上節課講到的setColorFilter蠻相似的。
但是我門可以看到谷歌官方所提供的和我們自己寫的還是有一些差異, 那麼我門需要來了解到XFermode的本質到底是什麼,上面開篇我們有提到過,XFermode是將繪製的圖形的畫素和Canvas上對應位置的畫素按照一定的規則進行混合,那麼我們需要知道他到底是怎麼進行混合,如何進行計算的
這個時候我門走到原始碼當中,檢視API文件發現其果然有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode這三個子類實現的功能要比setColorFilter的三個子類複雜得多。
由於AvoidXfermode, PixelXorXfermode都已經被標註為過時了,所以這次主要研究的是仍然在使用的PorterDuffXfermode

      /**
       * <p>Specialized implementation of {@link Paint}'s
       * {@link Paint#setXfermode(Xfermode) transfer mode}. Refer to the
       * documentation of the {@link PorterDuff.Mode} enum for more
       * information on the available alpha compositing and blending modes.      
       */
      public class PorterDuffXfermode extends Xfermode {
          /**
           * Create an xfermode that uses the specified porter-duff mode.
           *
           * @param mode           The porter-duff mode that is applied
           */
          public PorterDuffXfermode(PorterDuff.Mode mode) {
              porterDuffMode = mode.nativeInt;
          }
      }

那麼在這裡我們可以看到其實實際上這個類非常簡單, 我門可以認為他就是一個常量類標註了16種模式, 而在這裡真正的模式實在mode當中(下面粗略看一下就行)

    public class PorterDuff {
/**
 * {@usesMathJax}
 *
 * <h3>Porter-Duff</h3>
 *
 * <p>The name of the parent class is an homage to the work of Thomas Porter and
 * Tom Duff, presented in their seminal 1984 paper titled "Compositing Digital Images".
 * In this paper, the authors describe 12 compositing operators that govern how to
 * compute the color resulting of the composition of a source (the graphics object
 * to render) with a destination (the content of the render target).</p>
 *
 * <p>"Compositing Digital Images" was published in <em>Computer Graphics</em>
 * Volume 18, Number 3 dated July 1984.</p>
 *
 * <p>Because the work of Porter and Duff focuses solely on the effects of the alpha
 * channel of the source and destination, the 12 operators described in the original
 * paper are called alpha compositing modes here.</p>
 *
 * <p>For convenience, this class also provides several blending modes, which similarly
 * define the result of compositing a source and a destination but without being
 * constrained to the alpha channel. These blending modes are not defined by Porter
 * and Duff but have been included in this class for convenience purposes.</p>
 *
 * <h3>Diagrams</h3>
 *
 * <p>All the example diagrams presented below use the same source and destination
 * images:</p>
 *
 * <table summary="Source and Destination" style="background-color: transparent;">
 *     <tr>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SRC.png" />
 *             <figcaption>Source image</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DST.png" />
 *             <figcaption>Destination image</figcaption>
 *         </td>
 *     </tr>
 * </table>
 *
 * <p>The order of drawing operations used to generate each diagram is shown in the
 * following code snippet:</p>
 *
 * <pre class="pretty print"
 * Paint paint = new Paint();
 * canvas.drawBitmap(destinationImage, 0, 0, paint);
 *
 * PorterDuff.Mode mode = // choose a mode
 * paint.setXfermode(new PorterDuffXfermode(mode));
 *
 * canvas.drawBitmap(sourceImage, 0, 0, paint);
 * </pre>

 *
 * <h3>Alpha compositing modes</h3>
 *
 * <table summary="Alpha compositing modes" style="background-color: transparent;">
 *     <tr>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SRC.png" />
 *             <figcaption>{@link #SRC Source}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OVER.png" />
 *             <figcaption>{@link #SRC_OVER Source Over}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SRC_IN.png" />
 *             <figcaption>{@link #SRC_IN Source In}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SRC_ATOP.png" />
 *             <figcaption>{@link #SRC_ATOP Source Atop}</figcaption>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DST.png" />
 *             <figcaption>{@link #DST Destination}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DST_OVER.png" />
 *             <figcaption>{@link #DST_OVER Destination Over}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DST_IN.png" />
 *             <figcaption>{@link #DST_IN Destination In}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DST_ATOP.png" />
 *             <figcaption>{@link #DST_ATOP Destination Atop}</figcaption>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_CLEAR.png" />
 *             <figcaption>{@link #CLEAR Clear}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OUT.png" />
 *             <figcaption>{@link #SRC_OUT Source Out}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DST_OUT.png" />
 *             <figcaption>{@link #DST_OUT Destination Out}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_XOR.png" />
 *             <figcaption>{@link #XOR Exclusive Or}</figcaption>
 *         </td>
 *     </tr>
 * </table>
 *
 * <h3>Blending modes</h3>
 *
 * <table summary="Blending modes" style="background-color: transparent;">
 *     <tr>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_DARKEN.png" />
 *             <figcaption>{@link #DARKEN Darken}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_LIGHTEN.png" />
 *             <figcaption>{@link #LIGHTEN Lighten}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_MULTIPLY.png" />
 *             <figcaption>{@link #MULTIPLY Multiply}</figcaption>
 *         </td>
 *     </tr>
 *     <tr>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_SCREEN.png" />
 *             <figcaption>{@link #SCREEN Screen}</figcaption>
 *         </td>
 *         <td style="border: none; text-align: center;">
 *             <img src="{@docRoot}reference/android/images/graphics/composite_OVERLAY.png" />
 *             <figcaption>{@link #OVERLAY Overlay}</figcaption>
 *         </td>
 *     </tr>
 * </table>
 *
 * <h3>Compositing equations</h3>
 *
 * <p>The documentation of each individual alpha compositing or blending mode below
 * provides the exact equation used to compute alpha and color value of the result
 * of the composition of a source and destination.</p>
 *
 * <p>The result (or output) alpha value is noted \(\alpha_{out}\). The result (or output)
 * color value is noted \(C_{out}\).</p>
 */
public enum Mode {
    // these value must match their native equivalents. See SkXfermode.h
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_CLEAR.png" />
     *     <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = 0\)</p>
     * <p>\(C_{out} = 0\)</p>
     */
    CLEAR       (0),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC.png" />
     *     <figcaption>The source pixels replace the destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src}\)</p>
     * <p>\(C_{out} = C_{src}\)</p>
     */
    SRC         (1),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST.png" />
     *     <figcaption>The source pixels are discarded, leaving the destination intact.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{dst}\)</p>
     */
    DST         (2),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OVER.png" />
     *     <figcaption>The source pixels are drawn over the destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    SRC_OVER    (3),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_OVER.png" />
     *     <figcaption>The source pixels are drawn behind the destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{dst} + (1 - \alpha_{dst}) * \alpha_{src}\)</p>
     * <p>\(C_{out} = C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
     */
    DST_OVER    (4),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_IN.png" />
     *     <figcaption>Keeps the source pixels that cover the destination pixels,
     *     discards the remaining source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} * \alpha_{dst}\)</p>
     */
    SRC_IN      (5),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_IN.png" />
     *     <figcaption>Keeps the destination pixels that cover source pixels,
     *     discards the remaining source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{dst} * \alpha_{src}\)</p>
     */
    DST_IN      (6),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OUT.png" />
     *     <figcaption>Keeps the source pixels that do not cover destination pixels.
     *     Discards source pixels that cover destination pixels. Discards all
     *     destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src}\)</p>
     */
    SRC_OUT     (7),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_OUT.png" />
     *     <figcaption>Keeps the destination pixels that are not covered by source pixels.
     *     Discards destination pixels that are covered by source pixels. Discards all
     *     source pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = (1 - \alpha_{src}) * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    DST_OUT     (8),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_ATOP.png" />
     *     <figcaption>Discards the source pixels that do not cover destination pixels.
     *     Draws remaining source pixels over destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
     * <p>\(C_{out} = \alpha_{dst} * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    SRC_ATOP    (9),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_ATOP.png" />
     *     <figcaption>Discards the destination pixels that are not covered by source pixels.
     *     Draws remaining destination pixels over source pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src}\)</p>
     * <p>\(C_{out} = \alpha_{src} * C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
     */
    DST_ATOP    (10),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_XOR.png" />
     *     <figcaption>Discards the source and destination pixels where source pixels
     *     cover destination pixels. Draws remaining source pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    XOR         (11),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DARKEN.png" />
     *     <figcaption>Retains the smallest component of the source and
     *     destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})\)</p>
     */
    DARKEN      (16),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_LIGHTEN.png" />
     *     <figcaption>Retains the largest component of the source and
     *     destination pixel.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})\)</p>
     */
    LIGHTEN     (17),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_MULTIPLY.png" />
     *     <figcaption>Multiplies the source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} * C_{dst}\)</p>
     */
    MULTIPLY    (13),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SCREEN.png" />
     *     <figcaption>Adds the source and destination pixels, then subtracts the
     *     source pixels multiplied by the destination.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}\)</p>
     */
    SCREEN      (14),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_ADD.png" />
     *     <figcaption>Adds the source pixels to the destination pixels and saturates
     *     the result.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = max(0, min(\alpha_{src} + \alpha_{dst}, 1))\)</p>
     * <p>\(C_{out} = max(0, min(C_{src} + C_{dst}, 1))\)</p>
     */
    ADD         (12),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_OVERLAY.png" />
     *     <figcaption>Multiplies or screens the source and destination depending on the
     *     destination color.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(\begin{equation}
     * C_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\
     * \alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) & otherwise \end{cases}
     * \end{equation}\)</p>
     */
    OVERLAY     (15);

    Mode(int nativeInt) {
        this.nativeInt = nativeInt;
    }

    /**
     * @hide
     */
    public final int nativeInt;
}

/**
 * @hide
 */
public static int modeToInt(Mode mode) {
    return mode.nativeInt;
}

/**
 * @hide
 */
public static Mode intToMode(int val) {
    switch (val) {
        default:
        case  0: return Mode.CLEAR;
        case  1: return Mode.SRC;
        case  2: return Mode.DST;
        case  3: return Mode.SRC_OVER;
        case  4: return Mode.DST_OVER;
        case  5: return Mode.SRC_IN;
        case  6: return Mode.DST_IN;
        case  7: return Mode.SRC_OUT;
        case  8: return Mode.DST_OUT;
        case  9: return Mode.SRC_ATOP;
        case 10: return Mode.DST_ATOP;
        case 11: return Mode.XOR;
        case 16: return Mode.DARKEN;
        case 17: return Mode.LIGHTEN;
        case 13: return Mode.MULTIPLY;
        case 14: return Mode.SCREEN;
        case 12: return Mode.ADD;
        case 15: return Mode.OVERLAY;
    }
}

}

這個時候我們會發現比較的懵逼,都是html是什麼狀況?那麼這個時候我幫大家做了個處理將這些註釋貼到了一個html當中(具體見課件)給大家翻譯了一下

11462765-c44a7b460c281c5c.png
image.png

11462765-31231f889c5bc0c3.png
image.png

11462765-aa7eee4bed96b677.png
image.png

那麼這裡通過翻譯出來之後我們可以很明顯知道,這個類當中所定義的是這些16種模式的常量以及16模式所能達到的效果,以及踏實如何進行計算的。

那麼這裡我們就上面寫的幾個重點進行分析

      The result (or output) alpha value is noted \(\alpha_{out}\). The result (or output)
      color value is noted \(C_{out}\).
      alpha的結果或者輸出被標記為 \(\alpha_{out}\).  顏色值的結果或輸出被標記為\(C_{out}\).

從這句話其實我門可以看出,他寫的很明白
在上述的過程中有提到(\alpha_{out})表示透明輸出值(C_{out}).表示是顏色輸出值,那麼其實就是通過兩個圖形計算的到這兩個關鍵輸出值就是構成我們圖形混合的最大祕密
而下面就是具體的那些計算公式

       /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_CLEAR.png" />
     *     <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = 0\)</p>
     * <p>\(C_{out} = 0\)</p>
     */
    CLEAR       (0),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC.png" />
     *     <figcaption>The source pixels replace the destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src}\)</p>
     * <p>\(C_{out} = C_{src}\)</p>
     */
    SRC         (1),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST.png" />
     *     <figcaption>The source pixels are discarded, leaving the destination intact.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{dst}\)</p>
     */
    DST         (2),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OVER.png" />
     *     <figcaption>The source pixels are drawn over the destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    SRC_OVER    (3),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_OVER.png" />
     *     <figcaption>The source pixels are drawn behind the destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{dst} + (1 - \alpha_{dst}) * \alpha_{src}\)</p>
     * <p>\(C_{out} = C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
     */
    DST_OVER    (4),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_IN.png" />
     *     <figcaption>Keeps the source pixels that cover the destination pixels,
     *     discards the remaining source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} * \alpha_{dst}\)</p>
     */
    SRC_IN      (5),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_IN.png" />
     *     <figcaption>Keeps the destination pixels that cover source pixels,
     *     discards the remaining source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{dst} * \alpha_{src}\)</p>
     */
    DST_IN      (6),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_OUT.png" />
     *     <figcaption>Keeps the source pixels that do not cover destination pixels.
     *     Discards source pixels that cover destination pixels. Discards all
     *     destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src}\)</p>
     */
    SRC_OUT     (7),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_OUT.png" />
     *     <figcaption>Keeps the destination pixels that are not covered by source pixels.
     *     Discards destination pixels that are covered by source pixels. Discards all
     *     source pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = (1 - \alpha_{src}) * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    DST_OUT     (8),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SRC_ATOP.png" />
     *     <figcaption>Discards the source pixels that do not cover destination pixels.
     *     Draws remaining source pixels over destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
     * <p>\(C_{out} = \alpha_{dst} * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    SRC_ATOP    (9),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DST_ATOP.png" />
     *     <figcaption>Discards the destination pixels that are not covered by source pixels.
     *     Draws remaining destination pixels over source pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src}\)</p>
     * <p>\(C_{out} = \alpha_{src} * C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
     */
    DST_ATOP    (10),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_XOR.png" />
     *     <figcaption>Discards the source and destination pixels where source pixels
     *     cover destination pixels. Draws remaining source pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
     */
    XOR         (11),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_DARKEN.png" />
     *     <figcaption>Retains the smallest component of the source and
     *     destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})\)</p>
     */
    DARKEN      (16),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_LIGHTEN.png" />
     *     <figcaption>Retains the largest component of the source and
     *     destination pixel.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})\)</p>
     */
    LIGHTEN     (17),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_MULTIPLY.png" />
     *     <figcaption>Multiplies the source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} * C_{dst}\)</p>
     */
    MULTIPLY    (13),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_SCREEN.png" />
     *     <figcaption>Adds the source and destination pixels, then subtracts the
     *     source pixels multiplied by the destination.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}\)</p>
     */
    SCREEN      (14),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_ADD.png" />
     *     <figcaption>Adds the source pixels to the destination pixels and saturates
     *     the result.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = max(0, min(\alpha_{src} + \alpha_{dst}, 1))\)</p>
     * <p>\(C_{out} = max(0, min(C_{src} + C_{dst}, 1))\)</p>
     */
    ADD         (12),
    /**
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/composite_OVERLAY.png" />
     *     <figcaption>Multiplies or screens the source and destination depending on the
     *     destination color.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(\begin{equation}
     * C_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\
     * \alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) & otherwise \end{cases}
     * \end{equation}\)</p>
     */
    OVERLAY     (15);

我們一個畫素的顏色都是由四個分量組成,即ARGB,A表示的是我們Alpha值,RGB表示的是顏色
S表示的是原畫素,原畫素的值表示[Sa,Sc] Sa表示的就是源畫素的Alpha值,Sc表示源畫素的顏色值
藍色矩形表示的是原圖片,黃色圓表示的是目標圖片
在上述的計算公式當中我們關注幾個關鍵點
alpha------透明度
C------顏色
src------原圖
dst------目標圖
out------輸出

把這幾個弄清楚,其實我們的xfermode在我門面前就沒有任何的祕密可言了

那麼注意, 這個是27版本的,與之前版本計算規則上肯定有所差異, 其實我們看版本的原始碼更好理解寫,沒有這麼複雜那麼在此貼住以前老版本(21)的作為參考比對

public class PorterDuff {

// these value must match their native equivalents. See SkPorterDuff.h
public enum Mode {
    /** [0, 0] */
    CLEAR       (0),
    /** [Sa, Sc] */
    SRC         (1),
    /** [Da, Dc] */
    DST         (2),
    /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
    SRC_OVER    (3),
    /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
    DST_OVER    (4),
    /** [Sa * Da, Sc * Da] */
    SRC_IN      (5),
    /** [Sa * Da, Sa * Dc] */
    DST_IN      (6),
    /** [Sa * (1 - Da), Sc * (1 - Da)] */
    SRC_OUT     (7),
    /** [Da * (1 - Sa), Dc * (1 - Sa)] */
    DST_OUT     (8),
    /** [Da, Sc * Da + (1 - Sa) * Dc] */
    SRC_ATOP    (9),
    /** [Sa, Sa * Dc + Sc * (1 - Da)] */
    DST_ATOP    (10),
    /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
    XOR         (11),
    /** [Sa + Da - Sa*Da,
         Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
    DARKEN      (12),
    /** [Sa + Da - Sa*Da,
         Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
    LIGHTEN     (13),
    /** [Sa * Da, Sc * Dc] */
    MULTIPLY    (14),
    /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
    SCREEN      (15),
    /** Saturate(S + D) */
    ADD         (16),
    OVERLAY     (17);

    Mode(int nativeInt) {
        this.nativeInt = nativeInt;
    }

    /**
     * @hide
     */
    public final int nativeInt;
}
}

二、混合模式分類

我門可以將混合模式分為三個型別(注意看模式常量的名字)

1、SRC類

----優先顯示的是源圖片

SRC [Sa, Sc] ---- 處理圖片相交區域時,總是顯示的是原圖片

SRC_IN [Sa * Da, Sc * Da] ---- 處理圖片相交區域時,受到目標圖片的Alpha值影響
當我們的目標圖片為空白畫素的時候,目標圖片也會變成空白
簡單的來說就是用目標圖片的透明度來改變源圖片的透明度和飽和度,當目標圖片的透明度為0時,源圖片就不會顯示

SRC_OUT [Sa * (1 - Da), Sc * (1 - Da)] --- 同SRC_IN類似 (1 - Da)
用我們目標圖片的透明度的補值來改變源圖片的透明度和飽和度,當目標圖片的透明度為不透明時,源圖片就不會顯示

SRC_ATOP [Da, Sc * Da + (1 - Sa) * Dc]
---- 當透明度為100%和0%時,SRC_IN 和 SRC_ATOP是通用的
當透明度不為上述的兩個值時,SRC_ATOP 比 SRC_IN 源影象的飽和度會增

2、DST類

----優先顯示的是目標圖片

DST_IN [Sa * Da, Sa * Dc] ----- 對比一下SRC_IN,正好和我們SRC_IN想法,在相交的時候以源圖片的透明度來改變目標圖片的透明度和飽和度
當源圖片的透明度為0的時候,目標圖片完全不顯示

3、其他的疊加效果

MULTIPLY[Sa * Da, Sc * Dc] ---

        應用:可以把圖片的輪廓取出來
            LIGHTEN -- 變亮
         書架 頭頂燈光變亮效果

那麼到這裡我們已經基本瞭解了XFermode的情況,我們來看下具體是如何使用的,從使用角度來講soeasy, 只需要在Paint當中呼叫一句程式碼( paint.setXfermode(Mode)就能完成
下面是官方給出的demo

 public class sampleActivity extends AppCompatActivity {
// 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*3/4, h*3/4), 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(w/3, h/3, w*19/20, h*19/20, p);
    return bm;
}
@Override
protected 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 row
    private Bitmap mSrcB;
    private Bitmap mDstB;
    private Shader mBG;     // background checker-board pattern
    private 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 pattern
        Bitmap 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 border
            paint.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 pattern
            paint.setStyle(Paint.Style.FILL);
            paint.setShader(mBG);
            canvas.drawRect(x, y, x + W, y + H, paint);
            // draw the src/dst example into our offscreen bitmap
            int 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 label
            canvas.drawText(sLabels[i],
                    x + W/2, y - labelP.getTextSize()/2, labelP);
            x += W + 10;
            // wrap around when we've drawn enough for one row
            if ((i % ROW_MAX) == ROW_MAX - 1) {
                x = 0;
                y += H + 30;
            }
        }
    }
}
}

同學們可以去試著使用一下

作者:動腦學院Barry老師
原創部落格,請註明轉載處....
原文連結:https://www.jianshu.com/p/713584d018fc
來源:簡書(暱稱BarryKerwin)

相關文章