Added support for additional Blend Modes

Created new BlendMode enum that maps to XferModes with additional
supported native values that map to SkBlendMode constants
Created new BlendModeColorFilter
Deprecated PorterDuffColorMode and PorterDuffColorFilter in
favor of BlendMode and BlendModeColorFilter.
Updated Paint API to use setBlendMode and deprecated setXfermode

Bug: 119671925
Bug: 112166079
Bug: 73224934
Test: Added CTS tests for BlendMode and BlendModeColorFilter
Change-Id: Idd50e2f0b81975c5ecf6f1c0edd4eb68198d9131
This commit is contained in:
Nader Jawad
2018-11-16 11:22:32 -08:00
parent f66699ae16
commit 55e49d8816
40 changed files with 714 additions and 15 deletions

View File

@@ -13417,6 +13417,46 @@ package android.graphics {
ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
}
public final class BlendMode extends java.lang.Enum {
method public static android.graphics.BlendMode valueOf(java.lang.String);
method public static final android.graphics.BlendMode[] values();
enum_constant public static final android.graphics.BlendMode CLEAR;
enum_constant public static final android.graphics.BlendMode COLOR;
enum_constant public static final android.graphics.BlendMode COLOR_BURN;
enum_constant public static final android.graphics.BlendMode COLOR_DODGE;
enum_constant public static final android.graphics.BlendMode DARKEN;
enum_constant public static final android.graphics.BlendMode DIFFERENCE;
enum_constant public static final android.graphics.BlendMode DST;
enum_constant public static final android.graphics.BlendMode DST_ATOP;
enum_constant public static final android.graphics.BlendMode DST_IN;
enum_constant public static final android.graphics.BlendMode DST_OUT;
enum_constant public static final android.graphics.BlendMode DST_OVER;
enum_constant public static final android.graphics.BlendMode EXCLUSION;
enum_constant public static final android.graphics.BlendMode HARD_LIGHT;
enum_constant public static final android.graphics.BlendMode HUE;
enum_constant public static final android.graphics.BlendMode LIGHTEN;
enum_constant public static final android.graphics.BlendMode LUMINOSITY;
enum_constant public static final android.graphics.BlendMode MODULATE;
enum_constant public static final android.graphics.BlendMode MULTIPLY;
enum_constant public static final android.graphics.BlendMode OVERLAY;
enum_constant public static final android.graphics.BlendMode PLUS;
enum_constant public static final android.graphics.BlendMode SATURATION;
enum_constant public static final android.graphics.BlendMode SCREEN;
enum_constant public static final android.graphics.BlendMode SOFT_LIGHT;
enum_constant public static final android.graphics.BlendMode SRC;
enum_constant public static final android.graphics.BlendMode SRC_ATOP;
enum_constant public static final android.graphics.BlendMode SRC_IN;
enum_constant public static final android.graphics.BlendMode SRC_OUT;
enum_constant public static final android.graphics.BlendMode SRC_OVER;
enum_constant public static final android.graphics.BlendMode XOR;
}
public final class BlendModeColorFilter extends android.graphics.ColorFilter {
ctor public BlendModeColorFilter(int, android.graphics.BlendMode);
method public int getColor();
method public android.graphics.BlendMode getMode();
}
public class BlurMaskFilter extends android.graphics.MaskFilter {
ctor public BlurMaskFilter(float, android.graphics.BlurMaskFilter.Blur);
}
@@ -13479,7 +13519,8 @@ package android.graphics {
method public void drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint);
method public void drawCircle(float, float, float, android.graphics.Paint);
method public void drawColor(int);
method public void drawColor(int, android.graphics.PorterDuff.Mode);
method public deprecated void drawColor(int, android.graphics.PorterDuff.Mode);
method public void drawColor(int, android.graphics.BlendMode);
method public void drawDoubleRoundRect(android.graphics.RectF, float, float, android.graphics.RectF, float, float, android.graphics.Paint);
method public void drawDoubleRoundRect(android.graphics.RectF, float[], android.graphics.RectF, float[], android.graphics.Paint);
method public void drawLine(float, float, float, float, android.graphics.Paint);
@@ -14098,6 +14139,7 @@ package android.graphics {
method public float descent();
method public boolean equalsForTextMeasurement(android.graphics.Paint);
method public int getAlpha();
method public android.graphics.BlendMode getBlendMode();
method public int getColor();
method public android.graphics.ColorFilter getColorFilter();
method public boolean getFillPath(android.graphics.Path, android.graphics.Path);
@@ -14152,7 +14194,7 @@ package android.graphics {
method public float getUnderlinePosition();
method public float getUnderlineThickness();
method public float getWordSpacing();
method public android.graphics.Xfermode getXfermode();
method public deprecated android.graphics.Xfermode getXfermode();
method public boolean hasGlyph(java.lang.String);
method public final boolean isAntiAlias();
method public final boolean isDither();
@@ -14172,6 +14214,7 @@ package android.graphics {
method public void setARGB(int, int, int, int);
method public void setAlpha(int);
method public void setAntiAlias(boolean);
method public void setBlendMode(android.graphics.BlendMode);
method public void setColor(int);
method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
method public void setDither(boolean);
@@ -14205,7 +14248,7 @@ package android.graphics {
method public android.graphics.Typeface setTypeface(android.graphics.Typeface);
method public void setUnderlineText(boolean);
method public void setWordSpacing(float);
method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
method public deprecated android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
field public static final int CURSOR_AFTER = 0; // 0x0
field public static final int CURSOR_AT = 4; // 0x4
@@ -14487,7 +14530,7 @@ package android.graphics {
enum_constant public static final android.graphics.PorterDuff.Mode XOR;
}
public class PorterDuffColorFilter extends android.graphics.ColorFilter {
public deprecated class PorterDuffColorFilter extends android.graphics.ColorFilter {
ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
}
@@ -15040,7 +15083,7 @@ package android.graphics.drawable {
method public final void setCallback(android.graphics.drawable.Drawable.Callback);
method public void setChangingConfigurations(int);
method public abstract void setColorFilter(android.graphics.ColorFilter);
method public void setColorFilter(int, android.graphics.PorterDuff.Mode);
method public deprecated void setColorFilter(int, android.graphics.PorterDuff.Mode);
method public deprecated void setDither(boolean);
method public void setFilterBitmap(boolean);
method public void setHotspot(float, float);

View File

@@ -36,7 +36,7 @@ public:
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SafeUnref));
}
static jlong CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
return reinterpret_cast<jlong>(SkColorFilter::MakeModeFilter(srcColor, mode).release());
}
@@ -61,8 +61,8 @@ static const JNINativeMethod colorfilter_methods[] = {
{"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer }
};
static const JNINativeMethod porterduff_methods[] = {
{ "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter },
static const JNINativeMethod blendmode_methods[] = {
{ "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter },
};
static const JNINativeMethod lighting_methods[] = {
@@ -76,8 +76,10 @@ static const JNINativeMethod colormatrix_methods[] = {
int register_android_graphics_ColorFilter(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
NELEM(colorfilter_methods));
android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", porterduff_methods,
NELEM(porterduff_methods));
android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", blendmode_methods,
NELEM(blendmode_methods));
android::RegisterMethodsOrDie(env, "android/graphics/BlendModeColorFilter", blendmode_methods,
NELEM(blendmode_methods));
android::RegisterMethodsOrDie(env, "android/graphics/LightingColorFilter", lighting_methods,
NELEM(lighting_methods));
android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",

View File

@@ -845,12 +845,23 @@ namespace PaintGlue {
static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch");
static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch");
static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch");
static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch");
static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch");
static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch");
static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch");
static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch");
static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch");
static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch");
static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch");
SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
Paint* paint = reinterpret_cast<Paint*>(paintHandle);

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -241,10 +241,22 @@ public abstract class BaseCanvas {
nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
/**
* @deprecated use {@link Canvas#drawColor(int, BlendMode)}
*/
@Deprecated
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
}
/**
* Make lint happy.
* See {@link Canvas#drawColor(int, BlendMode)}
*/
public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
}
public void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {
throwIfHasHwBitmapInSwMode(paint);

View File

@@ -201,11 +201,20 @@ public class BaseRecordingCanvas extends Canvas {
nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
/**
* @deprecated use {@link #drawColor(int, BlendMode)} instead
*/
@Deprecated
@Override
public final void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
}
@Override
public final void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
}
@Override
public final void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {

View File

@@ -0,0 +1,472 @@
/*
* 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 {
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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),
/**
* <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;
}
@NonNull
private final Xfermode mXfermode;
BlendMode(int mode) {
mXfermode = new Xfermode();
mXfermode.porterDuffMode = mode;
}
/**
* @hide
*/
@NonNull
public Xfermode getXfermode() {
return mXfermode;
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.ColorInt;
import android.annotation.NonNull;
/**
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link BlendMode}.
*/
public final class BlendModeColorFilter extends ColorFilter {
@ColorInt final int mColor;
private final BlendMode mMode;
public BlendModeColorFilter(@ColorInt int color, @NonNull BlendMode mode) {
mColor = color;
mMode = mode;
}
/**
* Returns the ARGB color used to tint the source pixels when this filter
* is applied.
*
* @see Color
*
*/
@ColorInt
public int getColor() {
return mColor;
}
/**
* Returns the Porter-Duff mode used to composite this color filter's
* color with the source pixel when this filter is applied.
*
* @see BlendMode
*
*/
public BlendMode getMode() {
return mMode;
}
@Override
long createNativeInstance() {
return native_CreateBlendModeFilter(mColor, mMode.getXfermode().porterDuffMode);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
final BlendModeColorFilter other = (BlendModeColorFilter) object;
return other.mMode == mMode;
}
@Override
public int hashCode() {
return 31 * mMode.hashCode() + mColor;
}
private static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
}

View File

@@ -1684,11 +1684,25 @@ public class Canvas extends BaseCanvas {
*
* @param color the color to draw with
* @param mode the porter-duff mode to apply to the color
*
* @deprecated use {@link #drawColor(int, BlendMode)} instead
*/
@Deprecated
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
super.drawColor(color, mode);
}
/**
* Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
* blendmode.
*
* @param color the color to draw with
* @param mode the blendmode to apply to the color
*/
public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
super.drawColor(color, mode);
}
/**
* Draw a line segment with the specified start and stop x,y coordinates, using the specified
* paint.

View File

@@ -1168,11 +1168,28 @@ public class Paint {
* Get the paint's transfer mode object.
*
* @return the paint's transfer mode (or null)
*
* @deprecated use {@link #getBlendMode()} instead
*/
@Deprecated
public Xfermode getXfermode() {
return mXfermode;
}
/**
* Get the paint's blend mode object.
*
* @return the paint's blend mode (or null)
*/
@Nullable
public BlendMode getBlendMode() {
if (mXfermode == null) {
return null;
} else {
return BlendMode.fromValue(mXfermode.porterDuffMode);
}
}
/**
* Set or clear the transfer mode object. A transfer mode defines how
* source pixels (generate by a drawing command) are composited with
@@ -1185,8 +1202,17 @@ public class Paint {
*
* @param xfermode May be null. The xfermode to be installed in the paint
* @return xfermode
*
* @deprecated Use {@link #setBlendMode} to apply a Xfermode directly
* through usage of {@link BlendMode}
*/
@Deprecated
public Xfermode setXfermode(Xfermode xfermode) {
return installXfermode(xfermode);
}
@Nullable
private Xfermode installXfermode(Xfermode xfermode) {
int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
if (newMode != curMode) {
@@ -1196,6 +1222,23 @@ public class Paint {
return xfermode;
}
/**
* Set or clear the blend mode. A blend mode defines how source pixels
* (generated by a drawing command) are composited with the destination pixels
* (content of the render target).
* <p />
* Pass null to clear any previous blend mode.
* As a convenience, the parameter passed is also returned.
* <p />
*
* @see BlendMode
*
* @param blendmode May be null. The blend mode to be installed in the paint
*/
public void setBlendMode(@Nullable BlendMode blendmode) {
installXfermode(blendmode != null ? blendmode.getXfermode() : null);
}
/**
* Get the paint's patheffect object.
*

View File

@@ -23,7 +23,11 @@ import android.annotation.UnsupportedAppUsage;
/**
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link PorterDuff Porter-Duff composite mode}.
*
* @deprecated Consider using {@link BlendModeColorFilter} instead as it supports a wider
* set of blend modes than those defined in {@link PorterDuff.Mode}
*/
@Deprecated
public class PorterDuffColorFilter extends ColorFilter {
@ColorInt
private int mColor;
@@ -71,7 +75,7 @@ public class PorterDuffColorFilter extends ColorFilter {
@Override
long createNativeInstance() {
return native_CreatePorterDuffFilter(mColor, mMode.nativeInt);
return native_CreateBlendModeFilter(mColor, mMode.nativeInt);
}
@Override
@@ -91,5 +95,5 @@ public class PorterDuffColorFilter extends ColorFilter {
return 31 * mMode.hashCode() + mColor;
}
private static native long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode);
private static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
}

View File

@@ -595,7 +595,12 @@ public abstract class Drawable {
* <p class="note"><strong>Note:</strong> Setting a color filter disables
* {@link #setTintList(ColorStateList) tint}.
* </p>
*
* @see {@link #setColorFilter(ColorFilter)} }
* @deprecated use {@link #setColorFilter(ColorFilter)} with an instance
* of {@link android.graphics.BlendModeColorFilter}
*/
@Deprecated
public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
if (getColorFilter() instanceof PorterDuffColorFilter) {
PorterDuffColorFilter existing = (PorterDuffColorFilter) getColorFilter();