LayoutLib: Support PorterDuffColorFilter
Add support for select modes for PorterDuffColorFilter.
Change-Id: Ia7b4a6a92c326be977dd87a70f54c1f8954b546d
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index a417479..80f3b18 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -627,8 +627,7 @@
// set the color
graphics.setColor(new Color(color, true /*alpha*/));
- Composite composite = PorterDuffXfermode_Delegate.getComposite(
- PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
+ Composite composite = PorterDuffXfermode_Delegate.getComposite(mode, 0xFF);
if (composite != null) {
graphics.setComposite(composite);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
index bf03a5e..bd934d0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -19,6 +19,8 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import java.awt.Graphics2D;
+
/**
* Delegate implementing the native methods of android.graphics.ColorFilter
*
@@ -50,9 +52,17 @@
return sManager.getDelegate(nativeShader);
}
- public abstract boolean isSupported();
public abstract String getSupportMessage();
+ public boolean isSupported() {
+ return false;
+ }
+
+ public void applyFilter(Graphics2D g, int width, int height) {
+ // This should never be called directly. If supported, the sub class should override this.
+ assert false;
+ }
+
// ---- native methods ----
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
index 9aac2bd..6739484 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
@@ -43,11 +43,6 @@
// ---- Public Helper methods ----
@Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
public String getSupportMessage() {
return "ColorMatrix Color Filters are not supported.";
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
index 501d55c..0dd9703 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
@@ -43,11 +43,6 @@
// ---- Public Helper methods ----
@Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
public String getSupportMessage() {
return "Lighting Color Filters are not supported.";
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index c7c2e97..5adf4ca 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -827,8 +827,8 @@
delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
- // since none of those are supported, display a fidelity warning right away
- if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
+ // Log warning if it's not supported.
+ if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
}
@@ -873,7 +873,7 @@
delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
// since none of those are supported, display a fidelity warning right away
- if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
+ if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
}
@@ -906,7 +906,7 @@
delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
// since none of those are supported, display a fidelity warning right away
- if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
+ if (delegate.mRasterizer != null && !delegate.mRasterizer.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index 1bc3033..0543f4f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -19,6 +19,15 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.graphics.PorterDuff.Mode;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
+
/**
* Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
*
@@ -40,25 +49,80 @@
// ---- delegate data ----
+ private final int mSrcColor;
+ private final Mode mMode;
+ private int mWidth;
+ private int mHeight;
+ private BufferedImage mImage;
+
+
// ---- Public Helper methods ----
@Override
public boolean isSupported() {
+ switch (mMode) {
+ case CLEAR:
+ case SRC:
+ case SRC_IN:
+ case DST_IN:
+ case SRC_ATOP:
+ return true;
+ }
+
return false;
}
@Override
public String getSupportMessage() {
- return "PorterDuff Color Filters are not supported.";
+ return "PorterDuff Color Filter is not supported for mode: " + mMode.name() + ".";
+ }
+
+ @Override
+ public void applyFilter(Graphics2D g, int width, int height) {
+ createFilterImage(width, height);
+ g.setComposite(getComposite());
+ g.drawImage(mImage, 0, 0, null);
}
// ---- native methods ----
@LayoutlibDelegate
/*package*/ static long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
- PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate();
+ PorterDuffColorFilter_Delegate newDelegate =
+ new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
return sManager.addNewDelegate(newDelegate);
}
+
// ---- Private delegate/helper methods ----
+
+ private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
+ mSrcColor = srcColor;
+ mMode = getPorterDuffMode(mode);
+ }
+
+ private void createFilterImage(int width, int height) {
+ if (mWidth == width && mHeight == height && mImage != null) {
+ return;
+ }
+ mWidth = width;
+ mHeight = height;
+ mImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = mImage.createGraphics();
+ try {
+ graphics.setColor(new java.awt.Color(mSrcColor, true /* hasAlpha */));
+ graphics.fillRect(0, 0, width, height);
+ } finally {
+ graphics.dispose();
+ }
+ }
+
+ private AlphaComposite getComposite() {
+ return AlphaComposite.getInstance(getAlphaCompositeRule(mMode),
+ getAlpha() / 255f);
+ }
+
+ private int getAlpha() {
+ return mSrcColor >>> 24;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index a89fd57..f6c36b6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -21,9 +21,14 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.graphics.PorterDuff.Mode;
+
import java.awt.AlphaComposite;
import java.awt.Composite;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
+
/**
* Delegate implementing the native methods of android.graphics.PorterDuffXfermode
*
@@ -43,17 +48,17 @@
// ---- delegate data ----
- private final int mMode;
+ private final Mode mMode;
// ---- Public Helper methods ----
- public PorterDuff.Mode getMode() {
- return getPorterDuffMode(mMode);
+ public Mode getMode() {
+ return mMode;
}
@Override
public Composite getComposite(int alpha) {
- return getComposite(getPorterDuffMode(mMode), alpha);
+ return getComposite(mMode, alpha);
}
@Override
@@ -67,61 +72,8 @@
return null;
}
- public static PorterDuff.Mode getPorterDuffMode(int mode) {
- for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
- if (m.nativeInt == mode) {
- return m;
- }
- }
-
- Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- String.format("Unknown PorterDuff.Mode: %d", mode), null /*data*/);
- assert false;
- return PorterDuff.Mode.SRC_OVER;
- }
-
- public static Composite getComposite(PorterDuff.Mode mode, int alpha) {
- float falpha = alpha != 0xFF ? (float)alpha / 255.f : 1.f;
- switch (mode) {
- case CLEAR:
- return AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha);
- case DARKEN:
- break;
- case DST:
- return AlphaComposite.getInstance(AlphaComposite.DST, falpha);
- case DST_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha);
- case DST_IN:
- return AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha);
- case DST_OUT:
- return AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha);
- case DST_OVER:
- return AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha);
- case LIGHTEN:
- break;
- case MULTIPLY:
- break;
- case SCREEN:
- break;
- case SRC:
- return AlphaComposite.getInstance(AlphaComposite.SRC, falpha);
- case SRC_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha);
- case SRC_IN:
- return AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha);
- case SRC_OUT:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha);
- case SRC_OVER:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
- case XOR:
- return AlphaComposite.getInstance(AlphaComposite.XOR, falpha);
- }
-
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
- String.format("Unsupported PorterDuff Mode: %s", mode.name()),
- null, null /*data*/);
-
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
+ public static Composite getComposite(int mode, int alpha) {
+ return getComposite(getPorterDuffMode(mode), alpha);
}
// ---- native methods ----
@@ -135,6 +87,20 @@
// ---- Private delegate/helper methods ----
private PorterDuffXfermode_Delegate(int mode) {
- mMode = mode;
+ mMode = getPorterDuffMode(mode);
+ }
+
+ private static Composite getComposite(Mode mode, int alpha255) {
+ float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
+ int rule = getAlphaCompositeRule(mode);
+ if (rule >= 0) {
+ return AlphaComposite.getInstance(rule, alpha1);
+ }
+
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+ String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
+ null, null /*data*/);
+
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 21d6b1a..3a0321a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -21,6 +21,7 @@
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
+import android.graphics.ColorFilter_Delegate;
import android.graphics.Paint;
import android.graphics.Paint_Delegate;
import android.graphics.Rect;
@@ -48,8 +49,8 @@
* This is based on top of {@link Graphics2D} but can operate independently if none are available
* yet when setting transforms and clip information.
* <p>
- * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
- * {@link #draw(Drawable, Paint_Delegate)}
+ * This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and
+ * {@link #draw(Drawable)}
*
* Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
* a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
@@ -203,7 +204,7 @@
* called before the snapshot can be used to draw. Transform and clip operations are permitted
* before.
*
- * @param image the image to associate to the snapshot or null.
+ * @param bitmap the image to associate to the snapshot or null.
* @return the root snapshot
*/
public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
@@ -557,7 +558,6 @@
* Executes the Drawable's draw method, with a null paint delegate.
* <p/>
* Note that the method can be called several times if there are more than one active layer.
- * @param drawable
*/
public void draw(Drawable drawable) {
draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
@@ -567,20 +567,19 @@
* Executes the Drawable's draw method.
* <p/>
* Note that the method can be called several times if there are more than one active layer.
- * @param drawable
- * @param paint
* @param compositeOnly whether the paint is used for composite only. This is typically
* the case for bitmaps.
* @param forceSrcMode if true, this overrides the composite to be SRC
*/
public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
boolean forceSrcMode) {
+ int forceMode = forceSrcMode ? AlphaComposite.SRC : 0;
// the current snapshot may not have a mLocalLayer (ie it was created on save() instead
// of saveLayer(), but that doesn't mean there's no layer.
// mLayers however saves all the information we need (flags).
if (mLayers.size() == 1) {
// no layer, only base layer. easy case.
- drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
+ drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode);
} else {
// draw in all the layers until the layer save flags tells us to stop (ie drawing
// in that layer is limited to the layer itself.
@@ -590,7 +589,7 @@
do {
Layer layer = mLayers.get(i);
- drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
+ drawInLayer(layer, drawable, paint, compositeOnly, forceMode);
// then go to previous layer, only if there are any left, and its flags
// doesn't restrict drawing to the layer itself.
@@ -601,20 +600,61 @@
}
private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
- boolean compositeOnly, boolean forceSrcMode) {
+ boolean compositeOnly, int forceMode) {
Graphics2D originalGraphics = layer.getGraphics();
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D configuredGraphics2D =
- paint != null ?
- createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
- (Graphics2D) originalGraphics.create();
+ if (paint == null) {
+ drawOnGraphics((Graphics2D) originalGraphics.create(), drawable,
+ null /*paint*/, layer);
+ } else {
+ ColorFilter_Delegate filter = paint.getColorFilter();
+ if (filter == null || !filter.isSupported()) {
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
+ compositeOnly, forceMode);
+ drawOnGraphics(configuredGraphics, drawable, paint, layer);
+ return;
+ }
+ int width = layer.getImage().getWidth();
+ int height = layer.getImage().getHeight();
+
+ // Create a temporary image to which the color filter will be applied.
+ BufferedImage image = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
+ // Configure the Graphics2D object with drawing parameters and shader.
+ Graphics2D imageGraphics = createCustomGraphics(
+ imageBaseGraphics, paint, compositeOnly,
+ AlphaComposite.SRC_OVER);
+ // get a Graphics2D object configured with the drawing parameters, but no shader.
+ Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
+ true /*compositeOnly*/, forceMode);
+ try {
+ // The main draw operation.
+ drawable.draw(imageGraphics, paint);
+
+ // Apply the color filter.
+ filter.applyFilter(imageGraphics, width, height);
+
+ // Draw the tinted image on the main layer.
+ configuredGraphics.drawImage(image, 0, 0, null);
+ layer.change();
+ } finally {
+ // dispose Graphics2D objects
+ imageGraphics.dispose();
+ imageBaseGraphics.dispose();
+ configuredGraphics.dispose();
+ }
+ }
+ }
+
+ private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
+ Layer layer) {
try {
- drawable.draw(configuredGraphics2D, paint);
+ drawable.draw(g, paint);
layer.change();
} finally {
- // dispose Graphics2D object
- configuredGraphics2D.dispose();
+ g.dispose();
}
}
@@ -685,7 +725,7 @@
// now draw put the content of the local layer onto the layer,
// using the paint information
Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
- true /*alphaOnly*/, false /*forceSrcMode*/);
+ true /*alphaOnly*/, 0 /*forceMode*/);
g.drawImage(mLocalLayer.getImage(),
mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
@@ -701,7 +741,7 @@
* <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
*/
private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
- boolean compositeOnly, boolean forceSrcMode) {
+ boolean compositeOnly, int forceMode) {
// make new one graphics
Graphics2D g = (Graphics2D) original.create();
@@ -714,70 +754,73 @@
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
+ // set the shader first, as it'll replace the color if it can be used it.
boolean customShader = false;
-
- // get the shader first, as it'll replace the color if it can be used it.
- if (compositeOnly == false) {
- Shader_Delegate shaderDelegate = paint.getShader();
- if (shaderDelegate != null) {
- if (shaderDelegate.isSupported()) {
- java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
- assert shaderPaint != null;
- if (shaderPaint != null) {
- g.setPaint(shaderPaint);
- customShader = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
- shaderDelegate.getSupportMessage(),
- null /*throwable*/, null /*data*/);
- }
- }
-
- // if no shader, use the paint color
- if (customShader == false) {
- g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
- }
-
+ if (!compositeOnly) {
+ customShader = setShader(g, paint);
// set the stroke
g.setStroke(paint.getJavaStroke());
}
+ // set the composite.
+ setComposite(g, paint, compositeOnly || customShader, forceMode);
- // the alpha for the composite. Always opaque if the normal paint color is used since
- // it contains the alpha
- int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
+ return g;
+ }
- if (forceSrcMode) {
- g.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC, (float) alpha / 255.f));
- } else {
- boolean customXfermode = false;
- Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
- if (xfermodeDelegate != null) {
- if (xfermodeDelegate.isSupported()) {
- Composite composite = xfermodeDelegate.getComposite(alpha);
- assert composite != null;
- if (composite != null) {
- g.setComposite(composite);
- customXfermode = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
- xfermodeDelegate.getSupportMessage(),
- null /*throwable*/, null /*data*/);
+ private boolean setShader(Graphics2D g, Paint_Delegate paint) {
+ Shader_Delegate shaderDelegate = paint.getShader();
+ if (shaderDelegate != null) {
+ if (shaderDelegate.isSupported()) {
+ java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
+ assert shaderPaint != null;
+ if (shaderPaint != null) {
+ g.setPaint(shaderPaint);
+ return true;
}
- }
-
- // if there was no custom xfermode, but we have alpha (due to a shader and a non
- // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
- // that will handle the alpha.
- if (customXfermode == false && alpha != 0xFF) {
- g.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+ } else {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
+ shaderDelegate.getSupportMessage(),
+ null /*throwable*/, null /*data*/);
}
}
- return g;
+ // if no shader, use the paint color
+ g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
+
+ return false;
+ }
+
+ private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
+ int forceMode) {
+ // the alpha for the composite. Always opaque if the normal paint color is used since
+ // it contains the alpha
+ int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
+ if (forceMode != 0) {
+ g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
+ return;
+ }
+ Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
+ if (xfermodeDelegate != null) {
+ if (xfermodeDelegate.isSupported()) {
+ Composite composite = xfermodeDelegate.getComposite(alpha);
+ assert composite != null;
+ if (composite != null) {
+ g.setComposite(composite);
+ return;
+ }
+ } else {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
+ xfermodeDelegate.getSupportMessage(),
+ null /*throwable*/, null /*data*/);
+ }
+ }
+ // if there was no custom xfermode, but we have alpha (due to a shader and a non
+ // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
+ // that will handle the alpha.
+ if (alpha != 0xFF) {
+ g.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+ }
}
private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
new file mode 100644
index 0000000..bc53e93
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 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 com.android.layoutlib.bridge.impl;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter_Delegate;
+import android.graphics.PorterDuffXfermode_Delegate;
+
+import java.awt.AlphaComposite;
+
+/**
+ * Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
+ * PorterDuffXfermode_Delegate}.
+ */
+public final class PorterDuffUtility {
+
+ // Make the class non-instantiable.
+ private PorterDuffUtility() {
+ }
+
+ /**
+ * Convert the porterDuffMode from the framework to its corresponding enum. This defaults to
+ * {@link Mode#SRC_OVER} for invalid modes.
+ */
+ public static Mode getPorterDuffMode(int porterDuffMode) {
+ Mode[] values = Mode.values();
+ if (porterDuffMode >= 0 && porterDuffMode < values.length) {
+ return values[porterDuffMode];
+ }
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/);
+ assert false;
+ return Mode.SRC_OVER;
+ }
+
+ /**
+ * A utility method to convert the porterDuffMode to an int to be used as a rule for {@link
+ * AlphaComposite}. If {@code AlphaComposite} doesn't support the mode, -1 is returned.
+ */
+ public static int getAlphaCompositeRule(Mode porterDuffMode) {
+ switch (porterDuffMode) {
+ case CLEAR:
+ return AlphaComposite.CLEAR;
+ case DARKEN:
+ break;
+ case DST:
+ return AlphaComposite.DST;
+ case DST_ATOP:
+ return AlphaComposite.DST_ATOP;
+ case DST_IN:
+ return AlphaComposite.DST_IN;
+ case DST_OUT:
+ return AlphaComposite.DST_OUT;
+ case DST_OVER:
+ return AlphaComposite.DST_OVER;
+ case LIGHTEN:
+ break;
+ case MULTIPLY:
+ break;
+ case SCREEN:
+ break;
+ case SRC:
+ return AlphaComposite.SRC;
+ case SRC_ATOP:
+ return AlphaComposite.SRC_ATOP;
+ case SRC_IN:
+ return AlphaComposite.SRC_IN;
+ case SRC_OUT:
+ return AlphaComposite.SRC_OUT;
+ case SRC_OVER:
+ return AlphaComposite.SRC_OVER;
+ case XOR:
+ return AlphaComposite.XOR;
+ }
+ // This is an unsupported mode.
+ return -1;
+
+ }
+}