/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.graphics;

import android.annotation.NonNull;
import android.annotation.Nullable;

public enum BlendMode {

    /**
     * {@usesMathJax}
     *
     * <p>
     *  <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_PLUS.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>
     */
    PLUS(12),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.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>
     *
     */
    MODULATE(13),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_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),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_DODGE.png" />
     *     <figcaption>Makes destination brighter to reflect source.</figcaption>
     * </p>
     * <p>
     *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
     * </p>
     * <p>
     *      \begin{equation}
     *      C_{out} =
     *      \begin{cases}
     *          C_{src} * (1 - \alpha_{dst}) & C_{dst} = 0 \\
     *          C_{src} + \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = \alpha_{src} \\
     *          \alpha_{src} * min(\alpha_{dst}, C_{dst} * \alpha_{src}/(\alpha_{src} - C_{src}))
     *              + C_{src} *(1 - \alpha_{dst} + \alpha_{dst}*(1 - \alpha_{src}) & otherwise
     *      \end{cases}
     *      \end{equation}
     * </p>
     */
    COLOR_DODGE(18),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_BURN.png" />
     *     <figcaption>Makes destination darker to reflect source.</figcaption>
     * </p>
     * <p>
     *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
     * </p>
     * <p>
     *     \begin{equation}
     *     C_{out} =
     *     \begin{cases}
     *         C_{dst} + C_{src}*(1 - \alpha_{dst}) & C_{dst} = \alpha_{dst} \\
     *         \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = 0 \\
     *         \alpha_{src}*(\alpha_{dst} - min(\alpha_{dst}, (\alpha_{dst}
     *         - C_{dst})*\alpha_{src}/C_{src}))
     *         + C_{src} * (1 - \alpha_{dst}) + \alpha_{dst}*(1-\alpha_{src}) & otherwise
     *     \end{cases}
     *     \end{equation}
     * </p>
     */
    COLOR_BURN(19),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_HARD_LIGHT.png" />
     *     <figcaption>Makes destination lighter or darker, depending on source.</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} & C_{src}*(1-\alpha_{dst}) + C_{dst}*(1-\alpha_{src}) + 2*C_{src}
     *              \leq \alpha_{src} \\
     *           \alpha_{src}*\alpha_{dst}- 2*(\alpha_{dst} - C_{dst})*(\alpha_{src} - C_{src})
     *              & otherwise
     *      \end{cases}
     *      \end{equation}
     * </p>
     */
    HARD_LIGHT(20),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SOFT_LIGHT.png" />
     *     <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
     * </p>
     * <p>
     *     Where
     *       \begin{equation}
     *       m =
     *          \begin{cases}
     *              C_{dst} / \alpha_{dst} & \alpha_{dst} \gt 0 \\
     *              0 & otherwise
     *          \end{cases}
     *       \end{equation}
     * </p>
     * <p>
     *       \begin{equation}
     *       g =
     *          \begin{cases}
     *              (16 * m * m + 4 * m) * (m - 1) + 7 * m & 4 * C_{dst} \leq \alpha_{dst} \\
     *              \sqrt m - m & otherwise
     *          \end{cases}
     *       \end{equation}
     * </p>
     * <p>
     *       \begin{equation}
     *       f =
     *          \begin{cases}
     *              C_{dst} * (\alpha_{src} + (2 * C_{src} - \alpha_{src}) * (1 - m))
     *                  & 2 * C_{src} \leq \alpha_{src} \\
     *              C_{dst} * \alpha_{src} + \alpha_{dst} * (2 * C_{src} - \alpha_{src}) * g
     *                  & otherwise
     *          \end{cases}
     *       \end{equation}
     * </p>
     * <p>
     *       \begin{equation}
     *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
     *       \end{equation}
     *       \begin{equation}
     *          C_{out} = C_{src} / \alpha_{dst} + C_{dst} / \alpha_{src} + f
     *       \end{equation}
     * </p>
     */
    SOFT_LIGHT(21),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
     *     <figcaption>Subtracts darker from lighter with higher contrast.</figcaption>
     * </p>
     * <p>
     *     \begin{equation}
     *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
     *     \end{equation}
     * </p>
     * <p>
     *     \begin{equation}
     *           C_{out} = C_{src} + C_{dst} - 2 * min(C_{src}
     *                       * \alpha_{dst}, C_{dst} * \alpha_{src})
     *     \end{equation}
     * </p>
     */
    DIFFERENCE(22),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
     *     <figcaption>Subtracts darker from lighter with lower contrast.</figcaption>
     * </p>
     * <p>
     *     \begin{equation}
     *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
     *     \end{equation}
     * </p>
     * <p>
     *     \begin{equation}
     *          C_{out} = C_{src} + C_{dst} - 2 * C_{src} * C_{dst}
     *     \end{equation}
     * </p>
     */
    EXCLUSION(23),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
     *     <figcaption>Multiplies the source and destination pixels.</figcaption>
     * </p>
     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
     * <p>\(C_{out} =
     *      C_{src} * (1 - \alpha_{dst}) + C_{dst} * (1 - \alpha_{src}) + (C_{src} * C_{dst})\)
     * </p>
     */
    MULTIPLY(24),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_HUE.png" />
     *     <figcaption>
     *         Replaces hue of destination with hue of source, leaving saturation
     *         and luminosity unchanged.
     *     </figcaption>
     * </p>
     */
    HUE(25),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SATURATION.png" />
     *     <figcaption>
     *          Replaces saturation of destination saturation hue of source, leaving hue and
     *          luminosity unchanged.
     *     </figcaption>
     * </p>
     */
    SATURATION(26),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR.png" />
     *     <figcaption>
     *          Replaces hue and saturation of destination with hue and saturation of source,
     *          leaving luminosity unchanged.
     *     </figcaption>
     * </p>
     */
    COLOR(27),

    /**
     * {@usesMathJax}
     *
     * <p>
     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_LUMINOSITY.png" />
     *     <figcaption>
     *          Replaces luminosity of destination with luminosity of source, leaving hue and
     *          saturation unchanged.
     *     </figcaption>
     * </p>
     */
    LUMINOSITY(28);

    private static final BlendMode[] BLEND_MODES = values();

    /**
     * @hide
     */
    public static @Nullable BlendMode fromValue(int value) {
        for (BlendMode mode : BLEND_MODES) {
            if (mode.mXfermode.porterDuffMode == value) {
                return mode;
            }
        }
        return null;
    }

    /**
     * @hide
     */
    public static int toValue(BlendMode mode) {
        return mode.getXfermode().porterDuffMode;
    }

    /**
     * @hide
     */
    public static @Nullable PorterDuff.Mode blendModeToPorterDuffMode(@Nullable BlendMode mode) {
        if (mode != null) {
            switch (mode) {
                case CLEAR:
                    return PorterDuff.Mode.CLEAR;
                case SRC:
                    return PorterDuff.Mode.SRC;
                case DST:
                    return PorterDuff.Mode.DST;
                case SRC_OVER:
                    return PorterDuff.Mode.SRC_OVER;
                case DST_OVER:
                    return PorterDuff.Mode.DST_OVER;
                case SRC_IN:
                    return PorterDuff.Mode.SRC_IN;
                case DST_IN:
                    return PorterDuff.Mode.DST_IN;
                case SRC_OUT:
                    return PorterDuff.Mode.SRC_OUT;
                case DST_OUT:
                    return PorterDuff.Mode.DST_OUT;
                case SRC_ATOP:
                    return PorterDuff.Mode.SRC_ATOP;
                case DST_ATOP:
                    return PorterDuff.Mode.DST_ATOP;
                case XOR:
                    return PorterDuff.Mode.XOR;
                case DARKEN:
                    return PorterDuff.Mode.DARKEN;
                case LIGHTEN:
                    return PorterDuff.Mode.LIGHTEN;
                // b/73224934 PorterDuff Multiply maps to Skia Modulate
                case MODULATE:
                    return PorterDuff.Mode.MULTIPLY;
                case SCREEN:
                    return PorterDuff.Mode.SCREEN;
                case PLUS:
                    return PorterDuff.Mode.ADD;
                case OVERLAY:
                    return PorterDuff.Mode.OVERLAY;
                default:
                    return null;
            }
        } else {
            return null;
        }
    }

    @NonNull
    private final Xfermode mXfermode;

    BlendMode(int mode) {
        mXfermode = new Xfermode();
        mXfermode.porterDuffMode = mode;
    }

    /**
     * @hide
     */
    @NonNull
    public Xfermode getXfermode() {
        return mXfermode;
    }
}
