AndroidBlendMode.android.kt

/*
 * Copyright 2020 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 androidx.compose.ui.graphics

import android.os.Build
import androidx.annotation.RequiresApi

/**
 * Helper method to determine if the appropriate [BlendMode] is supported on the given Android
 * API level this provides an opportunity for consumers to fallback on an alternative user
 * experience for devices that do not support the corresponding blend mode. Usages of [BlendMode]
 * types that are not supported will fallback onto the default of [BlendMode.SrcOver]
 */
actual fun BlendMode.isSupported(): Boolean {
    // All blend modes supported on Android Q /API level 29+
    // For older API levels we first check to see if we are consuming the default BlendMode
    // or SrcOver which is supported on all platforms
    // Otherwise we attempt to convert to the appropriate PorterDuff mode and we get
    // something other than PorterDuff.Mode.SRC_OVER (the default) then we support this BlendMode.
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
        this == BlendMode.SrcOver ||
        toPorterDuffMode() != android.graphics.PorterDuff.Mode.SRC_OVER
}

/**
 * Convert the [BlendMode] to the corresponding [android.graphics.PorterDuff.Mode] if it exists,
 * falling back on the default of [android.graphics.PorterDuff.Mode.SRC_OVER] for unsupported types
 */
internal fun BlendMode.toPorterDuffMode(): android.graphics.PorterDuff.Mode = when (this) {
    BlendMode.Clear -> android.graphics.PorterDuff.Mode.CLEAR
    BlendMode.Src -> android.graphics.PorterDuff.Mode.SRC
    BlendMode.Dst -> android.graphics.PorterDuff.Mode.DST
    BlendMode.SrcOver -> android.graphics.PorterDuff.Mode.SRC_OVER
    BlendMode.DstOver -> android.graphics.PorterDuff.Mode.DST_OVER
    BlendMode.SrcIn -> android.graphics.PorterDuff.Mode.SRC_IN
    BlendMode.DstIn -> android.graphics.PorterDuff.Mode.DST_IN
    BlendMode.SrcOut -> android.graphics.PorterDuff.Mode.SRC_OUT
    BlendMode.DstOut -> android.graphics.PorterDuff.Mode.DST_OUT
    BlendMode.SrcAtop -> android.graphics.PorterDuff.Mode.SRC_ATOP
    BlendMode.DstAtop -> android.graphics.PorterDuff.Mode.DST_ATOP
    BlendMode.Xor -> android.graphics.PorterDuff.Mode.XOR
    BlendMode.Plus -> android.graphics.PorterDuff.Mode.ADD
    BlendMode.Screen -> android.graphics.PorterDuff.Mode.SCREEN
    BlendMode.Overlay -> android.graphics.PorterDuff.Mode.OVERLAY
    BlendMode.Darken -> android.graphics.PorterDuff.Mode.DARKEN
    BlendMode.Lighten -> android.graphics.PorterDuff.Mode.LIGHTEN
    BlendMode.Modulate -> {
        // b/73224934 Android PorterDuff Multiply maps to Skia Modulate
        android.graphics.PorterDuff.Mode.MULTIPLY
    }
    // Always return SRC_OVER as the default if there is no valid alternative
    else -> android.graphics.PorterDuff.Mode.SRC_OVER
}

/**
 * Convert the compose [BlendMode] to the underlying Android platform [android.graphics.BlendMode]
 */
@RequiresApi(Build.VERSION_CODES.Q)
internal fun BlendMode.toAndroidBlendMode(): android.graphics.BlendMode = when (this) {
    BlendMode.Clear -> android.graphics.BlendMode.CLEAR
    BlendMode.Src -> android.graphics.BlendMode.SRC
    BlendMode.Dst -> android.graphics.BlendMode.DST
    BlendMode.SrcOver -> android.graphics.BlendMode.SRC_OVER
    BlendMode.DstOver -> android.graphics.BlendMode.DST_OVER
    BlendMode.SrcIn -> android.graphics.BlendMode.SRC_IN
    BlendMode.DstIn -> android.graphics.BlendMode.DST_IN
    BlendMode.SrcOut -> android.graphics.BlendMode.SRC_OUT
    BlendMode.DstOut -> android.graphics.BlendMode.DST_OUT
    BlendMode.SrcAtop -> android.graphics.BlendMode.SRC_ATOP
    BlendMode.DstAtop -> android.graphics.BlendMode.DST_ATOP
    BlendMode.Xor -> android.graphics.BlendMode.XOR
    BlendMode.Plus -> android.graphics.BlendMode.PLUS
    BlendMode.Modulate -> android.graphics.BlendMode.MODULATE
    BlendMode.Screen -> android.graphics.BlendMode.SCREEN
    BlendMode.Overlay -> android.graphics.BlendMode.OVERLAY
    BlendMode.Darken -> android.graphics.BlendMode.DARKEN
    BlendMode.Lighten -> android.graphics.BlendMode.LIGHTEN
    BlendMode.ColorDodge -> android.graphics.BlendMode.COLOR_DODGE
    BlendMode.ColorBurn -> android.graphics.BlendMode.COLOR_BURN
    BlendMode.Hardlight -> android.graphics.BlendMode.HARD_LIGHT
    BlendMode.Softlight -> android.graphics.BlendMode.SOFT_LIGHT
    BlendMode.Difference -> android.graphics.BlendMode.DIFFERENCE
    BlendMode.Exclusion -> android.graphics.BlendMode.EXCLUSION
    BlendMode.Multiply -> android.graphics.BlendMode.MULTIPLY
    BlendMode.Hue -> android.graphics.BlendMode.HUE
    BlendMode.Saturation -> android.graphics.BlendMode.SATURATION
    BlendMode.Color -> android.graphics.BlendMode.COLOR
    BlendMode.Luminosity -> android.graphics.BlendMode.LUMINOSITY
    // Always return SRC_OVER as the default if there is no valid alternative
    else -> android.graphics.BlendMode.SRC_OVER
}